diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 62e1c273e0..0000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,18 +0,0 @@ -version: 2 -jobs: - build: - docker: - - image: circleci/golang:1.10 - - working_directory: /go/src/github.com/cosmos/ethermint - - steps: - - checkout - - - run: - name: "Install tools and dependancies" - command: make tools deps - - - run: - name: "Run tests" - command: make test-lint test-unit test-import diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0a374581fc..b304e27f46 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ # CODEOWNERS: https://help.github.com/articles/about-codeowners/ # Primary repo maintainers -* @alexanderbez @AlexeyAkhunov @jackzampolin +* @fedekunze @noot diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index db9ad28534..6dd37f7f28 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,17 +4,31 @@ v Before smashing the submit button please review the checkboxes. v If a checkbox is n/a - please still include it but + a little note why ☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > --> -- Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ethermint/blob/master/CONTRIBUTING.md#pr-targeting)) +Closes: #XXX -- [ ] Linked to github-issue with discussion and accepted design OR link to spec that describes this work. -- [ ] Wrote tests -- [ ] Updated relevant documentation (`docs/`) -- [ ] Added entries in `PENDING.md` with issue # -- [ ] rereviewed `Files changed` in the github PR explorer +## Description + + + +______ + +For contributor use: + +- [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) +- [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. +- [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). +- [ ] Wrote unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) +- [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) +- [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). +- [ ] Added a relevant changelog entry to the `Unreleased` section in `CHANGELOG.md` +- [ ] Re-reviewed `Files changed` in the Github PR explorer ______ -For Admin Use: -- Added appropriate labels to PR (ex. wip, ready-for-review, docs) -- Reviewers Assigned -- Squashed all commits, uses message "Merge pull request #XYZ: [title]" +For admin use: + +- [ ] Added appropriate labels to PR (ex. `WIP`, `R4R`, `docs`, etc) +- [ ] Reviewers assigned +- [ ] Squashed all commits, uses message "Merge pull request #XYZ: [title]" ([coding standards](https://github.com/tendermint/coding/blob/master/README.md#merging-a-pr)) diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..9e099a16b8 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,29 @@ +version: 2 +updates: +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily + time: "10:00" + open-pull-requests-limit: 10 + reviewers: + - fedekunze + - noot + labels: + - dependencies +- package-ecosystem: docker + directory: "/" + schedule: + interval: daily + time: "10:00" + open-pull-requests-limit: 10 + reviewers: + - araskachoi + - fedekunze + - noot +- package-ecosystem: gomod + directory: "/" + schedule: + interval: daily + time: "10:00" + open-pull-requests-limit: 10 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000000..635b6f9f05 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,29 @@ +name: Build +on: + pull_request: + branches: + - "development" + +jobs: + cleanup-runs: + runs-on: ubuntu-latest + steps: + - uses: rokroskar/workflow-run-cleanup-action@master + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/development'" + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: technote-space/get-diff-action@v3.3 + id: git_diff + with: + SUFFIX_FILTER: | + .go + .mod + .sum + - run: | + make build + if: "env.GIT_DIFF != ''" diff --git a/.github/workflows/clean-artifacts.yml b/.github/workflows/clean-artifacts.yml new file mode 100644 index 0000000000..d5c2e72f0c --- /dev/null +++ b/.github/workflows/clean-artifacts.yml @@ -0,0 +1,19 @@ +name: Remove old artifacts +# Remove old artifacts runs a crob job that removes old artifacts +# generated from the split tests workflow. + +on: + schedule: + # Every day at 1am + - cron: "0 1 * * *" + +jobs: + remove-old-artifacts: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Remove old artifacts + uses: c-hive/gha-remove-artifacts@v1 + with: + age: "7 days" diff --git a/.github/workflows/deploy-contract.yml b/.github/workflows/deploy-contract.yml new file mode 100644 index 0000000000..bdff8dcc1e --- /dev/null +++ b/.github/workflows/deploy-contract.yml @@ -0,0 +1,38 @@ +name: Deploy Contract +on: + pull_request: + branches: + - "development" + +jobs: + cleanup-runs: + runs-on: ubuntu-latest + steps: + - uses: rokroskar/workflow-run-cleanup-action@master + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/development'" + + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Use Node.js + uses: actions/setup-node@v2.1.2 + with: + node-version: '12.x' + - name: Install dependencies + run: npm install + - uses: technote-space/get-diff-action@v3.3 + id: git_diff + with: + SUFFIX_FILTER: | + .go + .mod + .sum + .sol + - name: Test contract + run: | + sudo make contract-tools + sudo make test-contract + if: "env.GIT_DIFF != ''" diff --git a/.github/workflows/linkchecker.yml b/.github/workflows/linkchecker.yml new file mode 100644 index 0000000000..a4ad901796 --- /dev/null +++ b/.github/workflows/linkchecker.yml @@ -0,0 +1,12 @@ +name: Check Markdown links +on: + schedule: + - cron: "* */24 * * *" +jobs: + markdown-link-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@development + - uses: gaurav-nelson/github-action-markdown-link-check@1.0.7 + with: + folder-path: "docs" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000000..da81ac7938 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,29 @@ +name: Lint +# Lint runs golangci-lint over the entire cosmos-sdk repository +# This workflow is run on every pull request and push to development +# The `golangci` will pass without running if no *.{go, mod, sum} files have been changed. +on: + pull_request: + push: + branches: + - development +jobs: + golangci: + name: golangci-lint + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v2 + - uses: technote-space/get-diff-action@v3.3 + with: + SUFFIX_FILTER: | + .go + .mod + .sum + - uses: golangci/golangci-lint-action@master + with: + # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. + version: v1.28 + args: --timeout 10m + github-token: ${{ secrets.github_token }} + if: "env.GIT_DIFF != ''" diff --git a/.github/workflows/release-sims.yml b/.github/workflows/release-sims.yml new file mode 100644 index 0000000000..04b5d902cb --- /dev/null +++ b/.github/workflows/release-sims.yml @@ -0,0 +1,42 @@ +name: Release Sims +# Release Sims workflow runs long-lived (multi-seed & large block size) simulations of 500 blocks. +# This workflow only runs on a pull request when the branch contains rc** (rc1/vX.X.x) +# This release workflow *cannot* be skipped with the 'skip-sims' commit message. +on: + pull_request: + branches: + - "rc**" + +jobs: + cleanup-runs: + runs-on: ubuntu-latest + steps: + - uses: rokroskar/workflow-run-cleanup-action@master + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/development'" + + install-runsim: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, 'skip-sims')" + steps: + - name: install runsim + run: | + export GO111MODULE="on" && go get github.com/cosmos/tools/cmd/runsim@v1.0.0 + - uses: actions/cache@v2.1.1 + with: + path: ~/go/bin + key: ${{ runner.os }}-go-runsim-binary + + test-sim-multi-seed-long: + runs-on: ubuntu-latest + needs: install-runsim + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v2.1.1 + with: + path: ~/go/bin + key: ${{ runner.os }}-go-runsim-binary + - name: test-sim-multi-seed-long + run: | + make test-sim-multi-seed-long diff --git a/.github/workflows/sims.yml b/.github/workflows/sims.yml new file mode 100644 index 0000000000..a3899d3b72 --- /dev/null +++ b/.github/workflows/sims.yml @@ -0,0 +1,121 @@ +name: Sims +# Sims workflow runs multiple types of simulations (nondeterminism, import-export, after-import, multi-seed-short) +# This workflow will run on all Pull Requests, if a .go, .mod or .sum file have been changed. +# The simulations can be skipped if the commit message contains the 'skip-sims' string. +on: + pull_request: + push: + branches: + - development + +jobs: + cleanup-runs: + runs-on: ubuntu-latest + if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/development'" + steps: + - uses: rokroskar/workflow-run-cleanup-action@master + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + # install-runsim: + # runs-on: ubuntu-latest + # if: "!contains(github.event.head_commit.message, 'skip-sims')" + # steps: + # - uses: actions/setup-go@v2.1.2 + # - name: install runsim + # run: | + # export GO111MODULE="on" && go get github.com/cosmos/tools/cmd/runsim@v1.0.0 + # - uses: actions/cache@v2.1.1 + # with: + # path: ~/go/bin + # key: ${{ runner.os }}-go-runsim-binary + # test-sim-nondeterminism: + # runs-on: ubuntu-latest + # if: "!contains(github.event.head_commit.message, 'skip-sims')" + # needs: install-runsim + # steps: + # - uses: actions/checkout@v2 + # - uses: technote-space/get-diff-action@v3.2 + # with: + # SUFFIX_FILTER: | + # .go + # .mod + # .sum + # SET_ENV_NAME_INSERTIONS: 1 + # SET_ENV_NAME_LINES: 1 + # - uses: actions/cache@v2.1.1 + # with: + # path: ~/go/bin + # key: ${{ runner.os }}-go-runsim-binary + # if: "env.GIT_DIFF != ''" + # - name: test-sim-nondeterminism + # run: | + # make test-sim-nondeterminism + # if: "env.GIT_DIFF != ''" + # test-sim-import-export: + # runs-on: ubuntu-latest + # needs: install-runsim + # steps: + # - uses: actions/checkout@v2 + # - uses: technote-space/get-diff-action@v3.2 + # with: + # SUFFIX_FILTER: | + # .go + # .mod + # .sum + # SET_ENV_NAME_INSERTIONS: 1 + # SET_ENV_NAME_LINES: 1 + # - uses: actions/cache@v2.1.1 + # with: + # path: ~/go/bin + # key: ${{ runner.os }}-go-runsim-binary + # if: "env.GIT_DIFF != ''" + # - name: test-sim-import-export + # run: | + # make test-sim-import-export + # if: "env.GIT_DIFF != ''" + # test-sim-after-import: + # runs-on: ubuntu-latest + # if: "!contains(github.event.head_commit.message, 'skip-sims')" + # needs: install-runsim + # steps: + # - uses: actions/checkout@v2 + # - uses: technote-space/get-diff-action@v3.2 + # with: + # SUFFIX_FILTER: | + # .go + # .mod + # .sum + # SET_ENV_NAME_INSERTIONS: 1 + # SET_ENV_NAME_LINES: 1 + # - uses: actions/cache@v2.1.1 + # with: + # path: ~/go/bin + # key: ${{ runner.os }}-go-runsim-binary + # if: "env.GIT_DIFF != ''" + # - name: test-sim-after-import + # run: | + # make test-sim-after-import + # if: "env.GIT_DIFF != ''" + # test-sim-multi-seed-short: + # runs-on: ubuntu-latest + # if: "!contains(github.event.head_commit.message, 'skip-sims')" + # needs: install-runsim + # steps: + # - uses: actions/checkout@v2 + # - uses: technote-space/get-diff-action@v3.2 + # with: + # SUFFIX_FILTER: | + # .go + # .mod + # .sum + # SET_ENV_NAME_INSERTIONS: 1 + # SET_ENV_NAME_LINES: 1 + # - uses: actions/cache@v2.1.1 + # with: + # path: ~/go/bin + # key: ${{ runner.os }}-go-runsim-binary + # if: "env.GIT_DIFF != ''" + # - name: test-sim-multi-seed-short + # run: | + # make test-sim-multi-seed-short + # if: "env.GIT_DIFF != ''" diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000..e702e12ecb --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,22 @@ +name: "Close stale issues & pull requests" +on: + schedule: + - cron: "0 0 * * *" + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-pr-message: "This pull request has been automatically marked as stale because it has not had + recent activity. It will be closed in 7 days-before-close if no further activity occurs. Thank you + for your contributions." + stale-issue-message: "This issue is stale because it has been open 45 days with no activity. Remove `stale` label or comment or this will be closed in 7 days." + days-before-stale: 45 + days-before-close: 7 + exempt-issue-labels: "Status: On Ice, Status: Blocked, Type: Bug, Type: Security, Type: Meta-Issue, Type: Enhancement, Epic" + exempt-pr-labels: "Status: On Ice, Status: Blocked, Type: Bug, Type: Security, Type: Meta-Issue, Type: Enhancement, Epic" + stale-pr-label: "stale" + stale-issue-label: "stale" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000000..769e2beee4 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,229 @@ +name: Tests / Code Coverage +# Tests / Code Coverage workflow runs unit tests and uploads a code coverage report +# This workflow is run on pushes to development & every Pull Requests where a .go, .mod, .sum have been changed +on: + pull_request: + push: + branches: + - development +jobs: + test-rpc: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v2 + - uses: technote-space/get-diff-action@v3.3 + id: git_diff + with: + SUFFIX_FILTER: | + .go + .mod + .sum + - name: test-rpc + run: | + make test-rpc + if: "env.GIT_DIFF != ''" + + split-test-files: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Create a file with all the pkgs + run: go list ./... | grep -Ev 'vendor|importer|rpc/tester' > pkgs.txt + - name: Split pkgs into 4 files + run: split -n l/4 --additional-suffix=.txt ./pkgs.txt + # cache multiple + - uses: actions/upload-artifact@v2 + with: + name: "${{ github.sha }}-aa" + path: ./xaa.txt + - uses: actions/upload-artifact@v2 + with: + name: "${{ github.sha }}-ab" + path: ./xab.txt + - uses: actions/upload-artifact@v2 + with: + name: "${{ github.sha }}-ac" + path: ./xac.txt + - uses: actions/upload-artifact@v2 + with: + name: "${{ github.sha }}-ad" + path: ./xad.txt + + test-coverage-run-1: + runs-on: ubuntu-latest + needs: split-test-files + timeout-minutes: 10 + steps: + - uses: actions/checkout@v2 + - uses: technote-space/get-diff-action@v3.3 + id: git_diff + with: + SUFFIX_FILTER: | + .go + .mod + .sum + - uses: actions/download-artifact@v2 + with: + name: "${{ github.sha }}-aa" + if: "env.GIT_DIFF != ''" + - name: test & coverage report creation + run: | + cat xaa.txt | xargs go test -mod=readonly -timeout 8m -coverprofile=coverage.txt -covermode=atomic + if: "env.GIT_DIFF != ''" + - name: filter out proto files + run: | + excludelist+=" $(find ./ -type f -name '*.pb.go')" + for filename in ${excludelist}; do + filename=$(echo $filename | sed 's/^./github.com\/cosmos\/ethermint/g') + echo "Excluding ${filename} from coverage report..." + sed -i.bak "/$(echo $filename | sed 's/\//\\\//g')/d" coverage.txt + done + if: "env.GIT_DIFF != ''" + - uses: codecov/codecov-action@v1 + with: + file: ./coverage.txt + fail_ci_if_error: true + if: "env.GIT_DIFF != ''" + + test-coverage-run-2: + runs-on: ubuntu-latest + needs: split-test-files + timeout-minutes: 10 + steps: + - uses: actions/checkout@v2 + - uses: technote-space/get-diff-action@v3.3 + id: git_diff + with: + SUFFIX_FILTER: | + .go + .mod + .sum + - uses: actions/download-artifact@v2 + with: + name: "${{ github.sha }}-ab" + if: "env.GIT_DIFF != ''" + - name: test & coverage report creation + run: | + cat xab.txt | xargs go test -mod=readonly -timeout 6m -coverprofile=coverage.txt -covermode=atomic + if: "env.GIT_DIFF != ''" + - name: filter out proto files + run: | + excludelist+=" $(find ./ -type f -name '*.pb.go')" + for filename in ${excludelist}; do + filename=$(echo $filename | sed 's/^./github.com\/cosmos\/ethermint/g') + echo "Excluding ${filename} from coverage report..." + sed -i.bak "/$(echo $filename | sed 's/\//\\\//g')/d" coverage.txt + done + if: "env.GIT_DIFF != ''" + - uses: codecov/codecov-action@v1 + with: + file: ./coverage.txt + fail_ci_if_error: true + if: "env.GIT_DIFF != ''" + + test-coverage-run-3: + runs-on: ubuntu-latest + needs: split-test-files + timeout-minutes: 10 + steps: + - uses: actions/checkout@v2 + - uses: technote-space/get-diff-action@v3.3 + id: git_diff + with: + SUFFIX_FILTER: | + .go + .mod + .sum + - uses: actions/download-artifact@v2 + with: + name: "${{ github.sha }}-ac" + if: "env.GIT_DIFF != ''" + - name: test & coverage report creation + run: | + cat xac.txt | xargs go test -mod=readonly -timeout 6m -coverprofile=coverage.txt -covermode=atomic + if: "env.GIT_DIFF != ''" + - name: filter out proto files + run: | + excludelist+=" $(find ./ -type f -name '*.pb.go')" + for filename in ${excludelist}; do + filename=$(echo $filename | sed 's/^./github.com\/cosmos\/ethermint/g') + echo "Excluding ${filename} from coverage report..." + sed -i.bak "/$(echo $filename | sed 's/\//\\\//g')/d" coverage.txt + done + if: "env.GIT_DIFF != ''" + - uses: codecov/codecov-action@v1 + with: + file: ./coverage.txt + fail_ci_if_error: true + if: "env.GIT_DIFF != ''" + + test-coverage-run-4: + runs-on: ubuntu-latest + needs: split-test-files + timeout-minutes: 10 + steps: + - uses: actions/checkout@v2 + - uses: technote-space/get-diff-action@v3.3 + id: git_diff + with: + SUFFIX_FILTER: | + .go + .mod + .sum + - uses: actions/download-artifact@v2 + with: + name: "${{ github.sha }}-ad" + if: "env.GIT_DIFF != ''" + - name: test & coverage report creation + run: | + cat xad.txt | xargs go test -mod=readonly -timeout 6m -coverprofile=coverage.txt -covermode=atomic + if: "env.GIT_DIFF != ''" + - name: filter out proto files + run: | + excludelist+=" $(find ./ -type f -name '*.pb.go')" + for filename in ${excludelist}; do + filename=$(echo $filename | sed 's/^./github.com\/cosmos\/ethermint/g') + echo "Excluding ${filename} from coverage report..." + sed -i.bak "/$(echo $filename | sed 's/\//\\\//g')/d" coverage.txt + done + if: "env.GIT_DIFF != ''" + - uses: codecov/codecov-action@v1 + with: + file: ./coverage.txt + fail_ci_if_error: true + if: "env.GIT_DIFF != ''" + + test-importer: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v2 + - uses: technote-space/get-diff-action@v3.3 + id: git_diff + with: + SUFFIX_FILTER: | + .go + .mod + .sum + - name: test-importer + run: | + make test-import + if: "env.GIT_DIFF != ''" + + test-solidity: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v2 + - uses: technote-space/get-diff-action@v3.3 + id: git_diff + with: + SUFFIX_FILTER: | + .go + .mod + .sum + - name: test-solidity + run: | + make test-solidity + if: "env.GIT_DIFF != ''" diff --git a/.gitignore b/.gitignore index 17cffba803..d9cdfa463c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,61 @@ -# Binaries for programs and plugins +# OS +.DS_Store +*.swp +*.swo +*.swl +*.swm +*.swn +.vscode +.idea +*.pyc *.exe +*.exe~ *.dll *.so *.dylib -# Test binary, build with `go test -c` +# Build *.test +.glide/ +vendor +build +bin +tools/bin/* +docs/_build +docs/tutorial +docs/node_modules +docs/modules +dist +tools-stamp +docs-tools-stamp +proto-tools-stamp +golangci-lint +keyring_test_cosmos -# Output of the go coverage tool, specifically when used with LiteIDE +# Testing +coverage.txt *.out +sim_log_file +importer/tmp -# Vendor deps -.glide/ -vendor/ -build/ +# Vagrant +.vagrant/ +*.box +*.log +vagrant + +# IDE +.idea/ +*.iml + +# Graphviz +dependency-graph.png + +# Latex +*.aux +*.out +*.synctex.gz + +# Contracts +*.bin +*.abi diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000000..1cce52322d --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,65 @@ +run: + tests: false +# # timeout for analysis, e.g. 30s, 5m, default is 1m +# timeout: 5m + +linters: + disable-all: true + enable: + - bodyclose + - deadcode + - depguard + - dogsled + - errcheck + - goconst + - gocritic + - gofmt + - goimports + - golint + - gosec + - gosimple + - govet + - ineffassign + - interfacer + - maligned + - misspell + - nakedret + - prealloc + - scopelint + - staticcheck + - structcheck + - stylecheck + - typecheck + - unconvert + - unused + - unparam + - misspell + +issues: + exclude-rules: + - text: "Use of weak random number generator" + linters: + - gosec + - text: "comment on exported var" + linters: + - golint + - text: "don't use an underscore in package name" + linters: + - golint + - text: "ST1003:" + linters: + - stylecheck + # FIXME: Disabled until golangci-lint updates stylecheck with this fix: + # https://github.com/dominikh/go-tools/issues/389 + - text: "ST1016:" + linters: + - stylecheck + max-issues-per-linter: 10000 + max-same-issues: 10000 + +linters-settings: + dogsled: + max-blank-identifiers: 3 + maligned: + # print struct with more effective memory layout or not, false by default + suggest-new: true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..579d097ec1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,127 @@ + + +# Changelog + +## Unreleased + +### Features + +* (rpc) [\#552](https://github.com/ChainSafe/ethermint/pull/552) Implement Eth Personal namespace `personal_importRawKey`. + +### Bug fixes + +* (keys) [\#554](https://github.com/ChainSafe/ethermint/pull/554) Fix private key derivation. +* (app/ante) [\#550](https://github.com/ChainSafe/ethermint/pull/550) Update ante handler nonce verification to accept any nonce greater than or equal to the expected nonce to allow to successive transactions. + +## [v0.2.0] - 2020-09-24 + +### State Machine Breaking + +* (app) [\#540](https://github.com/ChainSafe/ethermint/issues/540) Chain identifier's format has been changed to match the Cosmos `chainID` [standard](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-5.md), which is required for IBC. The epoch number of the ID is used as the EVM `chainID`. + +### API Breaking + +* (types) [\#503](https://github.com/ChainSafe/ethermint/pull/503) The `types.DenomDefault` constant for `"aphoton"` has been renamed to `types.AttoPhoton`. + +### Improvements + +* (types) [\#504](https://github.com/ChainSafe/ethermint/pull/504) Unmarshal a JSON `EthAccount` using an Ethereum hex address in addition to Bech32. +* (types) [\#503](https://github.com/ChainSafe/ethermint/pull/503) Add `--coin-denom` flag to testnet command that sets the given coin denomination to SDK and Ethermint parameters. +* (types) [\#502](https://github.com/ChainSafe/ethermint/pull/502) `EthAccount` now also exposes the Ethereum hex address in `string` format to clients. +* (types) [\#494](https://github.com/ChainSafe/ethermint/pull/494) Update `EthAccount` public key JSON type to `string`. +* (app) [\#471](https://github.com/ChainSafe/ethermint/pull/471) Add `x/upgrade` module for managing software updates. +* (`x/evm`) [\#458](https://github.com/ChainSafe/ethermint/pull/458) Define parameter for token denomination used for the EVM module. +* (`x/evm`) [\#443](https://github.com/ChainSafe/ethermint/issues/443) Support custom Ethereum `ChainConfig` params. +* (types) [\#434](https://github.com/ChainSafe/ethermint/issues/434) Update default denomination to Atto Photon (`aphoton`). +* (types) [\#515](https://github.com/ChainSafe/ethermint/pull/515) Update minimum gas price to be 1. + +### Bug Fixes + +* (ante) [\#525](https://github.com/ChainSafe/ethermint/pull/525) Add message validation decorator to `AnteHandler` for `MsgEthereumTx`. +* (types) [\#507](https://github.com/ChainSafe/ethermint/pull/507) Fix hardcoded `aphoton` on `EthAccount` balance getter and setter. +* (types) [\#501](https://github.com/ChainSafe/ethermint/pull/501) Fix bech32 encoding error by using the compressed ethereum secp256k1 public key. +* (`x/evm`) [\#496](https://github.com/ChainSafe/ethermint/pull/496) Fix bugs on `journal.revert` and `CommitStateDB.Copy`. +* (types) [\#480](https://github.com/ChainSafe/ethermint/pull/480) Update [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) coin type to `60` to satisfy [EIP84](https://github.com/ethereum/EIPs/issues/84). +* (types) [\#513](https://github.com/ChainSafe/ethermint/pull/513) Fix simulated transaction bug that was causing a consensus error by unintentionally affecting the state. + +## [v0.1.0] - 2020-08-23 + +### Improvements + +* (sdk) [\#386](https://github.com/ChainSafe/ethermint/pull/386) Bump Cosmos SDK version to [v0.39.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.39.1) +* (`x/evm`) [\#181](https://github.com/ChainSafe/ethermint/issues/181) Updated EVM module to the recommended module structure. +* (app) [\#188](https://github.com/ChainSafe/ethermint/issues/186) Misc cleanup: + * (`x/evm`) Rename `EthereumTxMsg` --> `MsgEthereumTx` and `EmintMsg` --> `MsgEthermint` for consistency with SDK standards + * Updated integration and unit tests to use `EthermintApp` as testing suite + * Use expected `Keeper` interface for `AccountKeeper` + * Replaced `count` type in keeper with `int` + * Add SDK events for transactions +* [\#236](https://github.com/ChainSafe/ethermint/pull/236) Changes from upgrade: + * (`app/ante`) Moved `AnteHandler` implementation to `app/ante` + * (keys) Marked `ExportEthKeyCommand` as **UNSAFE** + * (`x/evm`) Moved `BeginBlock` and `EndBlock` to `x/evm/abci.go` +* (`x/evm`) [\#255](https://github.com/ChainSafe/ethermint/pull/255) Add missing `GenesisState` fields and support `ExportGenesis` functionality. +* [\#272](https://github.com/ChainSafe/ethermint/pull/272) Add `Logger` for evm module. +* [\#317](https://github.com/ChainSafe/ethermint/pull/317) `GenesisAccount` validation. +* (`x/evm`) [\#319](https://github.com/ChainSafe/ethermint/pull/319) Various evm improvements: + * Add transaction `[]*ethtypes.Logs` to evm's `GenesisState` to persist logs after an upgrade. + * Remove evm `CodeKey` and `BlockKey`in favor of a prefix `Store`. + * Set `BlockBloom` during `EndBlock` instead of `BeginBlock`. + * `Commit` state object and `Finalize` storage after `InitGenesis` setup. +* (rpc) [\#325](https://github.com/ChainSafe/ethermint/pull/325) `eth_coinbase` JSON-RPC query now returns the node's validator address. + +### Features + +* (build) [\#378](https://github.com/ChainSafe/ethermint/pull/378) Create multi-node, local, automated testnet setup with `make localnet-start`. +* (rpc) [\#330](https://github.com/ChainSafe/ethermint/issues/330) Implement `PublicFilterAPI`'s `EventSystem` which subscribes to Tendermint events upon `Filter` creation. +* (rpc) [\#231](https://github.com/ChainSafe/ethermint/issues/231) Implement `NewBlockFilter` in rpc/filters.go which instantiates a polling block filter + * Polls for new blocks via `BlockNumber` rpc call; if block number changes, it requests the new block via `GetBlockByNumber` rpc call and adds it to its internal list of blocks + * Update `uninstallFilter` and `getFilterChanges` accordingly + * `uninstallFilter` stops the polling goroutine + * `getFilterChanges` returns the filter's internal list of block hashes and resets it +* (rpc) [\#54](https://github.com/ChainSafe/ethermint/issues/54), [\#55](https://github.com/ChainSafe/ethermint/issues/55) + Implement `eth_getFilterLogs` and `eth_getLogs`: + * For a given filter, look through each block for transactions. If there are transactions in the block, get the logs from it, and filter using the filterLogs method + * `eth_getLogs` and `eth_getFilterChanges` for log filters use the same underlying method as `eth_getFilterLogs` + * update `HandleMsgEthereumTx` to store logs using the ethereum hash +* (app) [\#187](https://github.com/ChainSafe/ethermint/issues/187) Add support for simulations. + +### Bug Fixes + +* (rpc) [\#305](https://github.com/ChainSafe/ethermint/issues/305) Update `eth_getTransactionCount` to check for account existence before getting sequence and return 0 as the nonce if it doesn't exist. +* (`x/evm`) [\#319](https://github.com/ChainSafe/ethermint/pull/319) Fix `SetBlockHash` that was setting the incorrect height during `BeginBlock`. +* (`x/evm`) [\#176](https://github.com/ChainSafe/ethermint/issues/176) Updated Web3 transaction hash from using RLP hash. Now all transaction hashes exposed are amino hashes: + * Removes `Hash()` (RLP) function from `MsgEthereumTx` to avoid confusion or misuse in future. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dfe6f7d630..59bdeefab9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -73,7 +73,7 @@ Ethermint utilizes [semantic versioning](https://semver.org/). ### Development Procedure: - the latest state of development is on `develop` - `develop` must never fail `make test` -- `develop` should not fail `make test-lint` +- `develop` should not fail `make lint` - no --force onto `develop` (except when reverting a broken commit, which should seldom happen) - create a development branch either on github.com/cosmos/ethermint, or your fork (using `git remote add origin`) - [squash your commits](https://github.com/todotxt/todo.txt-android/wiki/Squash-All-Commits-Related-to-a-Single-Issue-into-a-Single-Commit) into an individual commit diff --git a/Dockerfile b/Dockerfile index 125351752f..f41e8b99c9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,20 @@ FROM golang:alpine AS build-env # Set up dependencies -ENV PACKAGES make git curl build-base +ENV PACKAGES git build-base # Set working directory for the build -WORKDIR /go/src/github.com/cosmos/ethermint +WORKDIR /go/src/github.com/Chainsafe/ethermint # Install dependencies RUN apk add --update $PACKAGES +RUN apk add linux-headers # Add source files COPY . . # Make the binary -RUN make tools deps build +RUN make build # Final image FROM alpine @@ -23,7 +24,8 @@ RUN apk add --update ca-certificates WORKDIR /root # Copy over binaries from the build-env -COPY --from=build-env /go/src/github.com/cosmos/ethermint/build/emintd /usr/bin/emintd +COPY --from=build-env /go/src/github.com/Chainsafe/ethermint/build/ethermintd /usr/bin/ethermintd +COPY --from=build-env /go/src/github.com/Chainsafe/ethermint/build/ethermintcli /usr/bin/ethermintcli -# Run emintd by default -CMD ["emintd"] +# Run ethermintd by default +CMD ["ethermintd"] diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index dbc6c40d72..0000000000 --- a/Gopkg.lock +++ /dev/null @@ -1,803 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - branch = "master" - digest = "1:8038f3159385d2017d12ce7d8ccf25e59554a265d76d8c31f8c80acb589da6c6" - name = "github.com/aristanetworks/goarista" - packages = ["monotime"] - pruneopts = "T" - revision = "5bb443fba8e05f4a819301a63af91fe3cbadcc17" - -[[projects]] - branch = "master" - digest = "1:ad4589ec239820ee99eb01c1ad47ebc5f8e02c4f5103a9b210adff9696d89f36" - name = "github.com/beorn7/perks" - packages = ["quantile"] - pruneopts = "T" - revision = "3a771d992973f24aa725d07868b467d1ddfceafb" - -[[projects]] - branch = "master" - digest = "1:0bd9f11575e82b723837f50e170d010ec29a50aa8ca02a962c439146f03aea55" - name = "github.com/btcsuite/btcd" - packages = ["btcec"] - pruneopts = "T" - revision = "67e573d211ace594f1366b4ce9d39726c4b19bd0" - -[[projects]] - digest = "1:d0d998526cfb68788229a31c16a557fdf1fbbb510654be6b3732c2758e06b533" - name = "github.com/btcsuite/btcutil" - packages = ["bech32"] - pruneopts = "T" - revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" - -[[projects]] - branch = "develop" - digest = "1:83eccb94de6a2aadff1bddb7020e3850131645ff45e9ce9093d1604e407130e7" - name = "github.com/cosmos/cosmos-sdk" - packages = [ - "baseapp", - "codec", - "store", - "types", - "version", - "x/auth", - "x/bank", - "x/gov", - "x/gov/tags", - "x/mock", - "x/params", - "x/params/subspace", - "x/slashing", - "x/stake", - "x/stake/keeper", - "x/stake/querier", - "x/stake/tags", - "x/stake/types", - ] - pruneopts = "T" - revision = "ec9c4ea543b5d0f558cf6ad9f1386d26cfe87f28" - -[[projects]] - digest = "1:9f42202ac457c462ad8bb9642806d275af9ab4850cf0b1960b9c6f083d4a309a" - name = "github.com/davecgh/go-spew" - packages = ["spew"] - pruneopts = "T" - revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" - version = "v1.1.1" - -[[projects]] - digest = "1:e47d51dab652d26c3fba6f8cba403f922d02757a82abdc77e90df7948daf296e" - name = "github.com/deckarep/golang-set" - packages = ["."] - pruneopts = "T" - revision = "cbaa98ba5575e67703b32b4b19f73c91f3c4159e" - version = "v1.7.1" - -[[projects]] - branch = "master" - digest = "1:67d0b50be0549e610017cb91e0b0b745ec0cad7c613bc8e18ff2d1c1fc8825a7" - name = "github.com/edsrzf/mmap-go" - packages = ["."] - pruneopts = "T" - revision = "0bce6a6887123b67a60366d2c9fe2dfb74289d2e" - -[[projects]] - branch = "ethermint-statedb" - digest = "1:00d8053ec8370727a6ecb12cdbfc0b3ce08bce2f8ac2aa34d57402ff09243517" - name = "github.com/ethereum/go-ethereum" - packages = [ - ".", - "accounts", - "accounts/abi", - "accounts/keystore", - "accounts/usbwallet", - "accounts/usbwallet/internal/trezor", - "common", - "common/bitutil", - "common/hexutil", - "common/math", - "common/mclock", - "common/prque", - "consensus", - "consensus/ethash", - "consensus/misc", - "core", - "core/rawdb", - "core/state", - "core/types", - "core/vm", - "crypto", - "crypto/bn256", - "crypto/bn256/cloudflare", - "crypto/bn256/google", - "crypto/ecies", - "crypto/secp256k1", - "crypto/sha3", - "eth/downloader", - "ethdb", - "event", - "internal/ethapi", - "log", - "metrics", - "p2p", - "p2p/discover", - "p2p/discv5", - "p2p/nat", - "p2p/netutil", - "params", - "rlp", - "rpc", - "signer/core", - "trie", - ] - pruneopts = "T" - revision = "0a57b29f0c8e6dc27901fae1c91d3758723b81eb" - source = "github.com/alexanderbez/go-ethereum" - -[[projects]] - digest = "1:7fc160b460a6fc506b37fcca68332464c3f2cd57b6e3f111f26c5bbfd2d5518e" - name = "github.com/fsnotify/fsnotify" - packages = ["."] - pruneopts = "T" - revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" - version = "v1.4.7" - -[[projects]] - digest = "1:0b9c3ad6c948d57a379da9c4e1cdd989b1c73ddc5ec8673f52a9539ce60a109b" - name = "github.com/go-kit/kit" - packages = [ - "log", - "log/level", - "log/term", - "metrics", - "metrics/discard", - "metrics/internal/lv", - "metrics/prometheus", - ] - pruneopts = "T" - revision = "4dc7be5d2d12881735283bcab7352178e190fc71" - version = "v0.6.0" - -[[projects]] - digest = "1:31a18dae27a29aa074515e43a443abfd2ba6deb6d69309d8d7ce789c45f34659" - name = "github.com/go-logfmt/logfmt" - packages = ["."] - pruneopts = "T" - revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" - version = "v0.3.0" - -[[projects]] - digest = "1:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d" - name = "github.com/go-stack/stack" - packages = ["."] - pruneopts = "T" - revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a" - version = "v1.8.0" - -[[projects]] - digest = "1:da39f4a22829ca95e63566208e0ea42d6f055f41dff1b14fdab88d88f62df653" - name = "github.com/gogo/protobuf" - packages = [ - "gogoproto", - "jsonpb", - "proto", - "protoc-gen-gogo/descriptor", - "sortkeys", - "types", - ] - pruneopts = "T" - revision = "636bf0302bc95575d69441b25a2603156ffdddf1" - version = "v1.1.1" - -[[projects]] - digest = "1:832e17df5ff8bbe0e0693d2fb46c5e53f96c662ee804049ce3ab6557df74e3ab" - name = "github.com/golang/protobuf" - packages = [ - "proto", - "protoc-gen-go/descriptor", - "ptypes", - "ptypes/any", - "ptypes/duration", - "ptypes/timestamp", - ] - pruneopts = "T" - revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" - version = "v1.1.0" - -[[projects]] - branch = "master" - digest = "1:6027b20c168728321bd99ad01f35118eded457b01c03e647a84833ab331f2f5b" - name = "github.com/golang/snappy" - packages = ["."] - pruneopts = "T" - revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" - -[[projects]] - digest = "1:3a26588bc48b96825977c1b3df964f8fd842cd6860cc26370588d3563433cf11" - name = "github.com/google/uuid" - packages = ["."] - pruneopts = "T" - revision = "d460ce9f8df2e77fb1ba55ca87fafed96c607494" - version = "v1.0.0" - -[[projects]] - digest = "1:0ead695774eaa7bf1a284d246febe82054767941de80ab2328a194b088f07026" - name = "github.com/gorilla/websocket" - packages = ["."] - pruneopts = "T" - revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" - version = "v1.2.0" - -[[projects]] - digest = "1:8ec8d88c248041a6df5f6574b87bc00e7e0b493881dad2e7ef47b11dc69093b5" - name = "github.com/hashicorp/golang-lru" - packages = [ - ".", - "simplelru", - ] - pruneopts = "T" - revision = "20f1fb78b0740ba8c3cb143a61e86ba5c8669768" - version = "v0.5.0" - -[[projects]] - digest = "1:071bcbf82c289fba4d3f63c876bf4f0ba7eda625cd60795e0a03ccbf949e517a" - name = "github.com/hashicorp/hcl" - packages = [ - ".", - "hcl/ast", - "hcl/parser", - "hcl/scanner", - "hcl/strconv", - "hcl/token", - "json/parser", - "json/scanner", - "json/token", - ] - pruneopts = "T" - revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241" - version = "v1.0.0" - -[[projects]] - digest = "1:a33cc2e4fb12c58430d2aae5834ff6e84cb609da97692e1fe2aa0cd5ebc92623" - name = "github.com/huin/goupnp" - packages = [ - ".", - "dcps/internetgateway1", - "dcps/internetgateway2", - "httpu", - "scpd", - "soap", - "ssdp", - ] - pruneopts = "T" - revision = "656e61dfadd241c7cbdd22a023fa81ecb6860ea8" - version = "v1.0.0" - -[[projects]] - digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" - name = "github.com/inconshreveable/mousetrap" - packages = ["."] - pruneopts = "T" - revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" - version = "v1.0" - -[[projects]] - digest = "1:32b82e71cf24f8b78323e0d7903c4b90278486283965aa2a19b1ea13763b8f34" - name = "github.com/jackpal/go-nat-pmp" - packages = ["."] - pruneopts = "T" - revision = "c9cfead9f2a36ddf3daa40ba269aa7f4bbba6b62" - version = "v1.0.1" - -[[projects]] - branch = "master" - digest = "1:dc6b1a6801b3055e9bd3da4cd1e568606eb48118cc6f28e947783aa5d998ad74" - name = "github.com/jmhodges/levigo" - packages = ["."] - pruneopts = "T" - revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9" - -[[projects]] - branch = "master" - digest = "1:01f1325bf6f105bb633029a2d8b63f1a2357181e60af8dadabf14ad2e84398c5" - name = "github.com/karalabe/hid" - packages = ["."] - pruneopts = "T" - revision = "2b4488a37358b7283de4f9622553e85ebbe73125" - -[[projects]] - branch = "master" - digest = "1:a64e323dc06b73892e5bb5d040ced475c4645d456038333883f58934abbf6f72" - name = "github.com/kr/logfmt" - packages = ["."] - pruneopts = "T" - revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" - -[[projects]] - digest = "1:53e8c5c79716437e601696140e8b1801aae4204f4ec54a504333702a49572c4f" - name = "github.com/magiconair/properties" - packages = ["."] - pruneopts = "T" - revision = "c2353362d570a7bfa228149c62842019201cfb71" - version = "v1.8.0" - -[[projects]] - digest = "1:a8e3d14801bed585908d130ebfc3b925ba642208e6f30d879437ddfc7bb9b413" - name = "github.com/matttproud/golang_protobuf_extensions" - packages = ["pbutil"] - pruneopts = "T" - revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" - version = "v1.0.1" - -[[projects]] - digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318" - name = "github.com/mitchellh/mapstructure" - packages = ["."] - pruneopts = "T" - revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe" - version = "v1.1.2" - -[[projects]] - digest = "1:e5d0bd87abc2781d14e274807a470acd180f0499f8bf5bb18606e9ec22ad9de9" - name = "github.com/pborman/uuid" - packages = ["."] - pruneopts = "T" - revision = "adf5a7427709b9deb95d29d3fa8a2bf9cfd388f1" - version = "v1.2" - -[[projects]] - digest = "1:ccf9949c9c53e85dcb7e2905fc620571422567040925381e6baa62f0b7b850fe" - name = "github.com/pelletier/go-toml" - packages = ["."] - pruneopts = "T" - revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" - version = "v1.2.0" - -[[projects]] - digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" - name = "github.com/pkg/errors" - packages = ["."] - pruneopts = "T" - revision = "645ef00459ed84a119197bfb8d8205042c6df63d" - version = "v0.8.0" - -[[projects]] - digest = "1:22aa691fe0213cb5c07d103f9effebcb7ad04bee45a0ce5fe5369d0ca2ec3a1f" - name = "github.com/pmezard/go-difflib" - packages = ["difflib"] - pruneopts = "T" - revision = "792786c7400a136282c1664665ae0a8db921c6c2" - version = "v1.0.0" - -[[projects]] - digest = "1:98aa8bc119587e8bddd558bf2921a645ea6c0ff3195760142113d4dc7cab509f" - name = "github.com/prometheus/client_golang" - packages = [ - "prometheus", - "prometheus/internal", - "prometheus/promhttp", - ] - pruneopts = "T" - revision = "abad2d1bd44235a26707c172eab6bca5bf2dbad3" - version = "v0.9.1" - -[[projects]] - branch = "master" - digest = "1:185cf55b1f44a1bf243558901c3f06efa5c64ba62cfdcbb1bf7bbe8c3fb68561" - name = "github.com/prometheus/client_model" - packages = ["go"] - pruneopts = "T" - revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f" - -[[projects]] - branch = "master" - digest = "1:95442856f5c1df4ff0be91b5a320ee717dd539d4091a3574aeb96bfbd407aa41" - name = "github.com/prometheus/common" - packages = [ - "expfmt", - "internal/bitbucket.org/ww/goautoneg", - "model", - ] - pruneopts = "T" - revision = "0b1957f9d949dfa3084171a6ec5642b38055276a" - -[[projects]] - branch = "master" - digest = "1:57bf59ce0c73cef5cc4796a5d64f1ec5b81f6335f242d4a80a62b0a6edc4b77f" - name = "github.com/prometheus/procfs" - packages = [ - ".", - "internal/util", - "nfs", - "xfs", - ] - pruneopts = "T" - revision = "185b4288413d2a0dd0806f78c90dde719829e5ae" - -[[projects]] - digest = "1:523d2c2500965d035691347a6d30befd53fde95fad16e0b94bef5d3d2cca8ff7" - name = "github.com/rcrowley/go-metrics" - packages = ["."] - pruneopts = "T" - revision = "e2704e165165ec55d062f5919b4b29494e9fa790" - -[[projects]] - digest = "1:9787d2d3220cbfd444596afd03ab0abcf391df169b789fbe3eae27fa2e426cf6" - name = "github.com/rjeczalik/notify" - packages = ["."] - pruneopts = "T" - revision = "69d839f37b13a8cb7a78366f7633a4071cb43be7" - version = "v0.9.2" - -[[projects]] - digest = "1:a8a03bca5a81878daa4958136f3372af00437c61129ca088a430b0b786b9378a" - name = "github.com/rs/cors" - packages = ["."] - pruneopts = "T" - revision = "9a47f48565a795472d43519dd49aac781f3034fb" - version = "v1.6.0" - -[[projects]] - digest = "1:b7bf9fd95d38ebe6726a63b7d0320611f7c920c64e2c8313eba0cec51926bf55" - name = "github.com/spf13/afero" - packages = [ - ".", - "mem", - ] - pruneopts = "T" - revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd" - version = "v1.1.2" - -[[projects]] - digest = "1:08d65904057412fc0270fc4812a1c90c594186819243160dc779a402d4b6d0bc" - name = "github.com/spf13/cast" - packages = ["."] - pruneopts = "T" - revision = "8c9545af88b134710ab1cd196795e7f2388358d7" - version = "v1.3.0" - -[[projects]] - digest = "1:52565bd966162d1f4579757f66ce6a7ca9054e7f6b662f0c7c96e4dd228fd017" - name = "github.com/spf13/cobra" - packages = ["."] - pruneopts = "T" - revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b" - version = "v0.0.1" - -[[projects]] - digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb" - name = "github.com/spf13/jwalterweatherman" - packages = ["."] - pruneopts = "T" - revision = "4a4406e478ca629068e7768fc33f3f044173c0a6" - version = "v1.0.0" - -[[projects]] - digest = "1:0f775ea7a72e30d5574267692aaa9ff265aafd15214a7ae7db26bc77f2ca04dc" - name = "github.com/spf13/pflag" - packages = ["."] - pruneopts = "T" - revision = "298182f68c66c05229eb03ac171abe6e309ee79a" - version = "v1.0.3" - -[[projects]] - digest = "1:a8a1cbf83d6ba47a3421e51b5dd1999e1f64f6175c64295d6b42bdea55312a79" - name = "github.com/spf13/viper" - packages = ["."] - pruneopts = "T" - revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7" - version = "v1.0.0" - -[[projects]] - digest = "1:8f39978e4fb2a11d43cc954f2ab458cb38995d4c1557b6d3a7c8cafe0ec2277c" - name = "github.com/stretchr/testify" - packages = [ - "assert", - "require", - "suite", - ] - pruneopts = "T" - revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" - version = "v1.2.1" - -[[projects]] - branch = "master" - digest = "1:fa0605d74039818b662892c950cfd9938ab81ebb5f8e2479ceb4734cdae21df3" - name = "github.com/syndtr/goleveldb" - packages = [ - "leveldb", - "leveldb/cache", - "leveldb/comparer", - "leveldb/errors", - "leveldb/filter", - "leveldb/iterator", - "leveldb/journal", - "leveldb/memdb", - "leveldb/opt", - "leveldb/storage", - "leveldb/table", - "leveldb/util", - ] - pruneopts = "T" - revision = "f9080354173f192dfc8821931eacf9cfd6819253" - -[[projects]] - digest = "1:71ffd1fca92b4972ecd588cf13d9929d4f444659788e9128d055a9126498d41d" - name = "github.com/tendermint/btcd" - packages = ["btcec"] - pruneopts = "T" - revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df" - -[[projects]] - digest = "1:02462ad4cc9b135c4ebfb9edccb53e8c705bbebdbf8664799ea641440188387e" - name = "github.com/tendermint/go-amino" - packages = ["."] - pruneopts = "T" - revision = "dc14acf9ef15f85828bfbc561ed9dd9d2a284885" - version = "v0.14.1" - -[[projects]] - digest = "1:8a1dc8fc625c867614b48327112718c51c0ca83453c8a043f2f23721e19b353f" - name = "github.com/tendermint/iavl" - packages = ["."] - pruneopts = "T" - revision = "de0740903a67b624d887f9055d4c60175dcfa758" - version = "v0.12.0" - -[[projects]] - digest = "1:844c7ba6332e1e6f073d7ac69768fd19f761bdf5964b559bd4b47103a0629144" - name = "github.com/tendermint/tendermint" - packages = [ - "abci/client", - "abci/example/code", - "abci/example/kvstore", - "abci/server", - "abci/types", - "blockchain", - "cmd/tendermint/commands", - "config", - "consensus", - "consensus/types", - "crypto", - "crypto/ed25519", - "crypto/encoding/amino", - "crypto/merkle", - "crypto/multisig", - "crypto/multisig/bitarray", - "crypto/secp256k1", - "crypto/tmhash", - "evidence", - "libs/autofile", - "libs/bech32", - "libs/cli", - "libs/cli/flags", - "libs/clist", - "libs/common", - "libs/db", - "libs/events", - "libs/fail", - "libs/flowrate", - "libs/log", - "libs/pubsub", - "libs/pubsub/query", - "lite", - "lite/client", - "lite/errors", - "lite/proxy", - "mempool", - "node", - "p2p", - "p2p/conn", - "p2p/pex", - "p2p/upnp", - "privval", - "proxy", - "rpc/client", - "rpc/core", - "rpc/core/types", - "rpc/grpc", - "rpc/lib/client", - "rpc/lib/server", - "rpc/lib/types", - "state", - "state/txindex", - "state/txindex/kv", - "state/txindex/null", - "types", - "types/time", - "version", - ] - pruneopts = "T" - revision = "v0.27.0" - -[[projects]] - digest = "1:d738326441b0b732070d727891855573dcb579e74d82fcf9a9459d3257f2eb0c" - name = "golang.org/x/crypto" - packages = [ - "chacha20poly1305", - "curve25519", - "ed25519", - "ed25519/internal/edwards25519", - "hkdf", - "internal/chacha20", - "internal/subtle", - "nacl/box", - "nacl/secretbox", - "pbkdf2", - "poly1305", - "ripemd160", - "salsa20/salsa", - "scrypt", - "ssh/terminal", - ] - pruneopts = "T" - revision = "3764759f34a542a3aef74d6b02e35be7ab893bba" - source = "https://github.com/tendermint/crypto" - -[[projects]] - digest = "1:5fdc7adede42f80d6201258355d478d856778e21d735f14972abd8ff793fdbf7" - name = "golang.org/x/net" - packages = [ - "context", - "html", - "html/atom", - "html/charset", - "http/httpguts", - "http2", - "http2/hpack", - "idna", - "internal/timeseries", - "netutil", - "trace", - "websocket", - ] - pruneopts = "T" - revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f" - -[[projects]] - digest = "1:96672c90ede3a9cd379151c13c436c93efe845c4a3fdd2ce2a94e9c96f233a2c" - name = "golang.org/x/sys" - packages = [ - "cpu", - "unix", - "windows", - ] - pruneopts = "T" - revision = "4e1fef5609515ec7a2cee7b5de30ba6d9b438cbf" - -[[projects]] - digest = "1:6164911cb5e94e8d8d5131d646613ff82c14f5a8ce869de2f6d80d9889df8c5a" - name = "golang.org/x/text" - packages = [ - "collate", - "collate/build", - "encoding", - "encoding/charmap", - "encoding/htmlindex", - "encoding/internal", - "encoding/internal/identifier", - "encoding/japanese", - "encoding/korean", - "encoding/simplifiedchinese", - "encoding/traditionalchinese", - "encoding/unicode", - "internal/colltab", - "internal/gen", - "internal/tag", - "internal/triegen", - "internal/ucd", - "internal/utf8internal", - "language", - "runes", - "secure/bidirule", - "transform", - "unicode/bidi", - "unicode/cldr", - "unicode/norm", - "unicode/rangetable", - ] - pruneopts = "T" - revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" - version = "v0.3.0" - -[[projects]] - branch = "master" - digest = "1:2460b53d2a66eb9897a17c59ce16c82eeec9affaa31a3ce5814d254abc80fbbd" - name = "google.golang.org/genproto" - packages = ["googleapis/rpc/status"] - pruneopts = "T" - revision = "5fc9ac5403620be16bcdb0c8e7644b1178472c3b" - -[[projects]] - digest = "1:adafc60b1d4688759f3fc8f9089e71dd17abd123f4729de6b913bf08c9143770" - name = "google.golang.org/grpc" - packages = [ - ".", - "balancer", - "balancer/base", - "balancer/roundrobin", - "codes", - "connectivity", - "credentials", - "encoding", - "encoding/proto", - "grpclog", - "internal", - "internal/backoff", - "internal/channelz", - "internal/grpcrand", - "keepalive", - "metadata", - "naming", - "peer", - "resolver", - "resolver/dns", - "resolver/passthrough", - "stats", - "status", - "tap", - "transport", - ] - pruneopts = "T" - revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8" - version = "v1.13.0" - -[[projects]] - branch = "v2" - digest = "1:3d3f9391ab615be8655ae0d686a1564f3fec413979bb1aaf018bac1ec1bb1cc7" - name = "gopkg.in/natefinch/npipe.v2" - packages = ["."] - pruneopts = "T" - revision = "c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6" - -[[projects]] - digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" - name = "gopkg.in/yaml.v2" - packages = ["."] - pruneopts = "T" - revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" - version = "v2.2.1" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/cosmos/cosmos-sdk/baseapp", - "github.com/cosmos/cosmos-sdk/codec", - "github.com/cosmos/cosmos-sdk/store", - "github.com/cosmos/cosmos-sdk/types", - "github.com/cosmos/cosmos-sdk/x/auth", - "github.com/cosmos/cosmos-sdk/x/bank", - "github.com/cosmos/cosmos-sdk/x/gov", - "github.com/cosmos/cosmos-sdk/x/params", - "github.com/cosmos/cosmos-sdk/x/slashing", - "github.com/cosmos/cosmos-sdk/x/stake", - "github.com/ethereum/go-ethereum/common", - "github.com/ethereum/go-ethereum/common/hexutil", - "github.com/ethereum/go-ethereum/consensus", - "github.com/ethereum/go-ethereum/consensus/ethash", - "github.com/ethereum/go-ethereum/consensus/misc", - "github.com/ethereum/go-ethereum/core", - "github.com/ethereum/go-ethereum/core/state", - "github.com/ethereum/go-ethereum/core/types", - "github.com/ethereum/go-ethereum/core/vm", - "github.com/ethereum/go-ethereum/crypto", - "github.com/ethereum/go-ethereum/crypto/secp256k1", - "github.com/ethereum/go-ethereum/crypto/sha3", - "github.com/ethereum/go-ethereum/params", - "github.com/ethereum/go-ethereum/rlp", - "github.com/ethereum/go-ethereum/rpc", - "github.com/ethereum/go-ethereum/signer/core", - "github.com/pkg/errors", - "github.com/stretchr/testify/require", - "github.com/stretchr/testify/suite", - "github.com/tendermint/tendermint/abci/types", - "github.com/tendermint/tendermint/crypto", - "github.com/tendermint/tendermint/libs/common", - "github.com/tendermint/tendermint/libs/db", - "github.com/tendermint/tendermint/libs/log", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index 857702dd58..0000000000 --- a/Gopkg.toml +++ /dev/null @@ -1,63 +0,0 @@ -[[constraint]] - name = "github.com/ethereum/go-ethereum" - # TODO: Remove this forked source and branch once Turbo-Geth is ready as a - # client. - source = "github.com/alexanderbez/go-ethereum" - branch = "ethermint-statedb" - -[[constraint]] - name = "github.com/cosmos/cosmos-sdk" - revision = "ec9c4ea543b5d0f558cf6ad9f1386d26cfe87f28" - # version = "v0.28.0" - -[[constraint]] - name = "github.com/hashicorp/golang-lru" - revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3" - -[[constraint]] - name = "github.com/spf13/cobra" - version = "~0.0.1" - -[[constraint]] - name = "github.com/stretchr/testify" - version = "=1.2.1" - -[[constraint]] - name = "github.com/pkg/errors" - version = "=0.8.0" - -[[constraint]] - name = "golang.org/x/net" - revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f" - -####################### -# dependecy overrides # -####################### - -[[override]] - name = "gopkg.in/fatih/set.v0" - version = "=0.1.0" - -[[override]] - name = "github.com/tendermint/go-amino" - version = "v0.14.1" - -[[override]] - name = "golang.org/x/crypto" - source = "https://github.com/tendermint/crypto" - revision = "3764759f34a542a3aef74d6b02e35be7ab893bba" - -[[override]] - name = "github.com/tendermint/iavl" - version = "v0.12.0" - -[[override]] - name = "github.com/tendermint/tendermint" - revision = "v0.27.0" - -[[override]] - name = "golang.org/x/sys" - revision = "4e1fef5609515ec7a2cee7b5de30ba6d9b438cbf" - -[prune] - go-tests = true diff --git a/Makefile b/Makefile index c3767ce71c..0e0e570c72 100644 --- a/Makefile +++ b/Makefile @@ -12,158 +12,463 @@ # See the License for the specific language governing permissions and # limitations under the License. -PACKAGES=$(shell go list ./... | grep -Ev 'vendor|importer') -COMMIT_HASH := $(shell git rev-parse --short HEAD) -BUILD_FLAGS = -tags netgo -ldflags "-X github.com/cosmos/ethermint/version.GitCommit=${COMMIT_HASH}" +#!/usr/bin/make -f + +VERSION := $(shell echo $(shell git describe --tags) | sed 's/^v//') +COMMIT := $(shell git log -1 --format='%H') +PACKAGES=$(shell go list ./... | grep -Ev 'vendor|importer|rpc/tester') DOCKER_TAG = unstable DOCKER_IMAGE = cosmos/ethermint -ETHERMINT_DAEMON_BINARY = emintd -ETHERMINT_CLI_BINARY = emintcli +ETHERMINT_DAEMON_BINARY = ethermintd +ETHERMINT_CLI_BINARY = ethermintcli +GO_MOD=GO111MODULE=on +BUILDDIR ?= $(CURDIR)/build +SIMAPP = ./app +LEDGER_ENABLED ?= true -all: tools deps install +ifeq ($(OS),Windows_NT) + DETECTED_OS := windows +else + UNAME_S = $(shell uname -s) +ifeq ($(UNAME_S),Darwin) + DETECTED_OS := mac + else + DETECTED_OS := linux + endif +endif +export DETECTED_OS +export GO111MODULE = on -####################### -### Build / Install ### -####################### +########################################## +# Find OS and Go environment +# GO contains the Go binary +# FS contains the OS file separator +########################################## -build: ifeq ($(OS),Windows_NT) - go build $(BUILD_FLAGS) -o build/$(ETHERMINT_DAEMON_BINARY).exe ./cmd/emintd - go build $(BUILD_FLAGS) -o build/$(ETHERMINT_CLI_BINARY).exe ./cmd/emintcli + GO := $(shell where go.exe 2> NUL) + FS := "\\" else - go build $(BUILD_FLAGS) -o build/$(ETHERMINT_DAEMON_BINARY) ./cmd/emintd/ - go build $(BUILD_FLAGS) -o build/$(ETHERMINT_CLI_BINARY) ./cmd/emintcli/ + GO := $(shell command -v go 2> /dev/null) + FS := "/" endif -install: - go install $(BUILD_FLAGS) ./cmd/emintd - go install $(BUILD_FLAGS) ./cmd/emintcli +ifeq ($(GO),) + $(error could not find go. Is it in PATH? $(GO)) +endif -clean: - @rm -rf ./build ./vendor +GOPATH ?= $(shell $(GO) env GOPATH) +BINDIR ?= $(GOPATH)/bin +RUNSIM = $(BINDIR)/runsim -update-tools: - @echo "--> Updating vendor dependencies" - go get -u -v $(DEP) $(GOLINT) $(GOMETALINTER) $(UNCONVERT) $(INEFFASSIGN) $(MISSPELL) $(ERRCHECK) $(UNPARAM) - - -############################ -### Tools / Dependencies ### -############################ - -########################################################## -### TODO: Move tool depedencies to a separate makefile ### -########################################################## - -DEP = github.com/golang/dep/cmd/dep -GOLINT = github.com/tendermint/lint/golint -GOMETALINTER = gopkg.in/alecthomas/gometalinter.v2 -UNCONVERT = github.com/mdempsky/unconvert -INEFFASSIGN = github.com/gordonklaus/ineffassign -MISSPELL = github.com/client9/misspell/cmd/misspell -ERRCHECK = github.com/kisielk/errcheck -UNPARAM = mvdan.cc/unparam - -DEP_CHECK := $(shell command -v dep 2> /dev/null) -GOLINT_CHECK := $(shell command -v golint 2> /dev/null) -GOMETALINTER_CHECK := $(shell command -v gometalinter.v2 2> /dev/null) -UNCONVERT_CHECK := $(shell command -v unconvert 2> /dev/null) -INEFFASSIGN_CHECK := $(shell command -v ineffassign 2> /dev/null) -MISSPELL_CHECK := $(shell command -v misspell 2> /dev/null) -ERRCHECK_CHECK := $(shell command -v errcheck 2> /dev/null) -UNPARAM_CHECK := $(shell command -v unparam 2> /dev/null) - -tools: -ifdef DEP_CHECK - @echo "Dep is already installed. Run 'make update-tools' to update." -else - @echo "--> Installing dep" - go get -v $(DEP) + +# process build tags + +build_tags = netgo +ifeq ($(LEDGER_ENABLED),true) + ifeq ($(OS),Windows_NT) + GCCEXE = $(shell where gcc.exe 2> NUL) + ifeq ($(GCCEXE),) + $(error gcc.exe not installed for ledger support, please install or set LEDGER_ENABLED=false) + else + build_tags += ledger + endif + else + UNAME_S = $(shell uname -s) + ifeq ($(UNAME_S),OpenBSD) + $(warning OpenBSD detected, disabling ledger support (https://github.com/cosmos/cosmos-sdk/issues/1988)) + else + GCC = $(shell command -v gcc 2> /dev/null) + ifeq ($(GCC),) + $(error gcc not installed for ledger support, please install or set LEDGER_ENABLED=false) + else + build_tags += ledger + endif + endif + endif endif -ifdef GOLINT_CHECK - @echo "Golint is already installed. Run 'make update-tools' to update." + +ifeq ($(WITH_CLEVELDB),yes) + build_tags += gcc +endif +build_tags += $(BUILD_TAGS) +build_tags := $(strip $(build_tags)) + +whitespace := +whitespace += $(whitespace) +comma := , +build_tags_comma_sep := $(subst $(whitespace),$(comma),$(build_tags)) + +# process linker flags + +ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=ethermint \ + -X github.com/cosmos/cosmos-sdk/version.ServerName=$(ETHERMINT_DAEMON_BINARY) \ + -X github.com/cosmos/cosmos-sdk/version.ClientName=$(ETHERMINT_CLI_BINARY) \ + -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \ + -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) \ + -X "github.com/cosmos/cosmos-sdk/version.BuildTags=$(build_tags_comma_sep)" + +ifeq ($(WITH_CLEVELDB),yes) + ldflags += -X github.com/cosmos/cosmos-sdk/types.DBBackend=cleveldb +endif +ldflags += $(LDFLAGS) +ldflags := $(strip $(ldflags)) + +BUILD_FLAGS := -tags "$(build_tags)" -ldflags '$(ldflags)' + +all: tools verify install + +############################################################################### +### Build ### +############################################################################### + +build: go.sum +ifeq ($(OS), Windows_NT) + go build -mod=readonly $(BUILD_FLAGS) -o build/$(ETHERMINT_DAEMON_BINARY).exe ./cmd/$(ETHERMINT_DAEMON_BINARY) + go build -mod=readonly $(BUILD_FLAGS) -o build/$(ETHERMINT_CLI_BINARY).exe ./cmd/$(ETHERMINT_CLI_BINARY) else - @echo "--> Installing golint" - go get -v $(GOLINT) + go build -mod=readonly $(BUILD_FLAGS) -o build/$(ETHERMINT_DAEMON_BINARY) ./cmd/$(ETHERMINT_DAEMON_BINARY) + go build -mod=readonly $(BUILD_FLAGS) -o build/$(ETHERMINT_CLI_BINARY) ./cmd/$(ETHERMINT_CLI_BINARY) endif -ifdef GOMETALINTER_CHECK - @echo "Gometalinter.v2 is already installed. Run 'make update-tools' to update." + go build -mod=readonly ./... + +build-ethermint: go.sum + mkdir -p $(BUILDDIR) + go build -mod=readonly $(BUILD_FLAGS) -o $(BUILDDIR) ./cmd/$(ETHERMINT_DAEMON_BINARY) + go build -mod=readonly $(BUILD_FLAGS) -o $(BUILDDIR) ./cmd/$(ETHERMINT_CLI_BINARY) + +build-ethermint-linux: go.sum + GOOS=linux GOARCH=amd64 CGO_ENABLED=1 $(MAKE) build-ethermint + +.PHONY: build build-ethermint build-ethermint-linux + +install: + ${GO_MOD} go install $(BUILD_FLAGS) ./cmd/$(ETHERMINT_DAEMON_BINARY) + ${GO_MOD} go install $(BUILD_FLAGS) ./cmd/$(ETHERMINT_CLI_BINARY) + +clean: + @rm -rf ./build ./vendor + +.PHONY: install clean + +docker-build: + docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} . + docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest + docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:${COMMIT_HASH} + # update old container + docker rm ethermint + # create a new container from the latest image + docker create --name ethermint -t -i cosmos/ethermint:latest ethermint + # move the binaries to the ./build directory + mkdir -p ./build/ + docker cp ethermint:/usr/bin/ethermintd ./build/ ; \ + docker cp ethermint:/usr/bin/ethermintcli ./build/ + +docker-localnet: + docker build -f ./networks/local/ethermintnode/Dockerfile . -t ethermintd/node + +############################################################################### +### Tools & Dependencies ### +############################################################################### + +TOOLS_DESTDIR ?= $(GOPATH)/bin +RUNSIM = $(TOOLS_DESTDIR)/runsim + +# Install the runsim binary with a temporary workaround of entering an outside +# directory as the "go get" command ignores the -mod option and will polute the +# go.{mod, sum} files. +# +# ref: https://github.com/golang/go/issues/30515 +runsim: $(RUNSIM) +$(RUNSIM): + @echo "Installing runsim..." + @(cd /tmp && ${GO_MOD} go get github.com/cosmos/tools/cmd/runsim@v1.0.0) + +contract-tools: +ifeq (, $(shell which stringer)) + @echo "Installing stringer..." + @go install golang.org/x/tools/cmd/stringer else - @echo "--> Installing gometalinter.v2" - go get -v $(GOMETALINTER) + @echo "stringer already installed; skipping..." endif -ifdef UNCONVERT_CHECK - @echo "Unconvert is already installed. Run 'make update-tools' to update." + +ifeq (, $(shell which go-bindata)) + @echo "Installing go-bindata..." + @go install github.com/kevinburke/go-bindata/go-bindata else - @echo "--> Installing unconvert" - go get -v $(UNCONVERT) + @echo "go-bindata already installed; skipping..." endif -ifdef INEFFASSIGN_CHECK - @echo "Ineffassign is already installed. Run 'make update-tools' to update." + +ifeq (, $(shell which gencodec)) + @echo "Installing gencodec..." + @go install github.com/fjl/gencodec else - @echo "--> Installing ineffassign" - go get -v $(INEFFASSIGN) + @echo "gencodec already installed; skipping..." endif -ifdef MISSPELL_CHECK - @echo "misspell is already installed. Run 'make update-tools' to update." + +ifeq (, $(shell which protoc-gen-go)) + @echo "Installing protoc-gen-go..." + @go install github.com/golang/protobuf/protoc-gen-go else - @echo "--> Installing misspell" - go get -v $(MISSPELL) + @echo "protoc-gen-go already installed; skipping..." endif -ifdef ERRCHECK_CHECK - @echo "errcheck is already installed. Run 'make update-tools' to update." + +ifeq (, $(shell which solcjs)) + @echo "Installing solcjs..." + @apt-get install -f -y protobuf-compiler + @npm install -g solc@0.5.11 else - @echo "--> Installing errcheck" - go get -v $(ERRCHECK) + @echo "solcjs already installed; skipping..." endif -ifdef UNPARAM_CHECK - @echo "unparam is already installed. Run 'make update-tools' to update." + +docs-tools: +ifeq (, $(shell which yarn)) + @echo "Installing yarn..." + @npm install -g yarn else - @echo "--> Installing unparam" - go get -v $(UNPARAM) + @echo "yarn already installed; skipping..." endif -deps: - @rm -rf vendor/ - @echo "--> Running dep ensure" - @dep ensure -v +tools: tools-stamp +tools-stamp: contract-tools docs-tools runsim + # Create dummy file to satisfy dependency and avoid + # rebuilding when this Makefile target is hit twice + # in a row. + touch $@ + +tools-clean: + rm -f $(RUNSIM) + rm -f tools-stamp + +docs-tools-stamp: docs-tools + # Create dummy file to satisfy dependency and avoid + # rebuilding when this Makefile target is hit twice + # in a row. + touch $@ -####################### -### Testing / Misc. ### -####################### +.PHONY: runsim tools tools-stamp tools-clean docs-tools-stamp + +############################################################################### +### Tests & Simulation ### +############################################################################### test: test-unit test-unit: - @go test -v --vet=off $(PACKAGES) + @go test -v ./... $(PACKAGES) test-race: - @go test -v --vet=off -race $(PACKAGES) - -test-cli: - @echo "NO CLI TESTS" - -test-lint: - @echo "--> Running gometalinter..." - @gometalinter.v2 --config=gometalinter.json --exclude=vendor ./... + @go test -v --vet=off -race ./... $(PACKAGES) test-import: @go test ./importer -v --vet=off --run=TestImportBlocks --datadir tmp \ - --blockchain blockchain --timeout=5m + --blockchain blockchain + rm -rf importer/tmp + +test-rpc: + ./scripts/integration-test-all.sh -q 1 -z 1 -s 2 + +test-contract: + @type "npm" 2> /dev/null || (echo 'Npm does not exist. Please install node.js and npm."' && exit 1) + @type "solcjs" 2> /dev/null || (echo 'Solcjs does not exist. Please install solcjs using make contract-tools."' && exit 1) + @type "protoc" 2> /dev/null || (echo 'Failed to install protoc. Please reinstall protoc using make contract-tools.' && exit 1) + bash scripts/contract-test.sh + +test-sim-nondeterminism: + @echo "Running non-determinism test..." + @go test -mod=readonly $(SIMAPP) -run TestAppStateDeterminism -Enabled=true \ + -NumBlocks=100 -BlockSize=200 -Commit=true -Period=0 -v -timeout 24h + +test-sim-custom-genesis-fast: + @echo "Running custom genesis simulation..." + @echo "By default, ${HOME}/.$(ETHERMINT_DAEMON_BINARY)/config/genesis.json will be used." + @go test -mod=readonly $(SIMAPP) -run TestFullAppSimulation -Genesis=${HOME}/.$(ETHERMINT_DAEMON_BINARY)/config/genesis.json \ + -Enabled=true -NumBlocks=100 -BlockSize=200 -Commit=true -Seed=99 -Period=5 -v -timeout 24h + +test-sim-import-export: runsim + @echo "Running application import/export simulation. This may take several minutes..." + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 50 5 TestAppImportExport + +test-sim-after-import: runsim + @echo "Running application simulation-after-import. This may take several minutes..." + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 50 5 TestAppSimulationAfterImport + +test-sim-custom-genesis-multi-seed: runsim + @echo "Running multi-seed custom genesis simulation..." + @echo "By default, ${HOME}/.$(ETHERMINT_DAEMON_BINARY)/config/genesis.json will be used." + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail -Genesis=${HOME}/.$(ETHERMINT_DAEMON_BINARY)/config/genesis.json 400 5 TestFullAppSimulation + +test-sim-multi-seed-long: runsim + @echo "Running multi-seed application simulation. This may take awhile!" + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 500 50 TestFullAppSimulation + +test-sim-multi-seed-short: runsim + @echo "Running multi-seed application simulation. This may take awhile!" + @$(BINDIR)/runsim -Jobs=4 -SimAppPkg=$(SIMAPP) -ExitOnFail 50 10 TestFullAppSimulation + +test-solidity: + @echo "Beginning solidity tests..." + ./scripts/run-solidity-tests.sh + +.PHONY: test test-unit test-race test-import test-rpc test-contract test-solidity + +.PHONY: test-sim-nondeterminism test-sim-custom-genesis-fast test-sim-import-export test-sim-after-import \ + test-sim-custom-genesis-multi-seed test-sim-multi-seed-long test-sim-multi-seed-short + +############################################################################### +### Linting ### +############################################################################### + +lint: + golangci-lint run --out-format=tab --issues-exit-code=0 + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s + +format: + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -name '*.pb.go' | xargs gofmt -w -s + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -name '*.pb.go' | xargs misspell -w + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -name '*.pb.go' | xargs goimports -w -local github.com/tendermint + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -name '*.pb.go' | xargs goimports -w -local github.com/ethereum/go-ethereum + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -name '*.pb.go' | xargs goimports -w -local github.com/cosmos/cosmos-sdk + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -name '*.pb.go' | xargs goimports -w -local github.com/cosmos/ethermint + +.PHONY: lint format + +############################################################################### +### Protobuf ### +############################################################################### + +proto-all: proto-gen proto-lint proto-check-breaking + +proto-gen: + @./scripts/protocgen.sh + +proto-lint: + @buf check lint --error-format=json + +# NOTE: should match the default repo branch +proto-check-breaking: + @buf check breaking --against-input '.git#branch=development' + + +TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/v0.33.3 +GOGO_PROTO_URL = https://raw.githubusercontent.com/regen-network/protobuf/cosmos +COSMOS_PROTO_URL = https://raw.githubusercontent.com/regen-network/cosmos-proto/master +SDK_PROTO_URL = https://raw.githubusercontent.com/cosmos/cosmos-sdk/master + +TM_KV_TYPES = third_party/proto/tendermint/libs/kv +TM_MERKLE_TYPES = third_party/proto/tendermint/crypto/merkle +TM_ABCI_TYPES = third_party/proto/tendermint/abci/types +GOGO_PROTO_TYPES = third_party/proto/gogoproto +COSMOS_PROTO_TYPES = third_party/proto/cosmos-proto +SDK_PROTO_TYPES = third_party/proto/cosmos-sdk/types +AUTH_PROTO_TYPES = third_party/proto/cosmos-sdk/x/auth/types +VESTING_PROTO_TYPES = third_party/proto/cosmos-sdk/x/auth/vesting/types +SUPPLY_PROTO_TYPES = third_party/proto/cosmos-sdk/x/supply/types + +proto-update-deps: + @mkdir -p $(GOGO_PROTO_TYPES) + @curl -sSL $(GOGO_PROTO_URL)/gogoproto/gogo.proto > $(GOGO_PROTO_TYPES)/gogo.proto + + @mkdir -p $(COSMOS_PROTO_TYPES) + @curl -sSL $(COSMOS_PROTO_URL)/cosmos.proto > $(COSMOS_PROTO_TYPES)/cosmos.proto + + @mkdir -p $(TM_ABCI_TYPES) + @curl -sSL $(TM_URL)/abci/types/types.proto > $(TM_ABCI_TYPES)/types.proto + @sed -i '' '8 s|crypto/merkle/merkle.proto|third_party/proto/tendermint/crypto/merkle/merkle.proto|g' $(TM_ABCI_TYPES)/types.proto + @sed -i '' '9 s|libs/kv/types.proto|third_party/proto/tendermint/libs/kv/types.proto|g' $(TM_ABCI_TYPES)/types.proto + + @mkdir -p $(TM_KV_TYPES) + @curl -sSL $(TM_URL)/libs/kv/types.proto > $(TM_KV_TYPES)/types.proto + + @mkdir -p $(TM_MERKLE_TYPES) + @curl -sSL $(TM_URL)/crypto/merkle/merkle.proto > $(TM_MERKLE_TYPES)/merkle.proto + + @mkdir -p $(SDK_PROTO_TYPES) + @curl -sSL $(SDK_PROTO_URL)/types/types.proto > $(SDK_PROTO_TYPES)/types.proto + + @mkdir -p $(AUTH_PROTO_TYPES) + @curl -sSL $(SDK_PROTO_URL)/x/auth/types/types.proto > $(AUTH_PROTO_TYPES)/types.proto + @sed -i '' '5 s|types/types.proto|third_party/proto/cosmos-sdk/types/types.proto|g' $(AUTH_PROTO_TYPES)/types.proto + + @mkdir -p $(VESTING_PROTO_TYPES) + curl -sSL $(SDK_PROTO_URL)/x/auth/vesting/types/types.proto > $(VESTING_PROTO_TYPES)/types.proto + @sed -i '' '5 s|types/types.proto|third_party/proto/cosmos-sdk/types/types.proto|g' $(VESTING_PROTO_TYPES)/types.proto + @sed -i '' '6 s|x/auth/types/types.proto|third_party/proto/cosmos-sdk/x/auth/types/types.proto|g' $(VESTING_PROTO_TYPES)/types.proto + + @mkdir -p $(SUPPLY_PROTO_TYPES) + curl -sSL $(SDK_PROTO_URL)/x/supply/types/types.proto > $(SUPPLY_PROTO_TYPES)/types.proto + @sed -i '' '5 s|types/types.proto|third_party/proto/cosmos-sdk/types/types.proto|g' $(SUPPLY_PROTO_TYPES)/types.proto + @sed -i '' '6 s|x/auth/types/types.proto|third_party/proto/cosmos-sdk/x/auth/types/types.proto|g' $(SUPPLY_PROTO_TYPES)/types.proto + + +.PHONY: proto-all proto-gen proto-lint proto-check-breaking proto-update-deps + + +############################################################################### +### Documentation ### +############################################################################### + +# Start docs site at localhost:8080 +docs-serve: + @cd docs && \ + yarn install && \ + yarn run serve + +# Build the site into docs/.vuepress/dist +docs-build: + @$(MAKE) docs-tools-stamp && \ + cd docs && \ + yarn install && \ + yarn run build godocs: @echo "--> Wait a few seconds and visit http://localhost:6060/pkg/github.com/cosmos/ethermint" godoc -http=:6060 -docker: - docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} . - docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:latest - docker tag ${DOCKER_IMAGE}:${DOCKER_TAG} ${DOCKER_IMAGE}:${COMMIT_HASH} +############################################################################### +### Localnet ### +############################################################################### -format: - @echo "--> Formatting go files" - @find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -w -s - @find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs misspell -w +build-docker-local-ethermint: + @$(MAKE) -C networks/local + +# Run a 4-node testnet locally +localnet-start: localnet-stop +ifeq ($(OS),Windows_NT) + mkdir build & + @$(MAKE) docker-localnet + + IF not exist "build/node0/$(ETHERMINT_DAEMON_BINARY)/config/genesis.json" docker run --rm -v $(CURDIR)/build\ethermint\Z ethermintd/node "ethermintd testnet --v 4 -o /ethermint --starting-ip-address 192.168.10.2 --keyring-backend=test" + docker-compose up -d +else + mkdir -p ./build/ + @$(MAKE) docker-localnet + + if ! [ -f build/node0/$(ETHERMINT_DAEMON_BINARY)/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/ethermint:Z ethermintd/node "ethermintd testnet --v 4 -o /ethermint --starting-ip-address 192.168.10.2 --keyring-backend=test"; fi + docker-compose up -d +endif + +localnet-stop: + docker-compose down + +# clean testnet +localnet-clean: + docker-compose down + sudo rm -rf build/* + + # reset testnet +localnet-unsafe-reset: + docker-compose down +ifeq ($(OS),Windows_NT) + @docker run --rm -v $(CURDIR)/build\ethermint\Z ethermintd/node "ethermintd unsafe-reset-all --home=/ethermint/node0/ethermintd" + @docker run --rm -v $(CURDIR)/build\ethermint\Z ethermintd/node "ethermintd unsafe-reset-all --home=/ethermint/node1/ethermintd" + @docker run --rm -v $(CURDIR)/build\ethermint\Z ethermintd/node "ethermintd unsafe-reset-all --home=/ethermint/node2/ethermintd" + @docker run --rm -v $(CURDIR)/build\ethermint\Z ethermintd/node "ethermintd unsafe-reset-all --home=/ethermint/node3/ethermintd" +else + @docker run --rm -v $(CURDIR)/build:/ethermint:Z ethermintd/node "ethermintd unsafe-reset-all --home=/ethermint/node0/ethermintd" + @docker run --rm -v $(CURDIR)/build:/ethermint:Z ethermintd/node "ethermintd unsafe-reset-all --home=/ethermint/node1/ethermintd" + @docker run --rm -v $(CURDIR)/build:/ethermint:Z ethermintd/node "ethermintd unsafe-reset-all --home=/ethermint/node2/ethermintd" + @docker run --rm -v $(CURDIR)/build:/ethermint:Z ethermintd/node "ethermintd unsafe-reset-all --home=/ethermint/node3/ethermintd" +endif -.PHONY: build install update-tools tools deps godocs clean format test-lint \ -test-cli test-race test-unit test test-import +.PHONY: build-docker-local-ethermint localnet-start localnet-stop diff --git a/README.md b/README.md index ebc269eaf5..c2a8da72eb 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,77 @@ -[![CircleCI](https://circleci.com/gh/cosmos/ethermint.svg?style=svg)](https://circleci.com/gh/cosmos/ethermint) -[![](https://godoc.org/github.com/cosmos/ethermint?status.svg)](http://godoc.org/github.com/cosmos/ethermint) [![Go Report Card](https://goreportcard.com/badge/github.com/cosmos/ethermint)](https://goreportcard.com/report/github.com/cosmos/ethermint) - -# Ethermint - -__**WARNING:**__ Ethermint is under VERY ACTIVE DEVELOPMENT and should be treated as pre-alpha software. This means it is not meant to be run in production, its APIs are subject to change without warning and should not be relied upon, and it should not be used to hold any value. We will remove this warning when we have a release that is stable, secure, and properly tested. - -## What is it? - -`ethermint` will be an implementation of the EVM that runs on top of [`tendermint`](https://github.com/tendermint/tendermint) consensus, a Proof of Stake system. This project has as its primary goals: - -- [Hard Spoon](https://blog.cosmos.network/introducing-the-hard-spoon-4a9288d3f0df) enablement: This is the ability to take a token from the Ethereum mainnet and "spoon" (shift) the balances over to another network. This feature is intended to make it easy for applications that require more transactions than the Ethereum main chain can provide to move their code over to a compatible chain with much more capacity. -- Web3 Compatibility: In order enable applications to be moved over to an ethermint chain existing tooling (i.e. web3 compatible clients) need to be able to interact with `ethermint`. - -### Implementation - -#### Completed - -- Have a working implementation that can parse and validate the existing ETH Chain and persist it in a Tendermint store -- Implement Ethereum transactions in the CosmosSDK - -#### Current Work - -- Implement web3 compatible API layer -- Implement the EVM as a CosmosSDK module -- Allow the Ethermint EVM to interact with other [Cosmos SDK modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/core/app3.md) - -#### Next Steps - -- Hard spoon enablement: The ability to export state from `geth` and import token balances into Ethermint -- Ethermint is a functioning Cosmos SDK application and can be deployed as its own zone -- Full web3 compatibility will enable existing Ethereum applications to use Ethermint - -### Building Ethermint - -To build, execute the following commands: - -```bash -# To build the binary and put the resulting binary in ./build -$ make tools deps build - -# To build the project and install it in $GOBIN -$ make tools deps install -``` - -### Tests - -Integration tests are invoked via: + + +
+

Ethermint

+
+ +
+ + Version + + + License: Apache-2.0 + + + GoDoc + + + Go report card + + + Code Coverage + +
+
+ + Lines Of Code + + + Discord + + + Build Status + + + Lint Status + +
+ +Ethermint is a scalable, high-throughput Proof-of-Stake blockchain that is fully compatible and +interoperable with Ethereum. It's build using the the [Cosmos SDK](https://github.com/cosmos/cosmos-sdk/) which runs on top of [Tendermint Core](https://github.com/tendermint/tendermint) consensus engine. + +> **WARNING:** Ethermint is under VERY ACTIVE DEVELOPMENT and should be treated as pre-alpha software. This means it is not meant to be run in production, its APIs are subject to change without warning and should not be relied upon, and it should not be used to hold any value. We will remove this warning when we have a release that is stable, secure, and properly tested. + +**Note**: Requires [Go 1.14+](https://golang.org/dl/) + +## Quick Start + +To learn how the Ethermint works from a high-level perspective, go to the [Introduction](./docs/intro/overview.md) section from the documentation. + +For more, please refer to the [Ethermint Docs](./docs/), which are also hosted on [docs.ethermint.zone](https://docs.ethermint.zone/). + +## Tests + +Unit tests are invoked via: ```bash -$ make test +make test ``` -To run CLI tests, execute: +To run JSON-RPC tests, execute: ```bash -$ make test-cli +make test-rpc ``` -#### Ethereum Mainnet Import - -There is an included Ethereum mainnet exported blockchain file in `importer/blockchain` +There is also an included Ethereum mainnet exported blockchain file in `importer/blockchain` that includes blocks up to height `97638`. To execute and test a full import of these blocks using the EVM module, execute: ```bash -$ make test-import +make test-import ``` You may also provide a custom blockchain export file to test importing more blocks @@ -74,5 +81,5 @@ via the `--blockchain` flag. See `TestImportBlocks` for further documentation. The following chat channels and forums are a great spot to ask questions about Ethermint: -- [Cosmos Riot Chat Channel](https://riot.im/app/#/group/+cosmos:matrix.org) -- Cosmos Forum [![Discourse status](https://img.shields.io/discourse/https/forum.cosmos.network/status.svg)](https://forum.cosmos.network) +- [Cosmos Discord](https://discord.gg/W8trcGV) +- [Cosmos Forum](https://forum.cosmos.network) diff --git a/app/ante.go b/app/ante.go deleted file mode 100644 index 9ba8344ba7..0000000000 --- a/app/ante.go +++ /dev/null @@ -1,297 +0,0 @@ -package app - -import ( - "fmt" - "math/big" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - - "github.com/cosmos/ethermint/crypto" - "github.com/cosmos/ethermint/types" - evmtypes "github.com/cosmos/ethermint/x/evm/types" - - ethcmn "github.com/ethereum/go-ethereum/common" - ethcore "github.com/ethereum/go-ethereum/core" - - tmcrypto "github.com/tendermint/tendermint/crypto" -) - -const ( - memoCostPerByte sdk.Gas = 3 - secp256k1VerifyCost = 21000 -) - -// NewAnteHandler returns an ante handler responsible for attempting to route an -// Ethereum or SDK transaction to an internal ante handler for performing -// transaction-level processing (e.g. fee payment, signature verification) before -// being passed onto it's respective handler. -// -// NOTE: The EVM will already consume (intrinsic) gas for signature verification -// and covering input size as well as handling nonce incrementing. -func NewAnteHandler(ak auth.AccountKeeper, fck auth.FeeCollectionKeeper) sdk.AnteHandler { - return func( - ctx sdk.Context, tx sdk.Tx, sim bool, - ) (newCtx sdk.Context, res sdk.Result, abort bool) { - - switch castTx := tx.(type) { - case auth.StdTx: - return sdkAnteHandler(ctx, ak, fck, castTx, sim) - - case *evmtypes.EthereumTxMsg: - return ethAnteHandler(ctx, castTx, ak) - - default: - return ctx, sdk.ErrInternal(fmt.Sprintf("transaction type invalid: %T", tx)).Result(), true - } - } -} - -// ---------------------------------------------------------------------------- -// SDK Ante Handler - -func sdkAnteHandler( - ctx sdk.Context, ak auth.AccountKeeper, fck auth.FeeCollectionKeeper, stdTx auth.StdTx, sim bool, -) (newCtx sdk.Context, res sdk.Result, abort bool) { - - // Ensure that the provided fees meet a minimum threshold for the validator, - // if this is a CheckTx. This is only for local mempool purposes, and thus - // is only ran on check tx. - if ctx.IsCheckTx() && !sim { - res := auth.EnsureSufficientMempoolFees(ctx, stdTx) - if !res.IsOK() { - return newCtx, res, true - } - } - - newCtx = auth.SetGasMeter(sim, ctx, stdTx) - - // AnteHandlers must have their own defer/recover in order for the BaseApp - // to know how much gas was used! This is because the GasMeter is created in - // the AnteHandler, but if it panics the context won't be set properly in - // runTx's recover call. - defer func() { - if r := recover(); r != nil { - switch rType := r.(type) { - case sdk.ErrorOutOfGas: - log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor) - res = sdk.ErrOutOfGas(log).Result() - res.GasWanted = stdTx.Fee.Gas - res.GasUsed = newCtx.GasMeter().GasConsumed() - abort = true - default: - panic(r) - } - } - }() - - if err := stdTx.ValidateBasic(); err != nil { - return newCtx, err.Result(), true - } - - newCtx.GasMeter().ConsumeGas(memoCostPerByte*sdk.Gas(len(stdTx.GetMemo())), "memo") - - signerAccs, res := auth.GetSignerAccs(newCtx, ak, stdTx.GetSigners()) - if !res.IsOK() { - return newCtx, res, true - } - - // the first signer pays the transaction fees - if !stdTx.Fee.Amount.IsZero() { - signerAccs[0], res = auth.DeductFees(signerAccs[0], stdTx.Fee) - if !res.IsOK() { - return newCtx, res, true - } - - fck.AddCollectedFees(newCtx, stdTx.Fee.Amount) - } - - isGenesis := ctx.BlockHeight() == 0 - signBytesList := auth.GetSignBytesList(newCtx.ChainID(), stdTx, signerAccs, isGenesis) - stdSigs := stdTx.GetSignatures() - - for i := 0; i < len(stdSigs); i++ { - // check signature, return account with incremented nonce - signerAccs[i], res = processSig(newCtx, signerAccs[i], stdSigs[i], signBytesList[i], sim) - if !res.IsOK() { - return newCtx, res, true - } - - ak.SetAccount(newCtx, signerAccs[i]) - } - - return newCtx, sdk.Result{GasWanted: stdTx.Fee.Gas}, false -} - -// processSig verifies the signature and increments the nonce. If the account -// doesn't have a pubkey, set it. -func processSig( - ctx sdk.Context, acc auth.Account, sig auth.StdSignature, signBytes []byte, sim bool, -) (updatedAcc auth.Account, res sdk.Result) { - - pubKey, res := auth.ProcessPubKey(acc, sig, sim) - if !res.IsOK() { - return nil, res - } - - err := acc.SetPubKey(pubKey) - if err != nil { - return nil, sdk.ErrInternal("failed to set PubKey on signer account").Result() - } - - consumeSigGas(ctx.GasMeter(), pubKey) - if !sim && !pubKey.VerifyBytes(signBytes, sig.Signature) { - return nil, sdk.ErrUnauthorized("signature verification failed").Result() - } - - err = acc.SetSequence(acc.GetSequence() + 1) - if err != nil { - return nil, sdk.ErrInternal("failed to set account nonce").Result() - } - - return acc, res -} - -func consumeSigGas(meter sdk.GasMeter, pubkey tmcrypto.PubKey) { - switch pubkey.(type) { - case crypto.PubKeySecp256k1: - meter.ConsumeGas(secp256k1VerifyCost, "ante verify: secp256k1") - default: - panic("Unrecognized signature type") - } -} - -// ---------------------------------------------------------------------------- -// Ethereum Ante Handler - -// ethAnteHandler defines an internal ante handler for an Ethereum transaction -// ethTxMsg. During CheckTx, the transaction is passed through a series of -// pre-message execution validation checks such as signature and account -// verification in addition to minimum fees being checked. Otherwise, during -// DeliverTx, the transaction is simply passed to the EVM which will also -// perform the same series of checks. The distinction is made in CheckTx to -// prevent spam and DoS attacks. -func ethAnteHandler( - ctx sdk.Context, ethTxMsg *evmtypes.EthereumTxMsg, ak auth.AccountKeeper, -) (newCtx sdk.Context, res sdk.Result, abort bool) { - - if ctx.IsCheckTx() { - // Only perform pre-message (Ethereum transaction) execution validation - // during CheckTx. Otherwise, during DeliverTx the EVM will handle them. - if res := validateEthTxCheckTx(ctx, ak, ethTxMsg); !res.IsOK() { - return newCtx, res, true - } - } - - return ctx, sdk.Result{}, false -} - -func validateEthTxCheckTx( - ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.EthereumTxMsg, -) sdk.Result { - - // parse the chainID from a string to a base-10 integer - chainID, ok := new(big.Int).SetString(ctx.ChainID(), 10) - if !ok { - return types.ErrInvalidChainID(fmt.Sprintf("invalid chainID: %s", ctx.ChainID())).Result() - } - - // Validate sufficient fees have been provided that meet a minimum threshold - // defined by the proposer (for mempool purposes during CheckTx). - if res := ensureSufficientMempoolFees(ctx, ethTxMsg); !res.IsOK() { - return res - } - - // validate enough intrinsic gas - if res := validateIntrinsicGas(ethTxMsg); !res.IsOK() { - return res - } - - // validate sender/signature - signer, err := ethTxMsg.VerifySig(chainID) - if err != nil { - return sdk.ErrUnauthorized("signature verification failed").Result() - } - - // validate account (nonce and balance checks) - if res := validateAccount(ctx, ak, ethTxMsg, signer); !res.IsOK() { - return res - } - - return sdk.Result{} -} - -// validateIntrinsicGas validates that the Ethereum tx message has enough to -// cover intrinsic gas. Intrinsic gas for a transaction is the amount of gas -// that the transaction uses before the transaction is executed. The gas is a -// constant value of 21000 plus any cost inccured by additional bytes of data -// supplied with the transaction. -func validateIntrinsicGas(ethTxMsg *evmtypes.EthereumTxMsg) sdk.Result { - gas, err := ethcore.IntrinsicGas(ethTxMsg.Data.Payload, ethTxMsg.To() == nil, false) - if err != nil { - return sdk.ErrInternal(fmt.Sprintf("failed to compute intrinsic gas cost: %s", err)).Result() - } - - if ethTxMsg.Data.GasLimit < gas { - return sdk.ErrInternal( - fmt.Sprintf("intrinsic gas too low; %d < %d", ethTxMsg.Data.GasLimit, gas), - ).Result() - } - - return sdk.Result{} -} - -// validateAccount validates the account nonce and that the account has enough -// funds to cover the tx cost. -func validateAccount( - ctx sdk.Context, ak auth.AccountKeeper, ethTxMsg *evmtypes.EthereumTxMsg, signer ethcmn.Address, -) sdk.Result { - - acc := ak.GetAccount(ctx, sdk.AccAddress(signer.Bytes())) - - // on InitChain make sure account number == 0 - if ctx.BlockHeight() == 0 && acc.GetAccountNumber() != 0 { - return sdk.ErrInternal( - fmt.Sprintf( - "invalid account number for height zero; got %d, expected 0", acc.GetAccountNumber(), - )).Result() - } - - // Validate the transaction nonce is valid (equivalent to the sender account’s - // current nonce). - seq := acc.GetSequence() - if ethTxMsg.Data.AccountNonce != seq { - return sdk.ErrInvalidSequence( - fmt.Sprintf("nonce too low; got %d, expected %d", ethTxMsg.Data.AccountNonce, seq)).Result() - } - - // validate sender has enough funds - balance := acc.GetCoins().AmountOf(types.DenomDefault) - if balance.BigInt().Cmp(ethTxMsg.Cost()) < 0 { - return sdk.ErrInsufficientFunds( - fmt.Sprintf("insufficient funds: %s < %s", balance, ethTxMsg.Cost()), - ).Result() - } - - return sdk.Result{} -} - -// ensureSufficientMempoolFees verifies that enough fees have been provided by the -// Ethereum transaction that meet the minimum threshold set by the block -// proposer. -// -// NOTE: This should only be ran during a CheckTx mode. -func ensureSufficientMempoolFees(ctx sdk.Context, ethTxMsg *evmtypes.EthereumTxMsg) sdk.Result { - // fee = GP * GL - fee := sdk.Coins{sdk.NewInt64Coin(types.DenomDefault, ethTxMsg.Fee().Int64())} - - // it is assumed that the minimum fees will only include the single valid denom - if !ctx.MinimumFees().IsZero() && !fee.IsAllGTE(ctx.MinimumFees()) { - // reject the transaction that does not meet the minimum fee - return sdk.ErrInsufficientFee( - fmt.Sprintf("insufficient fee, got: %q required: %q", fee, ctx.MinimumFees()), - ).Result() - } - - return sdk.Result{} -} diff --git a/app/ante/ante.go b/app/ante/ante.go new file mode 100644 index 0000000000..340f5b61ad --- /dev/null +++ b/app/ante/ante.go @@ -0,0 +1,128 @@ +package ante + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth" + authante "github.com/cosmos/cosmos-sdk/x/auth/ante" + "github.com/cosmos/cosmos-sdk/x/auth/keeper" + "github.com/cosmos/cosmos-sdk/x/auth/types" + + "github.com/cosmos/ethermint/crypto" + evmtypes "github.com/cosmos/ethermint/x/evm/types" + + tmcrypto "github.com/tendermint/tendermint/crypto" +) + +func init() { + crypto.RegisterCodec(types.ModuleCdc) +} + +const ( + // TODO: Use this cost per byte through parameter or overriding NewConsumeGasForTxSizeDecorator + // which currently defaults at 10, if intended + // memoCostPerByte sdk.Gas = 3 + secp256k1VerifyCost uint64 = 21000 +) + +// NewAnteHandler returns an ante handler responsible for attempting to route an +// Ethereum or SDK transaction to an internal ante handler for performing +// transaction-level processing (e.g. fee payment, signature verification) before +// being passed onto it's respective handler. +func NewAnteHandler(ak auth.AccountKeeper, evmKeeper EVMKeeper, sk types.SupplyKeeper) sdk.AnteHandler { + return func( + ctx sdk.Context, tx sdk.Tx, sim bool, + ) (newCtx sdk.Context, err error) { + var anteHandler sdk.AnteHandler + switch tx.(type) { + case auth.StdTx: + anteHandler = sdk.ChainAnteDecorators( + authante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first + NewAccountSetupDecorator(ak), + authante.NewMempoolFeeDecorator(), + authante.NewValidateBasicDecorator(), + authante.NewValidateMemoDecorator(ak), + authante.NewConsumeGasForTxSizeDecorator(ak), + authante.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators + authante.NewValidateSigCountDecorator(ak), + authante.NewDeductFeeDecorator(ak, sk), + authante.NewSigGasConsumeDecorator(ak, sigGasConsumer), + authante.NewSigVerificationDecorator(ak), + authante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator + ) + + case evmtypes.MsgEthereumTx: + anteHandler = sdk.ChainAnteDecorators( + NewEthSetupContextDecorator(), // outermost AnteDecorator. EthSetUpContext must be called first + NewEthMempoolFeeDecorator(evmKeeper), + authante.NewValidateBasicDecorator(), + NewEthSigVerificationDecorator(), + NewAccountVerificationDecorator(ak, evmKeeper), + NewNonceVerificationDecorator(ak), + NewEthGasConsumeDecorator(ak, sk, evmKeeper), + NewIncrementSenderSequenceDecorator(ak), // innermost AnteDecorator. + ) + default: + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx) + } + + return anteHandler(ctx, tx, sim) + } +} + +// sigGasConsumer overrides the DefaultSigVerificationGasConsumer from the x/auth +// module on the SDK. It doesn't allow ed25519 nor multisig thresholds. +func sigGasConsumer( + meter sdk.GasMeter, _ []byte, pubkey tmcrypto.PubKey, _ types.Params, +) error { + switch pubkey.(type) { + case crypto.PubKeySecp256k1: + meter.ConsumeGas(secp256k1VerifyCost, "ante verify: secp256k1") + return nil + case tmcrypto.PubKey: + meter.ConsumeGas(secp256k1VerifyCost, "ante verify: tendermint secp256k1") + return nil + default: + return sdkerrors.Wrapf(sdkerrors.ErrInvalidPubKey, "unrecognized public key type: %T", pubkey) + } +} + +// AccountSetupDecorator sets an account to state if it's not stored already. This only applies for MsgEthermint. +type AccountSetupDecorator struct { + ak auth.AccountKeeper +} + +// NewAccountSetupDecorator creates a new AccountSetupDecorator instance +func NewAccountSetupDecorator(ak auth.AccountKeeper) AccountSetupDecorator { + return AccountSetupDecorator{ + ak: ak, + } +} + +// AnteHandle sets an account for MsgEthermint (evm) if the sender is registered. +// NOTE: Since the account is set without any funds, the message execution will +// fail if the validator requires a minimum fee > 0. +func (asd AccountSetupDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { + msgs := tx.GetMsgs() + if len(msgs) == 0 { + return ctx, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "no messages included in transaction") + } + + for _, msg := range msgs { + if msgEthermint, ok := msg.(evmtypes.MsgEthermint); ok { + setupAccount(asd.ak, ctx, msgEthermint.From) + } + } + + return next(ctx, tx, simulate) +} + +func setupAccount(ak keeper.AccountKeeper, ctx sdk.Context, addr sdk.AccAddress) { + acc := ak.GetAccount(ctx, addr) + if acc != nil { + return + } + + acc = ak.NewAccountWithAddress(ctx, addr) + ak.SetAccount(ctx, acc) +} diff --git a/app/ante/ante_test.go b/app/ante/ante_test.go new file mode 100644 index 0000000000..44d74d68c6 --- /dev/null +++ b/app/ante/ante_test.go @@ -0,0 +1,300 @@ +package ante_test + +import ( + "math/big" + "testing" + "time" + + "github.com/stretchr/testify/require" + + ethcmn "github.com/ethereum/go-ethereum/common" + + abci "github.com/tendermint/tendermint/abci/types" + tmcrypto "github.com/tendermint/tendermint/crypto" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ethermint/app" + "github.com/cosmos/ethermint/app/ante" + "github.com/cosmos/ethermint/types" + evmtypes "github.com/cosmos/ethermint/x/evm/types" +) + +func requireValidTx( + t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, sim bool, +) { + _, err := anteHandler(ctx, tx, sim) + require.NoError(t, err) +} + +func requireInvalidTx( + t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, + tx sdk.Tx, sim bool, +) { + _, err := anteHandler(ctx, tx, sim) + require.Error(t, err) +} + +func (suite *AnteTestSuite) TestValidEthTx() { + suite.ctx = suite.ctx.WithBlockHeight(1) + + addr1, priv1 := newTestAddrKey() + addr2, _ := newTestAddrKey() + + acc1 := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1) + _ = acc1.SetCoins(newTestCoins()) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc1) + + acc2 := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr2) + _ = acc2.SetCoins(newTestCoins()) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc2) + + // require a valid Ethereum tx to pass + to := ethcmn.BytesToAddress(addr2.Bytes()) + amt := big.NewInt(32) + gas := big.NewInt(20) + ethMsg := evmtypes.NewMsgEthereumTx(0, &to, amt, 22000, gas, []byte("test")) + + tx, err := newTestEthTx(suite.ctx, ethMsg, priv1) + suite.Require().NoError(err) + requireValidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false) +} + +func (suite *AnteTestSuite) TestValidTx() { + suite.ctx = suite.ctx.WithBlockHeight(1) + + addr1, priv1 := newTestAddrKey() + addr2, priv2 := newTestAddrKey() + + acc1 := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1) + _ = acc1.SetCoins(newTestCoins()) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc1) + + acc2 := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr2) + _ = acc2.SetCoins(newTestCoins()) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc2) + + // require a valid SDK tx to pass + fee := newTestStdFee() + msg1 := newTestMsg(addr1, addr2) + msgs := []sdk.Msg{msg1} + + privKeys := []tmcrypto.PrivKey{priv1, priv2} + accNums := []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber()} + accSeqs := []uint64{acc1.GetSequence(), acc2.GetSequence()} + + tx := newTestSDKTx(suite.ctx, msgs, privKeys, accNums, accSeqs, fee) + + requireValidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false) +} + +func (suite *AnteTestSuite) TestSDKInvalidSigs() { + suite.ctx = suite.ctx.WithBlockHeight(1) + + addr1, priv1 := newTestAddrKey() + addr2, priv2 := newTestAddrKey() + addr3, priv3 := newTestAddrKey() + + acc1 := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1) + _ = acc1.SetCoins(newTestCoins()) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc1) + + acc2 := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr2) + _ = acc2.SetCoins(newTestCoins()) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc2) + + fee := newTestStdFee() + msg1 := newTestMsg(addr1, addr2) + + // require validation failure with no signers + msgs := []sdk.Msg{msg1} + + privKeys := []tmcrypto.PrivKey{} + accNums := []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber()} + accSeqs := []uint64{acc1.GetSequence(), acc2.GetSequence()} + + tx := newTestSDKTx(suite.ctx, msgs, privKeys, accNums, accSeqs, fee) + requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false) + + // require validation failure with invalid number of signers + msgs = []sdk.Msg{msg1} + + privKeys = []tmcrypto.PrivKey{priv1} + accNums = []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber()} + accSeqs = []uint64{acc1.GetSequence(), acc2.GetSequence()} + + tx = newTestSDKTx(suite.ctx, msgs, privKeys, accNums, accSeqs, fee) + requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false) + + // require validation failure with an invalid signer + msg2 := newTestMsg(addr1, addr3) + msgs = []sdk.Msg{msg1, msg2} + + privKeys = []tmcrypto.PrivKey{priv1, priv2, priv3} + accNums = []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber(), 0} + accSeqs = []uint64{acc1.GetSequence(), acc2.GetSequence(), 0} + + tx = newTestSDKTx(suite.ctx, msgs, privKeys, accNums, accSeqs, fee) + requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false) +} + +func (suite *AnteTestSuite) TestSDKInvalidAcc() { + suite.ctx = suite.ctx.WithBlockHeight(1) + + addr1, priv1 := newTestAddrKey() + + acc1 := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1) + _ = acc1.SetCoins(newTestCoins()) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc1) + + fee := newTestStdFee() + msg1 := newTestMsg(addr1) + msgs := []sdk.Msg{msg1} + privKeys := []tmcrypto.PrivKey{priv1} + + // require validation failure with invalid account number + accNums := []uint64{1} + accSeqs := []uint64{acc1.GetSequence()} + + tx := newTestSDKTx(suite.ctx, msgs, privKeys, accNums, accSeqs, fee) + requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false) + + // require validation failure with invalid sequence (nonce) + accNums = []uint64{acc1.GetAccountNumber()} + accSeqs = []uint64{1} + + tx = newTestSDKTx(suite.ctx, msgs, privKeys, accNums, accSeqs, fee) + requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false) +} + +func (suite *AnteTestSuite) TestEthInvalidSig() { + suite.ctx = suite.ctx.WithBlockHeight(1) + + _, priv1 := newTestAddrKey() + addr2, _ := newTestAddrKey() + to := ethcmn.BytesToAddress(addr2.Bytes()) + amt := big.NewInt(32) + gas := big.NewInt(20) + ethMsg := evmtypes.NewMsgEthereumTx(0, &to, amt, 22000, gas, []byte("test")) + + tx, err := newTestEthTx(suite.ctx, ethMsg, priv1) + suite.Require().NoError(err) + + ctx := suite.ctx.WithChainID("ethermint-4") + requireInvalidTx(suite.T(), suite.anteHandler, ctx, tx, false) +} + +func (suite *AnteTestSuite) TestEthInvalidNonce() { + + suite.ctx = suite.ctx.WithBlockHeight(1) + + addr1, priv1 := newTestAddrKey() + addr2, _ := newTestAddrKey() + + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1) + err := acc.SetSequence(10) + suite.Require().NoError(err) + _ = acc.SetCoins(newTestCoins()) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + + // require a valid Ethereum tx to pass + to := ethcmn.BytesToAddress(addr2.Bytes()) + amt := big.NewInt(32) + gas := big.NewInt(20) + ethMsg := evmtypes.NewMsgEthereumTx(0, &to, amt, 22000, gas, []byte("test")) + + tx, err := newTestEthTx(suite.ctx, ethMsg, priv1) + suite.Require().NoError(err) + requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false) +} + +func (suite *AnteTestSuite) TestEthInsufficientBalance() { + suite.ctx = suite.ctx.WithBlockHeight(1) + + addr1, priv1 := newTestAddrKey() + addr2, _ := newTestAddrKey() + + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + + // require a valid Ethereum tx to pass + to := ethcmn.BytesToAddress(addr2.Bytes()) + amt := big.NewInt(32) + gas := big.NewInt(20) + ethMsg := evmtypes.NewMsgEthereumTx(0, &to, amt, 22000, gas, []byte("test")) + + tx, err := newTestEthTx(suite.ctx, ethMsg, priv1) + suite.Require().NoError(err) + requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false) +} + +func (suite *AnteTestSuite) TestEthInvalidIntrinsicGas() { + suite.ctx = suite.ctx.WithBlockHeight(1) + + addr1, priv1 := newTestAddrKey() + addr2, _ := newTestAddrKey() + + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1) + _ = acc.SetCoins(newTestCoins()) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + + // require a valid Ethereum tx to pass + to := ethcmn.BytesToAddress(addr2.Bytes()) + amt := big.NewInt(32) + gas := big.NewInt(20) + gasLimit := uint64(1000) + ethMsg := evmtypes.NewMsgEthereumTx(0, &to, amt, gasLimit, gas, []byte("test")) + + tx, err := newTestEthTx(suite.ctx, ethMsg, priv1) + suite.Require().NoError(err) + requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx.WithIsCheckTx(true), tx, false) +} + +func (suite *AnteTestSuite) TestEthInvalidMempoolFees() { + // setup app with checkTx = true + suite.app = app.Setup(true) + suite.ctx = suite.app.BaseApp.NewContext(true, abci.Header{Height: 1, ChainID: "ethermint-3", Time: time.Now().UTC()}) + suite.app.EvmKeeper.SetParams(suite.ctx, evmtypes.DefaultParams()) + + suite.anteHandler = ante.NewAnteHandler(suite.app.AccountKeeper, suite.app.EvmKeeper, suite.app.SupplyKeeper) + suite.ctx = suite.ctx.WithMinGasPrices(sdk.NewDecCoins(types.NewPhotonDecCoin(sdk.NewInt(500000)))) + addr1, priv1 := newTestAddrKey() + addr2, _ := newTestAddrKey() + + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1) + _ = acc.SetCoins(newTestCoins()) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + + // require a valid Ethereum tx to pass + to := ethcmn.BytesToAddress(addr2.Bytes()) + amt := big.NewInt(32) + gas := big.NewInt(20) + ethMsg := evmtypes.NewMsgEthereumTx(0, &to, amt, 22000, gas, []byte("payload")) + + tx, err := newTestEthTx(suite.ctx, ethMsg, priv1) + suite.Require().NoError(err) + requireInvalidTx(suite.T(), suite.anteHandler, suite.ctx, tx, false) +} + +func (suite *AnteTestSuite) TestEthInvalidChainID() { + suite.ctx = suite.ctx.WithBlockHeight(1) + + addr1, priv1 := newTestAddrKey() + addr2, _ := newTestAddrKey() + + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr1) + _ = acc.SetCoins(newTestCoins()) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + + // require a valid Ethereum tx to pass + to := ethcmn.BytesToAddress(addr2.Bytes()) + amt := big.NewInt(32) + gas := big.NewInt(20) + ethMsg := evmtypes.NewMsgEthereumTx(0, &to, amt, 22000, gas, []byte("test")) + + tx, err := newTestEthTx(suite.ctx, ethMsg, priv1) + suite.Require().NoError(err) + + ctx := suite.ctx.WithChainID("bad-chain-id") + requireInvalidTx(suite.T(), suite.anteHandler, ctx, tx, false) +} diff --git a/app/ante/doc.go b/app/ante/doc.go new file mode 100644 index 0000000000..73b56f74aa --- /dev/null +++ b/app/ante/doc.go @@ -0,0 +1,11 @@ +/*Package ante defines the SDK auth module's AnteHandler as well as an internal +AnteHandler for an Ethereum transaction (i.e MsgEthereumTx). + +During CheckTx, the transaction is passed through a series of +pre-message execution validation checks such as signature and account +verification in addition to minimum fees being checked. Otherwise, during +DeliverTx, the transaction is simply passed to the EVM which will also +perform the same series of checks. The distinction is made in CheckTx to +prevent spam and DoS attacks. +*/ +package ante diff --git a/app/ante/eth.go b/app/ante/eth.go new file mode 100644 index 0000000000..bad8243fa6 --- /dev/null +++ b/app/ante/eth.go @@ -0,0 +1,392 @@ +package ante + +import ( + "fmt" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth" + authante "github.com/cosmos/cosmos-sdk/x/auth/ante" + "github.com/cosmos/cosmos-sdk/x/auth/types" + + ethermint "github.com/cosmos/ethermint/types" + evmtypes "github.com/cosmos/ethermint/x/evm/types" + + "github.com/ethereum/go-ethereum/common" + ethcore "github.com/ethereum/go-ethereum/core" +) + +// EVMKeeper defines the expected keeper interface used on the Eth AnteHandler +type EVMKeeper interface { + GetParams(ctx sdk.Context) evmtypes.Params +} + +// EthSetupContextDecorator sets the infinite GasMeter in the Context and wraps +// the next AnteHandler with a defer clause to recover from any downstream +// OutOfGas panics in the AnteHandler chain to return an error with information +// on gas provided and gas used. +// CONTRACT: Must be first decorator in the chain +// CONTRACT: Tx must implement GasTx interface +type EthSetupContextDecorator struct{} + +// NewEthSetupContextDecorator creates a new EthSetupContextDecorator +func NewEthSetupContextDecorator() EthSetupContextDecorator { + return EthSetupContextDecorator{} +} + +// AnteHandle sets the infinite gas meter to done to ignore costs in AnteHandler checks. +// This is undone at the EthGasConsumeDecorator, where the context is set with the +// ethereum tx GasLimit. +func (escd EthSetupContextDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + ctx = ctx.WithBlockGasMeter(sdk.NewInfiniteGasMeter()) + + // all transactions must implement GasTx + gasTx, ok := tx.(authante.GasTx) + if !ok { + return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be GasTx") + } + + // Decorator will catch an OutOfGasPanic caused in the next antehandler + // AnteHandlers must have their own defer/recover in order for the BaseApp + // to know how much gas was used! This is because the GasMeter is created in + // the AnteHandler, but if it panics the context won't be set properly in + // runTx's recover call. + defer func() { + if r := recover(); r != nil { + switch rType := r.(type) { + case sdk.ErrorOutOfGas: + log := fmt.Sprintf( + "out of gas in location: %v; gasLimit: %d, gasUsed: %d", + rType.Descriptor, gasTx.GetGas(), ctx.GasMeter().GasConsumed(), + ) + err = sdkerrors.Wrap(sdkerrors.ErrOutOfGas, log) + default: + panic(r) + } + } + }() + + return next(ctx, tx, simulate) +} + +// EthMempoolFeeDecorator validates that sufficient fees have been provided that +// meet a minimum threshold defined by the proposer (for mempool purposes during CheckTx). +type EthMempoolFeeDecorator struct { + evmKeeper EVMKeeper +} + +// NewEthMempoolFeeDecorator creates a new EthMempoolFeeDecorator +func NewEthMempoolFeeDecorator(ek EVMKeeper) EthMempoolFeeDecorator { + return EthMempoolFeeDecorator{ + evmKeeper: ek, + } +} + +// AnteHandle verifies that enough fees have been provided by the +// Ethereum transaction that meet the minimum threshold set by the block +// proposer. +// +// NOTE: This should only be run during a CheckTx mode. +func (emfd EthMempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + if !ctx.IsCheckTx() { + return next(ctx, tx, simulate) + } + + msgEthTx, ok := tx.(evmtypes.MsgEthereumTx) + if !ok { + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx) + } + + evmDenom := emfd.evmKeeper.GetParams(ctx).EvmDenom + + // fee = GP * GL + fee := sdk.NewInt64DecCoin(evmDenom, msgEthTx.Fee().Int64()) + + minGasPrices := ctx.MinGasPrices() + + // check that fee provided is greater than the minimum + // NOTE: we only check if aphotons are present in min gas prices. It is up to the + // sender if they want to send additional fees in other denominations. + var hasEnoughFees bool + if fee.Amount.GTE(minGasPrices.AmountOf(evmDenom)) { + hasEnoughFees = true + } + + // reject transaction if minimum gas price is positive and the transaction does not + // meet the minimum fee + if !ctx.MinGasPrices().IsZero() && !hasEnoughFees { + return ctx, sdkerrors.Wrap( + sdkerrors.ErrInsufficientFee, + fmt.Sprintf("insufficient fee, got: %q required: %q", fee, ctx.MinGasPrices()), + ) + } + + return next(ctx, tx, simulate) +} + +// EthSigVerificationDecorator validates an ethereum signature +type EthSigVerificationDecorator struct{} + +// NewEthSigVerificationDecorator creates a new EthSigVerificationDecorator +func NewEthSigVerificationDecorator() EthSigVerificationDecorator { + return EthSigVerificationDecorator{} +} + +// AnteHandle validates the signature and returns sender address +func (esvd EthSigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + msgEthTx, ok := tx.(evmtypes.MsgEthereumTx) + if !ok { + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx) + } + + // parse the chainID from a string to a base-10 integer + chainIDEpoch, err := ethermint.ParseChainID(ctx.ChainID()) + if err != nil { + return ctx, err + } + + // validate sender/signature and cache the address + _, err = msgEthTx.VerifySig(chainIDEpoch) + if err != nil { + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "signature verification failed: %s", err.Error()) + } + + // NOTE: when signature verification succeeds, a non-empty signer address can be + // retrieved from the transaction on the next AnteDecorators. + + return next(ctx, msgEthTx, simulate) +} + +// AccountVerificationDecorator validates an account balance checks +type AccountVerificationDecorator struct { + ak auth.AccountKeeper + evmKeeper EVMKeeper +} + +// NewAccountVerificationDecorator creates a new AccountVerificationDecorator +func NewAccountVerificationDecorator(ak auth.AccountKeeper, ek EVMKeeper) AccountVerificationDecorator { + return AccountVerificationDecorator{ + ak: ak, + evmKeeper: ek, + } +} + +// AnteHandle validates the signature and returns sender address +func (avd AccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + if !ctx.IsCheckTx() { + return next(ctx, tx, simulate) + } + + msgEthTx, ok := tx.(evmtypes.MsgEthereumTx) + if !ok { + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx) + } + + // sender address should be in the tx cache from the previous AnteHandle call + address := msgEthTx.From() + if address.Empty() { + panic("sender address cannot be empty") + } + + acc := avd.ak.GetAccount(ctx, address) + if acc == nil { + acc = avd.ak.NewAccountWithAddress(ctx, address) + avd.ak.SetAccount(ctx, acc) + } + + // on InitChain make sure account number == 0 + if ctx.BlockHeight() == 0 && acc.GetAccountNumber() != 0 { + return ctx, sdkerrors.Wrapf( + sdkerrors.ErrInvalidSequence, + "invalid account number for height zero (got %d)", acc.GetAccountNumber(), + ) + } + + evmDenom := avd.evmKeeper.GetParams(ctx).EvmDenom + + // validate sender has enough funds to pay for gas cost + balance := acc.GetCoins().AmountOf(evmDenom) + if balance.BigInt().Cmp(msgEthTx.Cost()) < 0 { + return ctx, sdkerrors.Wrapf( + sdkerrors.ErrInsufficientFunds, + "sender balance < tx gas cost (%s%s < %s%s)", balance.String(), evmDenom, msgEthTx.Cost().String(), evmDenom, + ) + } + + return next(ctx, tx, simulate) +} + +// NonceVerificationDecorator checks that the account nonce from the transaction matches +// the sender account sequence. +type NonceVerificationDecorator struct { + ak auth.AccountKeeper +} + +// NewNonceVerificationDecorator creates a new NonceVerificationDecorator +func NewNonceVerificationDecorator(ak auth.AccountKeeper) NonceVerificationDecorator { + return NonceVerificationDecorator{ + ak: ak, + } +} + +// AnteHandle validates that the transaction nonce is valid (equivalent to the sender account’s +// current nonce). +func (nvd NonceVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + msgEthTx, ok := tx.(evmtypes.MsgEthereumTx) + if !ok { + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx) + } + + // sender address should be in the tx cache from the previous AnteHandle call + address := msgEthTx.From() + if address.Empty() { + panic("sender address cannot be empty") + } + + acc := nvd.ak.GetAccount(ctx, address) + if acc == nil { + return ctx, sdkerrors.Wrapf( + sdkerrors.ErrUnknownAddress, + "account %s (%s) is nil", common.BytesToAddress(address.Bytes()), address, + ) + } + + seq := acc.GetSequence() + // if multiple transactions are submitted in succession with increasing nonces, + // all will be rejected except the first, since the first needs to be included in a block + // before the sequence increments + if msgEthTx.Data.AccountNonce < seq { + return ctx, sdkerrors.Wrapf( + sdkerrors.ErrInvalidSequence, + "invalid nonce; got %d, expected %d", msgEthTx.Data.AccountNonce, seq, + ) + } + + return next(ctx, tx, simulate) +} + +// EthGasConsumeDecorator validates enough intrinsic gas for the transaction and +// gas consumption. +type EthGasConsumeDecorator struct { + ak auth.AccountKeeper + sk types.SupplyKeeper + evmKeeper EVMKeeper +} + +// NewEthGasConsumeDecorator creates a new EthGasConsumeDecorator +func NewEthGasConsumeDecorator(ak auth.AccountKeeper, sk types.SupplyKeeper, ek EVMKeeper) EthGasConsumeDecorator { + return EthGasConsumeDecorator{ + ak: ak, + sk: sk, + evmKeeper: ek, + } +} + +// AnteHandle validates that the Ethereum tx message has enough to cover intrinsic gas +// (during CheckTx only) and that the sender has enough balance to pay for the gas cost. +// +// Intrinsic gas for a transaction is the amount of gas +// that the transaction uses before the transaction is executed. The gas is a +// constant value of 21000 plus any cost inccured by additional bytes of data +// supplied with the transaction. +func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + msgEthTx, ok := tx.(evmtypes.MsgEthereumTx) + if !ok { + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx) + } + + // sender address should be in the tx cache from the previous AnteHandle call + address := msgEthTx.From() + if address.Empty() { + panic("sender address cannot be empty") + } + + // fetch sender account from signature + senderAcc, err := auth.GetSignerAcc(ctx, egcd.ak, address) + if err != nil { + return ctx, err + } + + if senderAcc == nil { + return ctx, sdkerrors.Wrapf( + sdkerrors.ErrUnknownAddress, + "sender account %s (%s) is nil", common.BytesToAddress(address.Bytes()), address, + ) + } + + gasLimit := msgEthTx.GetGas() + gas, err := ethcore.IntrinsicGas(msgEthTx.Data.Payload, msgEthTx.To() == nil, true, false) + if err != nil { + return ctx, sdkerrors.Wrap(err, "failed to compute intrinsic gas cost") + } + + // intrinsic gas verification during CheckTx + if ctx.IsCheckTx() && gasLimit < gas { + return ctx, sdkerrors.Wrapf(sdkerrors.ErrOutOfGas, "intrinsic gas too low: %d < %d", gasLimit, gas) + } + + // Charge sender for gas up to limit + if gasLimit != 0 { + // Cost calculates the fees paid to validators based on gas limit and price + cost := new(big.Int).Mul(msgEthTx.Data.Price, new(big.Int).SetUint64(gasLimit)) + + evmDenom := egcd.evmKeeper.GetParams(ctx).EvmDenom + + feeAmt := sdk.NewCoins( + sdk.NewCoin(evmDenom, sdk.NewIntFromBigInt(cost)), + ) + + err = auth.DeductFees(egcd.sk, ctx, senderAcc, feeAmt) + if err != nil { + return ctx, err + } + } + + // Set gas meter after ante handler to ignore gaskv costs + newCtx = auth.SetGasMeter(simulate, ctx, gasLimit) + return next(newCtx, tx, simulate) +} + +// IncrementSenderSequenceDecorator increments the sequence of the signers. The +// main difference with the SDK's IncrementSequenceDecorator is that the MsgEthereumTx +// doesn't implement the SigVerifiableTx interface. +// +// CONTRACT: must be called after msg.VerifySig in order to cache the sender address. +type IncrementSenderSequenceDecorator struct { + ak auth.AccountKeeper +} + +// NewIncrementSenderSequenceDecorator creates a new IncrementSenderSequenceDecorator. +func NewIncrementSenderSequenceDecorator(ak auth.AccountKeeper) IncrementSenderSequenceDecorator { + return IncrementSenderSequenceDecorator{ + ak: ak, + } +} + +// AnteHandle handles incrementing the sequence of the sender. +func (issd IncrementSenderSequenceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { + // get and set account must be called with an infinite gas meter in order to prevent + // additional gas from being deducted. + gasMeter := ctx.GasMeter() + ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + + msgEthTx, ok := tx.(evmtypes.MsgEthereumTx) + if !ok { + ctx = ctx.WithGasMeter(gasMeter) + return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx) + } + + // increment sequence of all signers + for _, addr := range msgEthTx.GetSigners() { + acc := issd.ak.GetAccount(ctx, addr) + if err := acc.SetSequence(acc.GetSequence() + 1); err != nil { + panic(err) + } + issd.ak.SetAccount(ctx, acc) + } + + // set the original gas meter + ctx = ctx.WithGasMeter(gasMeter) + return next(ctx, tx, simulate) +} diff --git a/app/ante/utils_test.go b/app/ante/utils_test.go new file mode 100644 index 0000000000..8704375005 --- /dev/null +++ b/app/ante/utils_test.go @@ -0,0 +1,108 @@ +package ante_test + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/suite" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + + "github.com/cosmos/ethermint/app" + ante "github.com/cosmos/ethermint/app/ante" + "github.com/cosmos/ethermint/crypto" + ethermint "github.com/cosmos/ethermint/types" + evmtypes "github.com/cosmos/ethermint/x/evm/types" + + ethcrypto "github.com/ethereum/go-ethereum/crypto" + + abci "github.com/tendermint/tendermint/abci/types" + tmcrypto "github.com/tendermint/tendermint/crypto" +) + +type AnteTestSuite struct { + suite.Suite + + ctx sdk.Context + app *app.EthermintApp + anteHandler sdk.AnteHandler +} + +func (suite *AnteTestSuite) SetupTest() { + checkTx := false + + suite.app = app.Setup(checkTx) + suite.app.Codec().RegisterConcrete(&sdk.TestMsg{}, "test/TestMsg", nil) + + suite.ctx = suite.app.BaseApp.NewContext(checkTx, abci.Header{Height: 1, ChainID: "ethermint-3", Time: time.Now().UTC()}) + suite.app.EvmKeeper.SetParams(suite.ctx, evmtypes.DefaultParams()) + + suite.anteHandler = ante.NewAnteHandler(suite.app.AccountKeeper, suite.app.EvmKeeper, suite.app.SupplyKeeper) +} + +func TestAnteTestSuite(t *testing.T) { + suite.Run(t, new(AnteTestSuite)) +} + +func newTestMsg(addrs ...sdk.AccAddress) *sdk.TestMsg { + return sdk.NewTestMsg(addrs...) +} + +func newTestCoins() sdk.Coins { + return sdk.NewCoins(ethermint.NewPhotonCoinInt64(500000000)) +} + +func newTestStdFee() auth.StdFee { + return auth.NewStdFee(220000, sdk.NewCoins(ethermint.NewPhotonCoinInt64(150))) +} + +// GenerateAddress generates an Ethereum address. +func newTestAddrKey() (sdk.AccAddress, tmcrypto.PrivKey) { + privkey, _ := crypto.GenerateKey() + addr := ethcrypto.PubkeyToAddress(privkey.ToECDSA().PublicKey) + + return sdk.AccAddress(addr.Bytes()), privkey +} + +func newTestSDKTx( + ctx sdk.Context, msgs []sdk.Msg, privs []tmcrypto.PrivKey, + accNums []uint64, seqs []uint64, fee auth.StdFee, +) sdk.Tx { + + sigs := make([]auth.StdSignature, len(privs)) + for i, priv := range privs { + signBytes := auth.StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "") + + sig, err := priv.Sign(signBytes) + if err != nil { + panic(err) + } + + sigs[i] = auth.StdSignature{ + PubKey: priv.PubKey(), + Signature: sig, + } + } + + return auth.NewStdTx(msgs, fee, sigs, "") +} + +func newTestEthTx(ctx sdk.Context, msg evmtypes.MsgEthereumTx, priv tmcrypto.PrivKey) (sdk.Tx, error) { + chainIDEpoch, err := ethermint.ParseChainID(ctx.ChainID()) + if err != nil { + return nil, err + } + + privkey, ok := priv.(crypto.PrivKeySecp256k1) + if !ok { + return nil, fmt.Errorf("invalid private key type: %T", priv) + } + + if err := msg.Sign(chainIDEpoch, privkey.ToECDSA()); err != nil { + return nil, err + } + + return msg, nil +} diff --git a/app/ante_test.go b/app/ante_test.go deleted file mode 100644 index 5ce3cb9191..0000000000 --- a/app/ante_test.go +++ /dev/null @@ -1,312 +0,0 @@ -package app - -import ( - "fmt" - "math/big" - "testing" - - ethcmn "github.com/ethereum/go-ethereum/common" - "github.com/stretchr/testify/require" - tmcrypto "github.com/tendermint/tendermint/crypto" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/ethermint/types" - evmtypes "github.com/cosmos/ethermint/x/evm/types" -) - -func requireValidTx( - t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, sim bool, -) { - - _, result, abort := anteHandler(ctx, tx, sim) - require.Equal(t, sdk.CodeOK, result.Code, result.Log) - require.False(t, abort) - require.True(t, result.IsOK()) -} - -func requireInvalidTx( - t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, - tx sdk.Tx, sim bool, code sdk.CodeType, -) { - - newCtx, result, abort := anteHandler(ctx, tx, sim) - require.True(t, abort) - require.Equal(t, code, result.Code, fmt.Sprintf("invalid result: %v", result)) - - if code == sdk.CodeOutOfGas { - stdTx, ok := tx.(auth.StdTx) - require.True(t, ok, "tx must be in form auth.StdTx") - - // require GasWanted is set correctly - require.Equal(t, stdTx.Fee.Gas, result.GasWanted, "'GasWanted' wanted not set correctly") - require.True(t, result.GasUsed > result.GasWanted, "'GasUsed' not greater than GasWanted") - - // require that context is set correctly - require.Equal(t, result.GasUsed, newCtx.GasMeter().GasConsumed(), "Context not updated correctly") - } -} - -func TestValidEthTx(t *testing.T) { - input := newTestSetup() - input.ctx = input.ctx.WithBlockHeight(1) - - addr1, priv1 := newTestAddrKey() - addr2, _ := newTestAddrKey() - - acc1 := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) - acc1.SetCoins(newTestCoins()) - input.accKeeper.SetAccount(input.ctx, acc1) - - acc2 := input.accKeeper.NewAccountWithAddress(input.ctx, addr2) - acc2.SetCoins(newTestCoins()) - input.accKeeper.SetAccount(input.ctx, acc2) - - // require a valid Ethereum tx to pass - to := ethcmn.BytesToAddress(addr2.Bytes()) - amt := big.NewInt(32) - gas := big.NewInt(20) - ethMsg := evmtypes.NewEthereumTxMsg(0, to, amt, 22000, gas, []byte("test")) - - tx := newTestEthTx(input.ctx, ethMsg, priv1) - requireValidTx(t, input.anteHandler, input.ctx, tx, false) -} - -func TestValidTx(t *testing.T) { - input := newTestSetup() - input.ctx = input.ctx.WithBlockHeight(1) - - addr1, priv1 := newTestAddrKey() - addr2, priv2 := newTestAddrKey() - - acc1 := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) - acc1.SetCoins(newTestCoins()) - input.accKeeper.SetAccount(input.ctx, acc1) - - acc2 := input.accKeeper.NewAccountWithAddress(input.ctx, addr2) - acc2.SetCoins(newTestCoins()) - input.accKeeper.SetAccount(input.ctx, acc2) - - // require a valid SDK tx to pass - fee := newTestStdFee() - msg1 := newTestMsg(addr1, addr2) - msgs := []sdk.Msg{msg1} - - privKeys := []tmcrypto.PrivKey{priv1, priv2} - accNums := []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber()} - accSeqs := []uint64{acc1.GetSequence(), acc2.GetSequence()} - - tx := newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee) - requireValidTx(t, input.anteHandler, input.ctx, tx, false) - - // require accounts to update - acc1 = input.accKeeper.GetAccount(input.ctx, addr1) - acc2 = input.accKeeper.GetAccount(input.ctx, addr2) - require.Equal(t, accSeqs[0]+1, acc1.GetSequence()) - require.Equal(t, accSeqs[1]+1, acc2.GetSequence()) -} - -func TestSDKInvalidSigs(t *testing.T) { - input := newTestSetup() - input.ctx = input.ctx.WithBlockHeight(1) - - addr1, priv1 := newTestAddrKey() - addr2, priv2 := newTestAddrKey() - addr3, priv3 := newTestAddrKey() - - acc1 := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) - acc1.SetCoins(newTestCoins()) - input.accKeeper.SetAccount(input.ctx, acc1) - - acc2 := input.accKeeper.NewAccountWithAddress(input.ctx, addr2) - acc2.SetCoins(newTestCoins()) - input.accKeeper.SetAccount(input.ctx, acc2) - - fee := newTestStdFee() - msg1 := newTestMsg(addr1, addr2) - - // require validation failure with no signers - msgs := []sdk.Msg{msg1} - - privKeys := []tmcrypto.PrivKey{} - accNums := []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber()} - accSeqs := []uint64{acc1.GetSequence(), acc2.GetSequence()} - - tx := newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee) - requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnauthorized) - - // require validation failure with invalid number of signers - msgs = []sdk.Msg{msg1} - - privKeys = []tmcrypto.PrivKey{priv1} - accNums = []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber()} - accSeqs = []uint64{acc1.GetSequence(), acc2.GetSequence()} - - tx = newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee) - requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnauthorized) - - // require validation failure with an invalid signer - msg2 := newTestMsg(addr1, addr3) - msgs = []sdk.Msg{msg1, msg2} - - privKeys = []tmcrypto.PrivKey{priv1, priv2, priv3} - accNums = []uint64{acc1.GetAccountNumber(), acc2.GetAccountNumber(), 0} - accSeqs = []uint64{acc1.GetSequence(), acc2.GetSequence(), 0} - - tx = newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee) - requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnknownAddress) -} - -func TestSDKInvalidAcc(t *testing.T) { - input := newTestSetup() - input.ctx = input.ctx.WithBlockHeight(1) - - addr1, priv1 := newTestAddrKey() - - acc1 := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) - acc1.SetCoins(newTestCoins()) - input.accKeeper.SetAccount(input.ctx, acc1) - - fee := newTestStdFee() - msg1 := newTestMsg(addr1) - msgs := []sdk.Msg{msg1} - privKeys := []tmcrypto.PrivKey{priv1} - - // require validation failure with invalid account number - accNums := []uint64{1} - accSeqs := []uint64{acc1.GetSequence()} - - tx := newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee) - requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnauthorized) - - // require validation failure with invalid sequence (nonce) - accNums = []uint64{acc1.GetAccountNumber()} - accSeqs = []uint64{1} - - tx = newTestSDKTx(input.ctx, msgs, privKeys, accNums, accSeqs, fee) - requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeUnauthorized) -} - -func TestEthInvalidSig(t *testing.T) { - input := newTestSetup() - input.ctx = input.ctx.WithBlockHeight(1) - - _, priv1 := newTestAddrKey() - addr2, _ := newTestAddrKey() - to := ethcmn.BytesToAddress(addr2.Bytes()) - amt := big.NewInt(32) - gas := big.NewInt(20) - ethMsg := evmtypes.NewEthereumTxMsg(0, to, amt, 22000, gas, []byte("test")) - - tx := newTestEthTx(input.ctx, ethMsg, priv1) - ctx := input.ctx.WithChainID("4") - requireInvalidTx(t, input.anteHandler, ctx, tx, false, sdk.CodeUnauthorized) -} - -func TestEthInvalidNonce(t *testing.T) { - input := newTestSetup() - input.ctx = input.ctx.WithBlockHeight(1) - - addr1, priv1 := newTestAddrKey() - addr2, _ := newTestAddrKey() - - acc := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) - acc.SetCoins(newTestCoins()) - acc.SetSequence(10) - input.accKeeper.SetAccount(input.ctx, acc) - - // require a valid Ethereum tx to pass - to := ethcmn.BytesToAddress(addr2.Bytes()) - amt := big.NewInt(32) - gas := big.NewInt(20) - ethMsg := evmtypes.NewEthereumTxMsg(0, to, amt, 22000, gas, []byte("test")) - - tx := newTestEthTx(input.ctx, ethMsg, priv1) - requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeInvalidSequence) -} - -func TestEthInsufficientBalance(t *testing.T) { - input := newTestSetup() - input.ctx = input.ctx.WithBlockHeight(1) - - addr1, priv1 := newTestAddrKey() - addr2, _ := newTestAddrKey() - - acc := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) - input.accKeeper.SetAccount(input.ctx, acc) - - // require a valid Ethereum tx to pass - to := ethcmn.BytesToAddress(addr2.Bytes()) - amt := big.NewInt(32) - gas := big.NewInt(20) - ethMsg := evmtypes.NewEthereumTxMsg(0, to, amt, 22000, gas, []byte("test")) - - tx := newTestEthTx(input.ctx, ethMsg, priv1) - requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeInsufficientFunds) -} - -func TestEthInvalidIntrinsicGas(t *testing.T) { - input := newTestSetup() - input.ctx = input.ctx.WithBlockHeight(1) - - addr1, priv1 := newTestAddrKey() - addr2, _ := newTestAddrKey() - - acc := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) - acc.SetCoins(newTestCoins()) - input.accKeeper.SetAccount(input.ctx, acc) - - // require a valid Ethereum tx to pass - to := ethcmn.BytesToAddress(addr2.Bytes()) - amt := big.NewInt(32) - gas := big.NewInt(20) - gasLimit := uint64(1000) - ethMsg := evmtypes.NewEthereumTxMsg(0, to, amt, gasLimit, gas, []byte("test")) - - tx := newTestEthTx(input.ctx, ethMsg, priv1) - requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeInternal) -} - -func TestEthInvalidMempoolFees(t *testing.T) { - input := newTestSetup() - input.ctx = input.ctx.WithBlockHeight(1) - input.ctx = input.ctx.WithMinimumFees(sdk.Coins{sdk.NewInt64Coin(types.DenomDefault, 500000)}) - - addr1, priv1 := newTestAddrKey() - addr2, _ := newTestAddrKey() - - acc := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) - acc.SetCoins(newTestCoins()) - input.accKeeper.SetAccount(input.ctx, acc) - - // require a valid Ethereum tx to pass - to := ethcmn.BytesToAddress(addr2.Bytes()) - amt := big.NewInt(32) - gas := big.NewInt(20) - ethMsg := evmtypes.NewEthereumTxMsg(0, to, amt, 22000, gas, []byte("test")) - - tx := newTestEthTx(input.ctx, ethMsg, priv1) - requireInvalidTx(t, input.anteHandler, input.ctx, tx, false, sdk.CodeInsufficientFee) -} - -func TestEthInvalidChainID(t *testing.T) { - input := newTestSetup() - input.ctx = input.ctx.WithBlockHeight(1) - - addr1, priv1 := newTestAddrKey() - addr2, _ := newTestAddrKey() - - acc := input.accKeeper.NewAccountWithAddress(input.ctx, addr1) - acc.SetCoins(newTestCoins()) - input.accKeeper.SetAccount(input.ctx, acc) - - // require a valid Ethereum tx to pass - to := ethcmn.BytesToAddress(addr2.Bytes()) - amt := big.NewInt(32) - gas := big.NewInt(20) - ethMsg := evmtypes.NewEthereumTxMsg(0, to, amt, 22000, gas, []byte("test")) - - tx := newTestEthTx(input.ctx, ethMsg, priv1) - ctx := input.ctx.WithChainID("bad-chain-id") - requireInvalidTx(t, input.anteHandler, ctx, tx, false, types.CodeInvalidChainID) -} diff --git a/app/ethermint.go b/app/ethermint.go index f90bc3b4e6..65d71a0491 100644 --- a/app/ethermint.go +++ b/app/ethermint.go @@ -1,176 +1,401 @@ package app import ( + "io" + "os" + bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/crisis" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/evidence" + "github.com/cosmos/cosmos-sdk/x/genutil" "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/mint" "github.com/cosmos/cosmos-sdk/x/params" + paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" "github.com/cosmos/cosmos-sdk/x/slashing" - "github.com/cosmos/cosmos-sdk/x/stake" - - "github.com/cosmos/ethermint/crypto" - evmtypes "github.com/cosmos/ethermint/x/evm/types" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/supply" + "github.com/cosmos/cosmos-sdk/x/upgrade" + upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" - "github.com/pkg/errors" + "github.com/cosmos/ethermint/app/ante" + ethermintcodec "github.com/cosmos/ethermint/codec" + ethermint "github.com/cosmos/ethermint/types" + "github.com/cosmos/ethermint/x/evm" + "github.com/cosmos/ethermint/x/faucet" abci "github.com/tendermint/tendermint/abci/types" - tmcmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" - tmlog "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/libs/log" + tmos "github.com/tendermint/tendermint/libs/os" + dbm "github.com/tendermint/tm-db" ) +func init() { + // set the address prefixes + config := sdk.GetConfig() + ethermint.SetBech32Prefixes(config) + ethermint.SetBip44CoinType(config) +} + const appName = "Ethermint" -// application multi-store keys var ( - storeKeyAccount = sdk.NewKVStoreKey("acc") - storeKeyStorage = sdk.NewKVStoreKey("contract_storage") - storeKeyMain = sdk.NewKVStoreKey("main") - storeKeyStake = sdk.NewKVStoreKey("stake") - storeKeySlashing = sdk.NewKVStoreKey("slashing") - storeKeyGov = sdk.NewKVStoreKey("gov") - storeKeyFeeColl = sdk.NewKVStoreKey("fee") - storeKeyParams = sdk.NewKVStoreKey("params") - storeKeyTransParams = sdk.NewTransientStoreKey("transient_params") -) + // DefaultCLIHome sets the default home directories for the application CLI + DefaultCLIHome = os.ExpandEnv("$HOME/.ethermintcli") -type ( - // EthermintApp implements an extended ABCI application. It is an application - // that may process transactions through Ethereum's EVM running atop of - // Tendermint consensus. - EthermintApp struct { - *bam.BaseApp - - cdc *codec.Codec - - accountKey *sdk.KVStoreKey - storageKey *sdk.KVStoreKey - mainKey *sdk.KVStoreKey - stakeKey *sdk.KVStoreKey - slashingKey *sdk.KVStoreKey - govKey *sdk.KVStoreKey - feeCollKey *sdk.KVStoreKey - paramsKey *sdk.KVStoreKey - tParamsKey *sdk.TransientStoreKey - - accountKeeper auth.AccountKeeper - feeCollKeeper auth.FeeCollectionKeeper - coinKeeper bank.Keeper - stakeKeeper stake.Keeper - slashingKeeper slashing.Keeper - govKeeper gov.Keeper - paramsKeeper params.Keeper + // DefaultNodeHome sets the folder where the applcation data and configuration will be stored + DefaultNodeHome = os.ExpandEnv("$HOME/.ethermintd") + + // ModuleBasics defines the module BasicManager is in charge of setting up basic, + // non-dependant module elements, such as codec registration + // and genesis verification. + ModuleBasics = module.NewBasicManager( + auth.AppModuleBasic{}, + supply.AppModuleBasic{}, + genutil.AppModuleBasic{}, + bank.AppModuleBasic{}, + staking.AppModuleBasic{}, + mint.AppModuleBasic{}, + distr.AppModuleBasic{}, + gov.NewAppModuleBasic( + paramsclient.ProposalHandler, distr.ProposalHandler, upgradeclient.ProposalHandler, + ), + params.AppModuleBasic{}, + crisis.AppModuleBasic{}, + slashing.AppModuleBasic{}, + evidence.AppModuleBasic{}, + upgrade.AppModuleBasic{}, + evm.AppModuleBasic{}, + faucet.AppModuleBasic{}, + ) + + // module account permissions + maccPerms = map[string][]string{ + auth.FeeCollectorName: nil, + distr.ModuleName: nil, + mint.ModuleName: {supply.Minter}, + staking.BondedPoolName: {supply.Burner, supply.Staking}, + staking.NotBondedPoolName: {supply.Burner, supply.Staking}, + gov.ModuleName: {supply.Burner}, + faucet.ModuleName: {supply.Minter}, + } + + // module accounts that are allowed to receive tokens + allowedReceivingModAcc = map[string]bool{ + distr.ModuleName: true, } ) -// NewEthermintApp returns a reference to a new initialized Ethermint -// application. -// -// TODO: Ethermint needs to support being bootstrapped as an application running -// in a sovereign zone and as an application running with a shared security model. -// For now, it will support only running as a sovereign application. -func NewEthermintApp(logger tmlog.Logger, db dbm.DB, baseAppOpts ...func(*bam.BaseApp)) *EthermintApp { - cdc := CreateCodec() +var _ simapp.App = (*EthermintApp)(nil) + +// EthermintApp implements an extended ABCI application. It is an application +// that may process transactions through Ethereum's EVM running atop of +// Tendermint consensus. +type EthermintApp struct { + *bam.BaseApp + cdc *codec.Codec + + invCheckPeriod uint + + // keys to access the substores + keys map[string]*sdk.KVStoreKey + tkeys map[string]*sdk.TransientStoreKey + + // subspaces + subspaces map[string]params.Subspace + + // keepers + AccountKeeper auth.AccountKeeper + BankKeeper bank.Keeper + SupplyKeeper supply.Keeper + StakingKeeper staking.Keeper + SlashingKeeper slashing.Keeper + MintKeeper mint.Keeper + DistrKeeper distr.Keeper + GovKeeper gov.Keeper + CrisisKeeper crisis.Keeper + UpgradeKeeper upgrade.Keeper + ParamsKeeper params.Keeper + EvidenceKeeper evidence.Keeper + EvmKeeper evm.Keeper + FaucetKeeper faucet.Keeper + + // the module manager + mm *module.Manager + + // simulation manager + sm *module.SimulationManager +} + +// NewEthermintApp returns a reference to a new initialized Ethermint application. +func NewEthermintApp( + logger log.Logger, + db dbm.DB, + traceStore io.Writer, + loadLatest bool, + skipUpgradeHeights map[int64]bool, + invCheckPeriod uint, + baseAppOptions ...func(*bam.BaseApp), +) *EthermintApp { + + cdc := ethermintcodec.MakeCodec(ModuleBasics) + + // NOTE we use custom Ethermint transaction decoder that supports the sdk.Tx interface instead of sdk.StdTx + bApp := bam.NewBaseApp(appName, logger, db, evm.TxDecoder(cdc), baseAppOptions...) + bApp.SetCommitMultiStoreTracer(traceStore) + bApp.SetAppVersion(version.Version) + + keys := sdk.NewKVStoreKeys( + bam.MainStoreKey, auth.StoreKey, staking.StoreKey, + supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey, + gov.StoreKey, params.StoreKey, upgrade.StoreKey, evidence.StoreKey, + evm.StoreKey, faucet.StoreKey, + ) + + tkeys := sdk.NewTransientStoreKeys(params.TStoreKey) - baseApp := bam.NewBaseApp(appName, logger, db, evmtypes.TxDecoder(cdc), baseAppOpts...) app := &EthermintApp{ - BaseApp: baseApp, - cdc: cdc, - accountKey: storeKeyAccount, - storageKey: storeKeyStorage, - mainKey: storeKeyMain, - stakeKey: storeKeyStake, - slashingKey: storeKeySlashing, - govKey: storeKeyGov, - feeCollKey: storeKeyFeeColl, - paramsKey: storeKeyParams, - tParamsKey: storeKeyTransParams, + BaseApp: bApp, + cdc: cdc, + invCheckPeriod: invCheckPeriod, + keys: keys, + tkeys: tkeys, + subspaces: make(map[string]params.Subspace), } - app.paramsKeeper = params.NewKeeper(app.cdc, app.paramsKey, app.tParamsKey) - app.accountKeeper = auth.NewAccountKeeper(app.cdc, app.accountKey, auth.ProtoBaseAccount) - app.feeCollKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.feeCollKey) + // init params keeper and subspaces + app.ParamsKeeper = params.NewKeeper(cdc, keys[params.StoreKey], tkeys[params.TStoreKey]) + app.subspaces[auth.ModuleName] = app.ParamsKeeper.Subspace(auth.DefaultParamspace) + app.subspaces[bank.ModuleName] = app.ParamsKeeper.Subspace(bank.DefaultParamspace) + app.subspaces[staking.ModuleName] = app.ParamsKeeper.Subspace(staking.DefaultParamspace) + app.subspaces[mint.ModuleName] = app.ParamsKeeper.Subspace(mint.DefaultParamspace) + app.subspaces[distr.ModuleName] = app.ParamsKeeper.Subspace(distr.DefaultParamspace) + app.subspaces[slashing.ModuleName] = app.ParamsKeeper.Subspace(slashing.DefaultParamspace) + app.subspaces[gov.ModuleName] = app.ParamsKeeper.Subspace(gov.DefaultParamspace).WithKeyTable(gov.ParamKeyTable()) + app.subspaces[crisis.ModuleName] = app.ParamsKeeper.Subspace(crisis.DefaultParamspace) + app.subspaces[evidence.ModuleName] = app.ParamsKeeper.Subspace(evidence.DefaultParamspace) + app.subspaces[evm.ModuleName] = app.ParamsKeeper.Subspace(evm.DefaultParamspace) - // register message handlers - app.Router(). - // TODO: add remaining routes - AddRoute("stake", stake.NewHandler(app.stakeKeeper)). - AddRoute("slashing", slashing.NewHandler(app.slashingKeeper)). - AddRoute("gov", gov.NewHandler(app.govKeeper)) + // use custom Ethermint account for contracts + app.AccountKeeper = auth.NewAccountKeeper( + cdc, keys[auth.StoreKey], app.subspaces[auth.ModuleName], ethermint.ProtoAccount, + ) + app.BankKeeper = bank.NewBaseKeeper( + app.AccountKeeper, app.subspaces[bank.ModuleName], app.BlacklistedAccAddrs(), + ) + app.SupplyKeeper = supply.NewKeeper( + cdc, keys[supply.StoreKey], app.AccountKeeper, app.BankKeeper, maccPerms, + ) + stakingKeeper := staking.NewKeeper( + cdc, keys[staking.StoreKey], app.SupplyKeeper, app.subspaces[staking.ModuleName], + ) + app.MintKeeper = mint.NewKeeper( + cdc, keys[mint.StoreKey], app.subspaces[mint.ModuleName], &stakingKeeper, + app.SupplyKeeper, auth.FeeCollectorName, + ) + app.DistrKeeper = distr.NewKeeper( + cdc, keys[distr.StoreKey], app.subspaces[distr.ModuleName], &stakingKeeper, + app.SupplyKeeper, auth.FeeCollectorName, app.ModuleAccountAddrs(), + ) + app.SlashingKeeper = slashing.NewKeeper( + cdc, keys[slashing.StoreKey], &stakingKeeper, app.subspaces[slashing.ModuleName], + ) + app.CrisisKeeper = crisis.NewKeeper( + app.subspaces[crisis.ModuleName], invCheckPeriod, app.SupplyKeeper, auth.FeeCollectorName, + ) + app.UpgradeKeeper = upgrade.NewKeeper(skipUpgradeHeights, keys[upgrade.StoreKey], app.cdc) + app.EvmKeeper = evm.NewKeeper( + app.cdc, keys[evm.StoreKey], app.subspaces[evm.ModuleName], app.AccountKeeper, + ) + app.FaucetKeeper = faucet.NewKeeper( + app.cdc, keys[faucet.StoreKey], app.SupplyKeeper, + ) - // initialize the underlying ABCI BaseApp - app.SetInitChainer(app.initChainer) - app.SetBeginBlocker(app.BeginBlocker) - app.SetEndBlocker(app.EndBlocker) - app.SetAnteHandler(NewAnteHandler(app.accountKeeper, app.feeCollKeeper)) + // create evidence keeper with router + evidenceKeeper := evidence.NewKeeper( + cdc, keys[evidence.StoreKey], app.subspaces[evidence.ModuleName], &app.StakingKeeper, app.SlashingKeeper, + ) + evidenceRouter := evidence.NewRouter() + // TODO: Register evidence routes. + evidenceKeeper.SetRouter(evidenceRouter) + app.EvidenceKeeper = *evidenceKeeper + + // register the proposal types + govRouter := gov.NewRouter() + govRouter.AddRoute(gov.RouterKey, gov.ProposalHandler). + AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper)). + AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.DistrKeeper)). + AddRoute(upgrade.RouterKey, upgrade.NewSoftwareUpgradeProposalHandler(app.UpgradeKeeper)) + + app.GovKeeper = gov.NewKeeper( + cdc, keys[gov.StoreKey], app.subspaces[gov.ModuleName], app.SupplyKeeper, + &stakingKeeper, govRouter, + ) + + // register the staking hooks + // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks + app.StakingKeeper = *stakingKeeper.SetHooks( + staking.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks()), + ) + + // NOTE: Any module instantiated in the module manager that is later modified + // must be passed by reference here. + app.mm = module.NewManager( + genutil.NewAppModule(app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx), + auth.NewAppModule(app.AccountKeeper), + bank.NewAppModule(app.BankKeeper, app.AccountKeeper), + crisis.NewAppModule(&app.CrisisKeeper), + supply.NewAppModule(app.SupplyKeeper, app.AccountKeeper), + gov.NewAppModule(app.GovKeeper, app.AccountKeeper, app.SupplyKeeper), + mint.NewAppModule(app.MintKeeper), + slashing.NewAppModule(app.SlashingKeeper, app.AccountKeeper, app.StakingKeeper), + distr.NewAppModule(app.DistrKeeper, app.AccountKeeper, app.SupplyKeeper, app.StakingKeeper), + staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), + evidence.NewAppModule(app.EvidenceKeeper), + evm.NewAppModule(app.EvmKeeper, app.AccountKeeper), + faucet.NewAppModule(app.FaucetKeeper), + ) + + // During begin block slashing happens after distr.BeginBlocker so that + // there is nothing left over in the validator fee pool, so as to keep the + // CanWithdrawInvariant invariant. + app.mm.SetOrderBeginBlockers( + evm.ModuleName, mint.ModuleName, distr.ModuleName, slashing.ModuleName, + evidence.ModuleName, + ) + app.mm.SetOrderEndBlockers( + evm.ModuleName, crisis.ModuleName, gov.ModuleName, staking.ModuleName, + ) + + // NOTE: The genutils module must occur after staking so that pools are + // properly initialized with tokens from genesis accounts. + app.mm.SetOrderInitGenesis( + auth.ModuleName, distr.ModuleName, staking.ModuleName, bank.ModuleName, + slashing.ModuleName, gov.ModuleName, mint.ModuleName, supply.ModuleName, + crisis.ModuleName, genutil.ModuleName, evidence.ModuleName, evm.ModuleName, + faucet.ModuleName, + ) + + app.mm.RegisterInvariants(&app.CrisisKeeper) + app.mm.RegisterRoutes(app.Router(), app.QueryRouter()) - app.MountStores( - app.mainKey, app.accountKey, app.stakeKey, app.slashingKey, - app.govKey, app.feeCollKey, app.paramsKey, app.storageKey, + // create the simulation manager and define the order of the modules for deterministic simulations + // + // NOTE: this is not required apps that don't use the simulator for fuzz testing + // transactions + app.sm = module.NewSimulationManager( + auth.NewAppModule(app.AccountKeeper), + bank.NewAppModule(app.BankKeeper, app.AccountKeeper), + supply.NewAppModule(app.SupplyKeeper, app.AccountKeeper), + gov.NewAppModule(app.GovKeeper, app.AccountKeeper, app.SupplyKeeper), + mint.NewAppModule(app.MintKeeper), + staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper), + distr.NewAppModule(app.DistrKeeper, app.AccountKeeper, app.SupplyKeeper, app.StakingKeeper), + slashing.NewAppModule(app.SlashingKeeper, app.AccountKeeper, app.StakingKeeper), + params.NewAppModule(), // NOTE: only used for simulation to generate randomized param change proposals ) - app.MountStore(app.tParamsKey, sdk.StoreTypeTransient) - if err := app.LoadLatestVersion(app.accountKey); err != nil { - tmcmn.Exit(err.Error()) + app.sm.RegisterStoreDecoders() + + // initialize stores + app.MountKVStores(keys) + app.MountTransientStores(tkeys) + + // initialize BaseApp + app.SetInitChainer(app.InitChainer) + app.SetBeginBlocker(app.BeginBlocker) + app.SetAnteHandler(ante.NewAnteHandler(app.AccountKeeper, app.EvmKeeper, app.SupplyKeeper)) + app.SetEndBlocker(app.EndBlocker) + + if loadLatest { + err := app.LoadLatestVersion(app.keys[bam.MainStoreKey]) + if err != nil { + tmos.Exit(err.Error()) + } } - app.BaseApp.Seal() return app } -// BeginBlocker signals the beginning of a block. It performs application -// updates on the start of every block. -func (app *EthermintApp) BeginBlocker( - _ sdk.Context, _ abci.RequestBeginBlock, -) abci.ResponseBeginBlock { +// Name returns the name of the App +func (app *EthermintApp) Name() string { return app.BaseApp.Name() } + +// BeginBlocker updates every begin block +func (app *EthermintApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { + return app.mm.BeginBlock(ctx, req) +} - return abci.ResponseBeginBlock{} +// EndBlocker updates every end block +func (app *EthermintApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { + return app.mm.EndBlock(ctx, req) } -// EndBlocker signals the end of a block. It performs application updates on -// the end of every block. -func (app *EthermintApp) EndBlocker( - _ sdk.Context, _ abci.RequestEndBlock, -) abci.ResponseEndBlock { +// InitChainer updates at chain initialization +func (app *EthermintApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + var genesisState simapp.GenesisState + app.cdc.MustUnmarshalJSON(req.AppStateBytes, &genesisState) + return app.mm.InitGenesis(ctx, genesisState) +} - return abci.ResponseEndBlock{} +// LoadHeight loads state at a particular height +func (app *EthermintApp) LoadHeight(height int64) error { + return app.LoadVersion(height, app.keys[bam.MainStoreKey]) } -// initChainer initializes the application blockchain with validators and other -// state data from TendermintCore. -func (app *EthermintApp) initChainer( - _ sdk.Context, req abci.RequestInitChain, -) abci.ResponseInitChain { +// ModuleAccountAddrs returns all the app's module account addresses. +func (app *EthermintApp) ModuleAccountAddrs() map[string]bool { + modAccAddrs := make(map[string]bool) + for acc := range maccPerms { + modAccAddrs[supply.NewModuleAddress(acc).String()] = true + } - var genesisState GenesisState - stateJSON := req.AppStateBytes + return modAccAddrs +} - err := app.cdc.UnmarshalJSON(stateJSON, &genesisState) - if err != nil { - panic(errors.Wrap(err, "failed to parse application genesis state")) +// BlacklistedAccAddrs returns all the app's module account addresses black listed for receiving tokens. +func (app *EthermintApp) BlacklistedAccAddrs() map[string]bool { + blacklistedAddrs := make(map[string]bool) + for acc := range maccPerms { + blacklistedAddrs[supply.NewModuleAddress(acc).String()] = !allowedReceivingModAcc[acc] } - // TODO: load the genesis accounts + return blacklistedAddrs +} - return abci.ResponseInitChain{} +// SimulationManager implements the SimulationApp interface +func (app *EthermintApp) SimulationManager() *module.SimulationManager { + return app.sm } -// CreateCodec creates a new amino wire codec and registers all the necessary -// concrete types and interfaces needed for the application. -func CreateCodec() *codec.Codec { - cdc := codec.New() +// GetKey returns the KVStoreKey for the provided store key. +// +// NOTE: This is solely to be used for testing purposes. +func (app *EthermintApp) GetKey(storeKey string) *sdk.KVStoreKey { + return app.keys[storeKey] +} - // TODO: Add remaining codec registrations: - // bank, staking, distribution, slashing, and gov +// Codec returns Ethermint's codec. +// +// NOTE: This is solely to be used for testing purposes as it may be desirable +// for modules to register their own custom testing types. +func (app *EthermintApp) Codec() *codec.Codec { + return app.cdc +} - crypto.RegisterCodec(cdc) - evmtypes.RegisterCodec(cdc) - auth.RegisterCodec(cdc) - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) +// GetMaccPerms returns a copy of the module account permissions +func GetMaccPerms() map[string][]string { + dupMaccPerms := make(map[string][]string) + for k, v := range maccPerms { + dupMaccPerms[k] = v + } - return cdc + return dupMaccPerms } diff --git a/app/ethermint_test.go b/app/ethermint_test.go new file mode 100644 index 0000000000..6a3cdd2636 --- /dev/null +++ b/app/ethermint_test.go @@ -0,0 +1,37 @@ +package app + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/codec" +) + +func TestEthermintAppExport(t *testing.T) { + db := dbm.NewMemDB() + app := NewEthermintApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, 0) + + genesisState := ModuleBasics.DefaultGenesis() + stateBytes, err := codec.MarshalJSONIndent(app.cdc, genesisState) + require.NoError(t, err) + + // Initialize the chain + app.InitChain( + abci.RequestInitChain{ + Validators: []abci.ValidatorUpdate{}, + AppStateBytes: stateBytes, + }, + ) + app.Commit() + + // Making a new app object with the db, so that initchain hasn't been called + app2 := NewEthermintApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, 0) + _, _, err = app2.ExportAppStateAndValidators(false, []string{}) + require.NoError(t, err, "ExportAppStateAndValidators should not have an error") +} diff --git a/app/export.go b/app/export.go new file mode 100644 index 0000000000..dfb7d17df8 --- /dev/null +++ b/app/export.go @@ -0,0 +1,177 @@ +package app + +import ( + "encoding/json" + "log" + + abci "github.com/tendermint/tendermint/abci/types" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/slashing" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/staking/exported" + + ethcdc "github.com/cosmos/ethermint/codec" +) + +// NewDefaultGenesisState generates the default state for the application. +func NewDefaultGenesisState() simapp.GenesisState { + _ = ethcdc.MakeCodec(ModuleBasics) + return ModuleBasics.DefaultGenesis() +} + +// ExportAppStateAndValidators exports the state of the application for a genesis +// file. +func (app *EthermintApp) ExportAppStateAndValidators( + forZeroHeight bool, jailWhiteList []string, +) (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) { + + // Creates context with current height and checks txs for ctx to be usable by start of next block + ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) + + if forZeroHeight { + app.prepForZeroHeightGenesis(ctx, jailWhiteList) + } + + // Export genesis to be used by SDK modules + genState := app.mm.ExportGenesis(ctx) + appState, err = codec.MarshalJSONIndent(app.cdc, genState) + if err != nil { + return nil, nil, err + } + + // Write validators to staking module to be used by TM node + validators = staking.WriteValidators(ctx, app.StakingKeeper) + return appState, validators, nil +} + +// prepare for fresh start at zero height +// NOTE zero height genesis is a temporary feature which will be deprecated +// in favour of export at a block height +func (app *EthermintApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []string) { + applyWhiteList := false + + //Check if there is a whitelist + if len(jailWhiteList) > 0 { + applyWhiteList = true + } + + whiteListMap := make(map[string]bool) + + for _, addr := range jailWhiteList { + _, err := sdk.ValAddressFromBech32(addr) + if err != nil { + log.Fatal(err) + } + whiteListMap[addr] = true + } + + /* Just to be safe, assert the invariants on current state. */ + app.CrisisKeeper.AssertInvariants(ctx) + + /* Handle fee distribution state. */ + + // withdraw all validator commission + app.StakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) { + _, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator()) + return false + }) + + // withdraw all delegator rewards + dels := app.StakingKeeper.GetAllDelegations(ctx) + for _, delegation := range dels { + _, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) + } + + // clear validator slash events + app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx) + + // clear validator historical rewards + app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx) + + // set context height to zero + height := ctx.BlockHeight() + ctx = ctx.WithBlockHeight(0) + + // reinitialize all validators + app.StakingKeeper.IterateValidators(ctx, func(_ int64, val exported.ValidatorI) (stop bool) { + // donate any unwithdrawn outstanding reward fraction tokens to the community pool + scraps := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, val.GetOperator()) + feePool := app.DistrKeeper.GetFeePool(ctx) + feePool.CommunityPool = feePool.CommunityPool.Add(scraps...) + app.DistrKeeper.SetFeePool(ctx, feePool) + + app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator()) + return false + }) + + // reinitialize all delegations + for _, del := range dels { + app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, del.DelegatorAddress, del.ValidatorAddress) + app.DistrKeeper.Hooks().AfterDelegationModified(ctx, del.DelegatorAddress, del.ValidatorAddress) + } + + // reset context height + ctx = ctx.WithBlockHeight(height) + + /* Handle staking state. */ + + // iterate through redelegations, reset creation height + app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red staking.Redelegation) (stop bool) { + for i := range red.Entries { + red.Entries[i].CreationHeight = 0 + } + app.StakingKeeper.SetRedelegation(ctx, red) + return false + }) + + // iterate through unbonding delegations, reset creation height + app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd staking.UnbondingDelegation) (stop bool) { + for i := range ubd.Entries { + ubd.Entries[i].CreationHeight = 0 + } + app.StakingKeeper.SetUnbondingDelegation(ctx, ubd) + return false + }) + + // Iterate through validators by power descending, reset bond heights, and + // update bond intra-tx counters. + store := ctx.KVStore(app.keys[staking.StoreKey]) + iter := sdk.KVStoreReversePrefixIterator(store, staking.ValidatorsKey) + counter := int16(0) + + for ; iter.Valid(); iter.Next() { + addr := sdk.ValAddress(iter.Key()[1:]) + validator, found := app.StakingKeeper.GetValidator(ctx, addr) + if !found { + panic("expected validator, not found") + } + + validator.UnbondingHeight = 0 + if applyWhiteList && !whiteListMap[addr.String()] { + validator.Jailed = true + } + + app.StakingKeeper.SetValidator(ctx, validator) + counter++ + } + + iter.Close() + + _ = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + + /* Handle slashing state. */ + + // reset start height on signing infos + app.SlashingKeeper.IterateValidatorSigningInfos( + ctx, + func(addr sdk.ConsAddress, info slashing.ValidatorSigningInfo) (stop bool) { + info.StartHeight = 0 + app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info) + return false + }, + ) +} diff --git a/app/genesis.go b/app/genesis.go deleted file mode 100644 index 0b59fe14e9..0000000000 --- a/app/genesis.go +++ /dev/null @@ -1,22 +0,0 @@ -package app - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/ethermint/types" -) - -type ( - // GenesisState defines the application's genesis state. It contains all the - // information required and accounts to initialize the blockchain. - GenesisState struct { - Accounts []GenesisAccount `json:"accounts"` - } - - // GenesisAccount defines an account to be initialized in the genesis state. - GenesisAccount struct { - Address sdk.AccAddress `json:"address"` - Coins sdk.Coins `json:"coins"` - Code []byte `json:"code,omitempty"` - Storage types.Storage `json:"storage,omitempty"` - } -) diff --git a/app/simulation_test.go b/app/simulation_test.go new file mode 100644 index 0000000000..a5b3d1be10 --- /dev/null +++ b/app/simulation_test.go @@ -0,0 +1,296 @@ +package app + +import ( + "encoding/json" + "fmt" + "math/rand" + "os" + "testing" + + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/simapp/helpers" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/mint" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/simulation" + "github.com/cosmos/cosmos-sdk/x/slashing" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/supply" +) + +func init() { + simapp.GetSimulatorFlags() +} + +type storeKeysPrefixes struct { + A sdk.StoreKey + B sdk.StoreKey + Prefixes [][]byte +} + +// fauxMerkleModeOpt returns a BaseApp option to use a dbStoreAdapter instead of +// an IAVLStore for faster simulation speed. +func fauxMerkleModeOpt(bapp *baseapp.BaseApp) { + bapp.SetFauxMerkleMode() +} + +// interBlockCacheOpt returns a BaseApp option function that sets the persistent +// inter-block write-through cache. +func interBlockCacheOpt() func(*baseapp.BaseApp) { + return baseapp.SetInterBlockCache(store.NewCommitKVStoreCacheManager()) +} + +func TestFullAppSimulation(t *testing.T) { + config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation") + if skip { + t.Skip("skipping application simulation") + } + require.NoError(t, err, "simulation setup failed") + + defer func() { + db.Close() + require.NoError(t, os.RemoveAll(dir)) + }() + + app := NewEthermintApp(logger, db, nil, true, map[int64]bool{}, simapp.FlagPeriodValue, fauxMerkleModeOpt) + require.Equal(t, appName, app.Name()) + + // run randomized simulation + _, simParams, simErr := simulation.SimulateFromSeed( + t, os.Stdout, app.BaseApp, simapp.AppStateFn(app.Codec(), app.SimulationManager()), + simapp.SimulationOperations(app, app.Codec(), config), + app.ModuleAccountAddrs(), config, + ) + + // export state and simParams before the simulation error is checked + err = simapp.CheckExportSimulation(app, config, simParams) + require.NoError(t, err) + require.NoError(t, simErr) + + if config.Commit { + simapp.PrintStats(db) + } +} + +func TestAppImportExport(t *testing.T) { + config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation") + if skip { + t.Skip("skipping application import/export simulation") + } + require.NoError(t, err, "simulation setup failed") + + defer func() { + db.Close() + require.NoError(t, os.RemoveAll(dir)) + }() + + app := NewEthermintApp(logger, db, nil, true, map[int64]bool{}, simapp.FlagPeriodValue, fauxMerkleModeOpt) + require.Equal(t, appName, app.Name()) + + // Run randomized simulation + _, simParams, simErr := simulation.SimulateFromSeed( + t, os.Stdout, app.BaseApp, simapp.AppStateFn(app.Codec(), app.SimulationManager()), + simapp.SimulationOperations(app, app.Codec(), config), + app.ModuleAccountAddrs(), config, + ) + + // export state and simParams before the simulation error is checked + err = simapp.CheckExportSimulation(app, config, simParams) + require.NoError(t, err) + require.NoError(t, simErr) + + if config.Commit { + simapp.PrintStats(db) + } + + fmt.Printf("exporting genesis...\n") + + appState, _, err := app.ExportAppStateAndValidators(false, []string{}) + require.NoError(t, err) + + fmt.Printf("importing genesis...\n") + + // nolint: dogsled + _, newDB, newDir, _, _, err := simapp.SetupSimulation("leveldb-app-sim-2", "Simulation-2") + require.NoError(t, err, "simulation setup failed") + + defer func() { + newDB.Close() + require.NoError(t, os.RemoveAll(newDir)) + }() + + newApp := NewEthermintApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, simapp.FlagPeriodValue, fauxMerkleModeOpt) + require.Equal(t, appName, newApp.Name()) + + var genesisState map[string]json.RawMessage + err = app.Codec().UnmarshalJSON(appState, &genesisState) + require.NoError(t, err) + + ctxA := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) + ctxB := newApp.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) + newApp.mm.InitGenesis(ctxB, genesisState) + + fmt.Printf("comparing stores...\n") + + storeKeysPrefixes := []storeKeysPrefixes{ + {app.keys[baseapp.MainStoreKey], newApp.keys[baseapp.MainStoreKey], [][]byte{}}, + {app.keys[auth.StoreKey], newApp.keys[auth.StoreKey], [][]byte{}}, + {app.keys[staking.StoreKey], newApp.keys[staking.StoreKey], + [][]byte{ + staking.UnbondingQueueKey, staking.RedelegationQueueKey, staking.ValidatorQueueKey, + staking.HistoricalInfoKey, + }}, // ordering may change but it doesn't matter + {app.keys[slashing.StoreKey], newApp.keys[slashing.StoreKey], [][]byte{}}, + {app.keys[mint.StoreKey], newApp.keys[mint.StoreKey], [][]byte{}}, + {app.keys[distr.StoreKey], newApp.keys[distr.StoreKey], [][]byte{}}, + {app.keys[supply.StoreKey], newApp.keys[supply.StoreKey], [][]byte{}}, + {app.keys[params.StoreKey], newApp.keys[params.StoreKey], [][]byte{}}, + {app.keys[gov.StoreKey], newApp.keys[gov.StoreKey], [][]byte{}}, + } + + for _, skp := range storeKeysPrefixes { + storeA := ctxA.KVStore(skp.A) + storeB := ctxB.KVStore(skp.B) + + failedKVAs, failedKVBs := sdk.DiffKVStores(storeA, storeB, skp.Prefixes) + require.Equal(t, len(failedKVAs), len(failedKVBs), "unequal sets of key-values to compare") + + fmt.Printf("compared %d key/value pairs between %s and %s\n", len(failedKVAs), skp.A, skp.B) + require.Equal(t, len(failedKVAs), 0, simapp.GetSimulationLog(skp.A.Name(), app.SimulationManager().StoreDecoders, app.Codec(), failedKVAs, failedKVBs)) + } +} + +func TestAppSimulationAfterImport(t *testing.T) { + config, db, dir, logger, skip, err := simapp.SetupSimulation("leveldb-app-sim", "Simulation") + if skip { + t.Skip("skipping application simulation after import") + } + require.NoError(t, err, "simulation setup failed") + + defer func() { + db.Close() + require.NoError(t, os.RemoveAll(dir)) + }() + + app := NewEthermintApp(logger, db, nil, true, map[int64]bool{}, simapp.FlagPeriodValue, fauxMerkleModeOpt) + require.Equal(t, appName, app.Name()) + + // Run randomized simulation + stopEarly, simParams, simErr := simulation.SimulateFromSeed( + t, os.Stdout, app.BaseApp, simapp.AppStateFn(app.Codec(), app.SimulationManager()), + simapp.SimulationOperations(app, app.Codec(), config), + app.ModuleAccountAddrs(), config, + ) + + // export state and simParams before the simulation error is checked + err = simapp.CheckExportSimulation(app, config, simParams) + require.NoError(t, err) + require.NoError(t, simErr) + + if config.Commit { + simapp.PrintStats(db) + } + + if stopEarly { + fmt.Println("can't export or import a zero-validator genesis, exiting test...") + return + } + + fmt.Printf("exporting genesis...\n") + + appState, _, err := app.ExportAppStateAndValidators(true, []string{}) + require.NoError(t, err) + + fmt.Printf("importing genesis...\n") + + // nolint: dosgsled + _, newDB, newDir, _, _, err := simapp.SetupSimulation("leveldb-app-sim-2", "Simulation-2") + require.NoError(t, err, "simulation setup failed") + + defer func() { + newDB.Close() + require.NoError(t, os.RemoveAll(newDir)) + }() + + newApp := NewEthermintApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, simapp.FlagPeriodValue, fauxMerkleModeOpt) + require.Equal(t, appName, newApp.Name()) + + newApp.InitChain(abci.RequestInitChain{ + AppStateBytes: appState, + }) + + _, _, err = simulation.SimulateFromSeed( + t, os.Stdout, newApp.BaseApp, simapp.AppStateFn(app.Codec(), app.SimulationManager()), + simapp.SimulationOperations(newApp, newApp.Codec(), config), + newApp.ModuleAccountAddrs(), config, + ) + require.NoError(t, err) +} + +func TestAppStateDeterminism(t *testing.T) { + if !simapp.FlagEnabledValue { + t.Skip("skipping application simulation") + } + + config := simapp.NewConfigFromFlags() + config.InitialBlockHeight = 1 + config.ExportParamsPath = "" + config.OnOperation = false + config.AllInvariants = false + config.ChainID = helpers.SimAppChainID + + numTimesToRunPerSeed := 2 + appHashList := make([]json.RawMessage, numTimesToRunPerSeed) + + config.Seed = rand.Int63() + + for i := 0; i < numTimesToRunPerSeed; i++ { + var logger log.Logger + if simapp.FlagVerboseValue { + logger = log.TestingLogger() + } else { + logger = log.NewNopLogger() + } + + db := dbm.NewMemDB() + + app := NewEthermintApp(logger, db, nil, true, map[int64]bool{}, simapp.FlagPeriodValue, interBlockCacheOpt()) + + fmt.Printf( + "running non-determinism simulation; seed %d: attempt: %d/%d\n", + config.Seed, i+1, numTimesToRunPerSeed, + ) + + _, _, err := simulation.SimulateFromSeed( + t, os.Stdout, app.BaseApp, simapp.AppStateFn(app.Codec(), app.SimulationManager()), + simapp.SimulationOperations(app, app.Codec(), config), + app.ModuleAccountAddrs(), config, + ) + require.NoError(t, err) + + if config.Commit { + simapp.PrintStats(db) + } + + appHash := app.LastCommitID().Hash + appHashList[i] = appHash + + if i != 0 { + require.Equal( + t, appHashList[0], appHashList[i], + "non-determinism in seed %d: %d/%d, attempt: %d/%d\n", config.Seed, i+1, numTimesToRunPerSeed, + ) + } + } +} diff --git a/app/test_helpers.go b/app/test_helpers.go new file mode 100644 index 0000000000..b23f94457e --- /dev/null +++ b/app/test_helpers.go @@ -0,0 +1,34 @@ +package app + +import ( + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/codec" +) + +// Setup initializes a new EthermintApp. A Nop logger is set in EthermintApp. +func Setup(isCheckTx bool) *EthermintApp { + db := dbm.NewMemDB() + app := NewEthermintApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, 0) + + if !isCheckTx { + // init chain must be called to stop deliverState from being nil + genesisState := NewDefaultGenesisState() + stateBytes, err := codec.MarshalJSONIndent(app.Codec(), genesisState) + if err != nil { + panic(err) + } + + // Initialize the chain + app.InitChain( + abci.RequestInitChain{ + Validators: []abci.ValidatorUpdate{}, + AppStateBytes: stateBytes, + }, + ) + } + + return app +} diff --git a/app/test_utils.go b/app/test_utils.go deleted file mode 100644 index 50baa54501..0000000000 --- a/app/test_utils.go +++ /dev/null @@ -1,126 +0,0 @@ -package app - -import ( - "fmt" - "math/big" - "time" - - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - - "github.com/cosmos/ethermint/crypto" - "github.com/cosmos/ethermint/types" - evmtypes "github.com/cosmos/ethermint/x/evm/types" - - ethcrypto "github.com/ethereum/go-ethereum/crypto" - - abci "github.com/tendermint/tendermint/abci/types" - tmcrypto "github.com/tendermint/tendermint/crypto" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" -) - -type testSetup struct { - ctx sdk.Context - cdc *codec.Codec - accKeeper auth.AccountKeeper - feeKeeper auth.FeeCollectionKeeper - anteHandler sdk.AnteHandler -} - -func newTestSetup() testSetup { - db := dbm.NewMemDB() - authCapKey := sdk.NewKVStoreKey("authCapKey") - feeCapKey := sdk.NewKVStoreKey("feeCapKey") - keyParams := sdk.NewKVStoreKey("params") - tkeyParams := sdk.NewTransientStoreKey("transient_params") - - ms := store.NewCommitMultiStore(db) - ms.MountStoreWithDB(authCapKey, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(feeCapKey, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeIAVL, db) - ms.LoadLatestVersion() - - cdc := CreateCodec() - cdc.RegisterConcrete(&sdk.TestMsg{}, "test/TestMsg", nil) - - accKeeper := auth.NewAccountKeeper(cdc, authCapKey, auth.ProtoBaseAccount) - feeKeeper := auth.NewFeeCollectionKeeper(cdc, feeCapKey) - anteHandler := NewAnteHandler(accKeeper, feeKeeper) - - ctx := sdk.NewContext( - ms, - abci.Header{ChainID: "3", Time: time.Now().UTC()}, - true, - log.NewNopLogger(), - ) - - return testSetup{ - ctx: ctx, - cdc: cdc, - accKeeper: accKeeper, - feeKeeper: feeKeeper, - anteHandler: anteHandler, - } -} - -func newTestMsg(addrs ...sdk.AccAddress) *sdk.TestMsg { - return sdk.NewTestMsg(addrs...) -} - -func newTestCoins() sdk.Coins { - return sdk.Coins{sdk.NewInt64Coin(types.DenomDefault, 500000000)} -} - -func newTestStdFee() auth.StdFee { - return auth.NewStdFee(220000, sdk.NewInt64Coin(types.DenomDefault, 150)) -} - -// GenerateAddress generates an Ethereum address. -func newTestAddrKey() (sdk.AccAddress, tmcrypto.PrivKey) { - privkey, _ := crypto.GenerateKey() - addr := ethcrypto.PubkeyToAddress(privkey.PublicKey) - - return sdk.AccAddress(addr.Bytes()), privkey -} - -func newTestSDKTx( - ctx sdk.Context, msgs []sdk.Msg, privs []tmcrypto.PrivKey, - accNums []uint64, seqs []uint64, fee auth.StdFee, -) sdk.Tx { - - sigs := make([]auth.StdSignature, len(privs)) - for i, priv := range privs { - signBytes := auth.StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "") - - sig, err := priv.Sign(signBytes) - if err != nil { - panic(err) - } - - sigs[i] = auth.StdSignature{ - PubKey: priv.PubKey(), - Signature: sig, - } - } - - return auth.NewStdTx(msgs, fee, sigs, "") -} - -func newTestEthTx(ctx sdk.Context, msg *evmtypes.EthereumTxMsg, priv tmcrypto.PrivKey) sdk.Tx { - chainID, ok := new(big.Int).SetString(ctx.ChainID(), 10) - if !ok { - panic(fmt.Sprintf("invalid chainID: %s", ctx.ChainID())) - } - - privkey, ok := priv.(crypto.PrivKeySecp256k1) - if !ok { - panic(fmt.Sprintf("invalid private key type: %T", priv)) - } - - msg.Sign(chainID, privkey.ToECDSA()) - return msg -} diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 0000000000..a0b081889b --- /dev/null +++ b/buf.yaml @@ -0,0 +1,20 @@ +build: + roots: + - . +lint: + use: + - DEFAULT + - COMMENTS + - FILE_LOWER_SNAKE_CASE + except: + - UNARY_RPC + - COMMENT_FIELD + - PACKAGE_DIRECTORY_MATCH + ignore: + - third_party + - codec/testdata +breaking: + use: + - FILE + ignore: + - third_party diff --git a/client/config.go b/client/config.go new file mode 100644 index 0000000000..1069e5c0f1 --- /dev/null +++ b/client/config.go @@ -0,0 +1,63 @@ +package client + +import ( + "fmt" + "os" + "path" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/client/flags" + + ethermint "github.com/cosmos/ethermint/types" +) + +// InitConfig adds the chain-id, encoding and output flags to the persistent flag set. +func InitConfig(cmd *cobra.Command) error { + home, err := cmd.PersistentFlags().GetString(cli.HomeFlag) + if err != nil { + return err + } + + configFile := path.Join(home, "config", "config.toml") + if _, err := os.Stat(configFile); err == nil { + viper.SetConfigFile(configFile) + + if err := viper.ReadInConfig(); err != nil { + return err + } + } + + if err := viper.BindPFlag(flags.FlagChainID, cmd.PersistentFlags().Lookup(flags.FlagChainID)); err != nil { + return err + } + + if err := viper.BindPFlag(cli.EncodingFlag, cmd.PersistentFlags().Lookup(cli.EncodingFlag)); err != nil { + return err + } + + return viper.BindPFlag(cli.OutputFlag, cmd.PersistentFlags().Lookup(cli.OutputFlag)) +} + +// ValidateChainID wraps a cobra command with a RunE function with base 10 integer chain-id verification. +func ValidateChainID(baseCmd *cobra.Command) *cobra.Command { + // Copy base run command to be used after chain verification + baseRunE := baseCmd.RunE + + // Function to replace command's RunE function + validateFn := func(cmd *cobra.Command, args []string) error { + chainID := viper.GetString(flags.FlagChainID) + + if !ethermint.IsValidChainID(chainID) { + return fmt.Errorf("invalid chain-id format: %s", chainID) + } + + return baseRunE(cmd, args) + } + + baseCmd.RunE = validateFn + return baseCmd +} diff --git a/client/export.go b/client/export.go new file mode 100644 index 0000000000..a90dcead98 --- /dev/null +++ b/client/export.go @@ -0,0 +1,81 @@ +package client + +import ( + "bufio" + "fmt" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/ethereum/go-ethereum/common/hexutil" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/input" + "github.com/cosmos/cosmos-sdk/crypto/keys" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ethermint/crypto" +) + +// UnsafeExportEthKeyCommand exports a key with the given name as a private key in hex format. +func UnsafeExportEthKeyCommand() *cobra.Command { + return &cobra.Command{ + Use: "unsafe-export-eth-key [name]", + Short: "**UNSAFE** Export an Ethereum private key", + Long: `**UNSAFE** Export an Ethereum private key unencrypted to use in dev tooling`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) + + kb, err := keys.NewKeyring( + sdk.KeyringServiceName(), + viper.GetString(flags.FlagKeyringBackend), + viper.GetString(flags.FlagHome), + inBuf, + crypto.EthSecp256k1Options()..., + ) + if err != nil { + return err + } + + decryptPassword := "" + conf := true + keyringBackend := viper.GetString(flags.FlagKeyringBackend) + switch keyringBackend { + case keys.BackendFile: + decryptPassword, err = input.GetPassword( + "**WARNING this is an unsafe way to export your unencrypted private key**\nEnter key password:", + inBuf) + case keys.BackendOS: + conf, err = input.GetConfirmation( + "**WARNING** this is an unsafe way to export your unencrypted private key, are you sure?", + inBuf) + } + if err != nil || !conf { + return err + } + + // Exports private key from keybase using password + privKey, err := kb.ExportPrivateKeyObject(args[0], decryptPassword) + if err != nil { + return err + } + + // Converts key to Ethermint secp256 implementation + emintKey, ok := privKey.(crypto.PrivKeySecp256k1) + if !ok { + return fmt.Errorf("invalid private key type, must be Ethereum key: %T", privKey) + } + + // Formats key for output + privB := ethcrypto.FromECDSA(emintKey.ToECDSA()) + keyS := strings.ToUpper(hexutil.Encode(privB)[2:]) + + fmt.Println(keyS) + + return nil + }, + } +} diff --git a/client/keys.go b/client/keys.go new file mode 100644 index 0000000000..22810e67d4 --- /dev/null +++ b/client/keys.go @@ -0,0 +1,88 @@ +package client + +import ( + "bufio" + "io" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/client/flags" + clientkeys "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/crypto/keys" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ethermint/crypto" +) + +const ( + flagDryRun = "dry-run" +) + +// KeyCommands registers a sub-tree of commands to interact with +// local private key storage. +func KeyCommands() *cobra.Command { + cmd := &cobra.Command{ + Use: "keys", + Short: "Add or view local private keys", + Long: `Keys allows you to manage your local keystore for tendermint. + + These keys may be in any format supported by go-crypto and can be + used by light-clients, full nodes, or any other application that + needs to sign with a private key.`, + } + + // support adding Ethereum supported keys + addCmd := clientkeys.AddKeyCommand() + + // update the default signing algorithm value to "eth_secp256k1" + algoFlag := addCmd.Flag("algo") + algoFlag.DefValue = string(crypto.EthSecp256k1) + err := algoFlag.Value.Set(string(crypto.EthSecp256k1)) + if err != nil { + panic(err) + } + addCmd.RunE = runAddCmd + + cmd.AddCommand( + clientkeys.MnemonicKeyCommand(), + addCmd, + clientkeys.ExportKeyCommand(), + clientkeys.ImportKeyCommand(), + clientkeys.ListKeysCmd(), + clientkeys.ShowKeysCmd(), + flags.LineBreak, + clientkeys.DeleteKeyCommand(), + clientkeys.ParseKeyStringCommand(), + clientkeys.MigrateCommand(), + flags.LineBreak, + UnsafeExportEthKeyCommand(), + ) + return cmd +} + +func runAddCmd(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) + kb, err := getKeybase(viper.GetBool(flagDryRun), inBuf) + if err != nil { + return err + } + + return clientkeys.RunAddCmd(cmd, args, kb, inBuf) +} + +func getKeybase(transient bool, buf io.Reader) (keys.Keybase, error) { + if transient { + return keys.NewInMemory( + crypto.EthSecp256k1Options()..., + ), nil + } + + return keys.NewKeyring( + sdk.KeyringServiceName(), + viper.GetString(flags.FlagKeyringBackend), + viper.GetString(flags.FlagHome), + buf, + crypto.EthSecp256k1Options()..., + ) +} diff --git a/client/testnet.go b/client/testnet.go new file mode 100644 index 0000000000..3874ab7d5b --- /dev/null +++ b/client/testnet.go @@ -0,0 +1,468 @@ +package client + +// DONTCOVER + +import ( + "bufio" + "encoding/json" + "fmt" + "net" + "os" + "path/filepath" + + ethcrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/spf13/cobra" + tmconfig "github.com/tendermint/tendermint/config" + tmcrypto "github.com/tendermint/tendermint/crypto" + tmos "github.com/tendermint/tendermint/libs/os" + tmrand "github.com/tendermint/tendermint/libs/rand" + tmtypes "github.com/tendermint/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" + + "github.com/cosmos/cosmos-sdk/client/flags" + clientkeys "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/server" + srvconfig "github.com/cosmos/cosmos-sdk/server/config" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/crisis" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/mint" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/cosmos/ethermint/crypto" + ethermint "github.com/cosmos/ethermint/types" + evmtypes "github.com/cosmos/ethermint/x/evm/types" +) + +var ( + flagNodeDirPrefix = "node-dir-prefix" + flagNumValidators = "v" + flagOutputDir = "output-dir" + flagNodeDaemonHome = "node-daemon-home" + flagNodeCLIHome = "node-cli-home" + flagStartingIPAddress = "starting-ip-address" + flagCoinDenom = "coin-denom" + flagKeyAlgo = "algo" + flagIPAddrs = "ip-addrs" +) + +const nodeDirPerm = 0755 + +// TestnetCmd initializes all files for tendermint testnet and application +func TestnetCmd(ctx *server.Context, cdc *codec.Codec, + mbm module.BasicManager, genAccIterator authtypes.GenesisAccountIterator, +) *cobra.Command { + cmd := &cobra.Command{ + Use: "testnet", + Short: "Initialize files for a Ethermint testnet", + Long: `testnet will create "v" number of directories and populate each with +necessary files (private validator, genesis, config, etc.). + +Note, strict routability for addresses is turned off in the config file.`, + + Example: "ethermintd testnet --v 4 --keyring-backend test --output-dir ./output --starting-ip-address 192.168.10.2", + RunE: func(cmd *cobra.Command, _ []string) error { + config := ctx.Config + + outputDir, _ := cmd.Flags().GetString(flagOutputDir) + keyringBackend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend) + chainID, _ := cmd.Flags().GetString(flags.FlagChainID) + minGasPrices, _ := cmd.Flags().GetString(server.FlagMinGasPrices) + nodeDirPrefix, _ := cmd.Flags().GetString(flagNodeDirPrefix) + nodeDaemonHome, _ := cmd.Flags().GetString(flagNodeDaemonHome) + nodeCLIHome, _ := cmd.Flags().GetString(flagNodeCLIHome) + startingIPAddress, _ := cmd.Flags().GetString(flagStartingIPAddress) + ipAddresses, _ := cmd.Flags().GetStringSlice(flagIPAddrs) + numValidators, _ := cmd.Flags().GetInt(flagNumValidators) + coinDenom, _ := cmd.Flags().GetString(flagCoinDenom) + algo, _ := cmd.Flags().GetString(flagKeyAlgo) + + return InitTestnet( + cmd, config, cdc, mbm, genAccIterator, outputDir, chainID, coinDenom, minGasPrices, + nodeDirPrefix, nodeDaemonHome, nodeCLIHome, startingIPAddress, ipAddresses, keyringBackend, algo, numValidators, + ) + }, + } + + cmd.Flags().Int(flagNumValidators, 4, "Number of validators to initialize the testnet with") + cmd.Flags().StringP(flagOutputDir, "o", "./build", "Directory to store initialization data for the testnet") + cmd.Flags().String(flagNodeDirPrefix, "node", "Prefix the directory name for each node with (node results in node0, node1, ...)") + cmd.Flags().String(flagNodeDaemonHome, "ethermintd", "Home directory of the node's daemon configuration") + cmd.Flags().String(flagNodeCLIHome, "ethermintcli", "Home directory of the node's cli configuration") + cmd.Flags().String(flagStartingIPAddress, "192.168.0.1", "Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)") + cmd.Flags().StringSlice(flagIPAddrs, []string{}, "List of IP addresses to use (i.e. `192.168.0.1,172.168.0.1` results in persistent peers list ID0@192.168.0.1:46656, ID1@172.168.0.1)") + cmd.Flags().String(flags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") + cmd.Flags().String(flagCoinDenom, ethermint.AttoPhoton, "Coin denomination used for staking, governance, mint, crisis and evm parameters") + cmd.Flags().String(server.FlagMinGasPrices, fmt.Sprintf("0.000006%s", ethermint.AttoPhoton), "Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01aphoton,0.001stake)") + cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)") + cmd.Flags().String(flagKeyAlgo, string(crypto.EthSecp256k1), "Key signing algorithm to generate keys for") + return cmd +} + +// InitTestnet initializes the testnet configuration +func InitTestnet( + cmd *cobra.Command, + config *tmconfig.Config, + cdc *codec.Codec, + mbm module.BasicManager, + genAccIterator authtypes.GenesisAccountIterator, + outputDir, + chainID, + coinDenom, + minGasPrices, + nodeDirPrefix, + nodeDaemonHome, + nodeCLIHome, + startingIPAddress string, + ipAddresses []string, + keyringBackend, + algo string, + numValidators int, +) error { + + if chainID == "" { + chainID = fmt.Sprintf("ethermint-%d", tmrand.Int63n(9999999999999)+1) + } + + if !ethermint.IsValidChainID(chainID) { + return fmt.Errorf("invalid chain-id: %s", chainID) + } + + if err := sdk.ValidateDenom(coinDenom); err != nil { + return err + } + + if len(ipAddresses) != 0 { + numValidators = len(ipAddresses) + } + + nodeIDs := make([]string, numValidators) + valPubKeys := make([]tmcrypto.PubKey, numValidators) + + simappConfig := srvconfig.DefaultConfig() + simappConfig.MinGasPrices = minGasPrices + + var ( + genAccounts []authexported.GenesisAccount + genFiles []string + ) + + inBuf := bufio.NewReader(cmd.InOrStdin()) + // generate private keys, node IDs, and initial transactions + for i := 0; i < numValidators; i++ { + nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) + nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome) + clientDir := filepath.Join(outputDir, nodeDirName, nodeCLIHome) + gentxsDir := filepath.Join(outputDir, "gentxs") + + config.SetRoot(nodeDir) + config.RPC.ListenAddress = "tcp://0.0.0.0:26657" + + if err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm); err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + if err := os.MkdirAll(clientDir, nodeDirPerm); err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + config.Moniker = nodeDirName + + var ip string + var err error + if len(ipAddresses) == 0 { + ip, err = getIP(i, startingIPAddress) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } + } else { + ip = ipAddresses[i] + } + + nodeIDs[i], valPubKeys[i], err = genutil.InitializeNodeValidatorFiles(config) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip) + genFiles = append(genFiles, config.GenesisFile()) + + kb, err := keys.NewKeyring( + sdk.KeyringServiceName(), + keyringBackend, + clientDir, + inBuf, + crypto.EthSecp256k1Options()..., + ) + if err != nil { + return err + } + + cmd.Printf( + "Password for account '%s' :\n", nodeDirName, + ) + + keyPass := clientkeys.DefaultKeyPass + addr, secret, err := GenerateSaveCoinKey(kb, nodeDirName, keyPass, true, keys.SigningAlgo(algo)) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + info := map[string]string{"secret": secret} + + cliPrint, err := json.Marshal(info) + if err != nil { + return err + } + + // save private key seed words + if err := writeFile(fmt.Sprintf("%v.json", "key_seed"), clientDir, cliPrint); err != nil { + return err + } + + accStakingTokens := sdk.TokensFromConsensusPower(5000) + coins := sdk.NewCoins( + sdk.NewCoin(coinDenom, accStakingTokens), + ) + + genAccounts = append(genAccounts, ethermint.EthAccount{ + BaseAccount: authtypes.NewBaseAccount(addr, coins, nil, 0, 0), + CodeHash: ethcrypto.Keccak256(nil), + }) + + valTokens := sdk.TokensFromConsensusPower(100) + msg := stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(addr), + valPubKeys[i], + sdk.NewCoin(coinDenom, valTokens), + stakingtypes.NewDescription(nodeDirName, "", "", "", ""), + stakingtypes.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()), + sdk.OneInt(), + ) + + tx := authtypes.NewStdTx([]sdk.Msg{msg}, authtypes.StdFee{}, []authtypes.StdSignature{}, memo) //nolint:staticcheck // SA1019: authtypes.StdFee is deprecated + txBldr := authtypes.NewTxBuilderFromCLI(inBuf).WithChainID(chainID).WithMemo(memo).WithKeybase(kb) + + signedTx, err := txBldr.SignStdTx(nodeDirName, clientkeys.DefaultKeyPass, tx, false) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + txBytes, err := cdc.MarshalJSON(signedTx) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + // gather gentxs folder + if err := writeFile(fmt.Sprintf("%v.json", nodeDirName), gentxsDir, txBytes); err != nil { + _ = os.RemoveAll(outputDir) + return err + } + + srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config/app.toml"), simappConfig) + } + + if err := initGenFiles(cdc, mbm, chainID, coinDenom, genAccounts, genFiles, numValidators); err != nil { + return err + } + + err := collectGenFiles( + cdc, config, chainID, nodeIDs, valPubKeys, numValidators, + outputDir, nodeDirPrefix, nodeDaemonHome, genAccIterator, + ) + if err != nil { + return err + } + + cmd.PrintErrf("Successfully initialized %d node directories\n", numValidators) + return nil +} + +func initGenFiles( + cdc *codec.Codec, mbm module.BasicManager, + chainID, coinDenom string, + genAccounts []authexported.GenesisAccount, + genFiles []string, numValidators int, +) error { + + appGenState := mbm.DefaultGenesis() + + // set the accounts in the genesis state + var authGenState authtypes.GenesisState + cdc.MustUnmarshalJSON(appGenState[authtypes.ModuleName], &authGenState) + + authGenState.Accounts = genAccounts + appGenState[authtypes.ModuleName] = cdc.MustMarshalJSON(authGenState) + + var stakingGenState stakingtypes.GenesisState + cdc.MustUnmarshalJSON(appGenState[stakingtypes.ModuleName], &stakingGenState) + + stakingGenState.Params.BondDenom = coinDenom + appGenState[stakingtypes.ModuleName] = cdc.MustMarshalJSON(stakingGenState) + + var govGenState govtypes.GenesisState + cdc.MustUnmarshalJSON(appGenState[govtypes.ModuleName], &govGenState) + + govGenState.DepositParams.MinDeposit[0].Denom = coinDenom + appGenState[govtypes.ModuleName] = cdc.MustMarshalJSON(govGenState) + + var mintGenState mint.GenesisState + cdc.MustUnmarshalJSON(appGenState[mint.ModuleName], &mintGenState) + + mintGenState.Params.MintDenom = coinDenom + appGenState[mint.ModuleName] = cdc.MustMarshalJSON(mintGenState) + + var crisisGenState crisis.GenesisState + cdc.MustUnmarshalJSON(appGenState[crisis.ModuleName], &crisisGenState) + + crisisGenState.ConstantFee.Denom = coinDenom + appGenState[crisis.ModuleName] = cdc.MustMarshalJSON(crisisGenState) + + var evmGenState evmtypes.GenesisState + cdc.MustUnmarshalJSON(appGenState[evmtypes.ModuleName], &evmGenState) + + evmGenState.Params.EvmDenom = coinDenom + appGenState[evmtypes.ModuleName] = cdc.MustMarshalJSON(evmGenState) + + appGenStateJSON, err := codec.MarshalJSONIndent(cdc, appGenState) + if err != nil { + return err + } + + genDoc := tmtypes.GenesisDoc{ + ChainID: chainID, + AppState: appGenStateJSON, + Validators: nil, + } + + // generate empty genesis files for each validator and save + for i := 0; i < numValidators; i++ { + if err := genDoc.SaveAs(genFiles[i]); err != nil { + return err + } + } + return nil +} + +// GenerateSaveCoinKey returns the address of a public key, along with the secret +// phrase to recover the private key. +func GenerateSaveCoinKey(keybase keys.Keybase, keyName, keyPass string, overwrite bool, algo keys.SigningAlgo) (sdk.AccAddress, string, error) { + // ensure no overwrite + if !overwrite { + _, err := keybase.Get(keyName) + if err == nil { + return sdk.AccAddress([]byte{}), "", fmt.Errorf( + "key already exists, overwrite is disabled") + } + } + + // generate a private key, with recovery phrase + info, secret, err := keybase.CreateMnemonic(keyName, keys.English, keyPass, algo) + if err != nil { + return sdk.AccAddress([]byte{}), "", err + } + + return sdk.AccAddress(info.GetPubKey().Address()), secret, nil +} + +func collectGenFiles( + cdc *codec.Codec, config *tmconfig.Config, chainID string, + nodeIDs []string, valPubKeys []tmcrypto.PubKey, + numValidators int, outputDir, nodeDirPrefix, nodeDaemonHome string, + genAccIterator authtypes.GenesisAccountIterator, +) error { + + var appState json.RawMessage + genTime := tmtime.Now() + + for i := 0; i < numValidators; i++ { + nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) + nodeDir := filepath.Join(outputDir, nodeDirName, nodeDaemonHome) + gentxsDir := filepath.Join(outputDir, "gentxs") + config.Moniker = nodeDirName + + config.SetRoot(nodeDir) + + nodeID, valPubKey := nodeIDs[i], valPubKeys[i] + initCfg := genutiltypes.NewInitConfig(chainID, gentxsDir, nodeID, nodeID, valPubKey) + + genDoc, err := tmtypes.GenesisDocFromFile(config.GenesisFile()) + if err != nil { + return err + } + + nodeAppState, err := genutil.GenAppStateFromConfig(cdc, config, initCfg, *genDoc, genAccIterator) + if err != nil { + return err + } + + if appState == nil { + // set the canonical application state (they should not differ) + appState = nodeAppState + } + + genFile := config.GenesisFile() + + // overwrite each validator's genesis file to have a canonical genesis time + if err := genutil.ExportGenesisFileWithTime(genFile, chainID, nil, appState, genTime); err != nil { + return err + } + } + + return nil +} + +func getIP(i int, startingIPAddr string) (ip string, err error) { + if len(startingIPAddr) == 0 { + ip, err = server.ExternalIP() + if err != nil { + return "", err + } + return ip, nil + } + return calculateIP(startingIPAddr, i) +} + +func calculateIP(ip string, i int) (string, error) { + ipv4 := net.ParseIP(ip).To4() + if ipv4 == nil { + return "", fmt.Errorf("%v: non ipv4 address", ip) + } + + for j := 0; j < i; j++ { + ipv4[3]++ + } + + return ipv4.String(), nil +} + +func writeFile(name string, dir string, contents []byte) error { + writePath := filepath.Join(dir) + file := filepath.Join(writePath, name) + + err := tmos.EnsureDir(writePath, 0755) + if err != nil { + return err + } + + err = tmos.WriteFile(file, contents, 0644) + if err != nil { + return err + } + + return nil +} diff --git a/cmd/emintcli/main.go b/cmd/emintcli/main.go deleted file mode 100644 index e769e031cc..0000000000 --- a/cmd/emintcli/main.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -func main() { - // TODO: Implement CLI commands and logic - // - // Ref: https://github.com/cosmos/ethermint/issues/432 -} diff --git a/cmd/emintd/main.go b/cmd/emintd/main.go deleted file mode 100644 index 331768405a..0000000000 --- a/cmd/emintd/main.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -func main() { - // TODO: Implement daemon command and logic - // - // Ref: https://github.com/cosmos/ethermint/issues/433 -} diff --git a/cmd/ethermintcli/main.go b/cmd/ethermintcli/main.go new file mode 100644 index 0000000000..6fe3505ba8 --- /dev/null +++ b/cmd/ethermintcli/main.go @@ -0,0 +1,142 @@ +package main + +import ( + "fmt" + + "github.com/spf13/cobra" + + tmamino "github.com/tendermint/tendermint/crypto/encoding/amino" + "github.com/tendermint/tendermint/libs/cli" + + sdkclient "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + clientkeys "github.com/cosmos/cosmos-sdk/client/keys" + clientrpc "github.com/cosmos/cosmos-sdk/client/rpc" + sdkcodec "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/auth" + authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + "github.com/cosmos/cosmos-sdk/x/bank" + bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" + + "github.com/cosmos/ethermint/app" + "github.com/cosmos/ethermint/client" + "github.com/cosmos/ethermint/codec" + "github.com/cosmos/ethermint/crypto" + "github.com/cosmos/ethermint/rpc" + ethermint "github.com/cosmos/ethermint/types" +) + +var ( + cdc = codec.MakeCodec(app.ModuleBasics) +) + +func main() { + // Configure cobra to sort commands + cobra.EnableCommandSorting = false + + tmamino.RegisterKeyType(crypto.PubKeySecp256k1{}, crypto.PubKeyAminoName) + tmamino.RegisterKeyType(crypto.PrivKeySecp256k1{}, crypto.PrivKeyAminoName) + + keys.CryptoCdc = cdc + clientkeys.KeysCdc = cdc + + // Read in the configuration file for the sdk + config := sdk.GetConfig() + ethermint.SetBech32Prefixes(config) + ethermint.SetBip44CoinType(config) + config.Seal() + + rootCmd := &cobra.Command{ + Use: "ethermintcli", + Short: "Command line interface for interacting with ethermintd", + } + + // Add --chain-id to persistent flags and mark it required + rootCmd.PersistentFlags().String(flags.FlagChainID, "", "Chain ID of tendermint node") + rootCmd.PersistentPreRunE = func(_ *cobra.Command, _ []string) error { + return client.InitConfig(rootCmd) + } + + // Construct Root Command + rootCmd.AddCommand( + clientrpc.StatusCommand(), + sdkclient.ConfigCmd(app.DefaultCLIHome), + queryCmd(cdc), + txCmd(cdc), + client.ValidateChainID( + rpc.EmintServeCmd(cdc), + ), + flags.LineBreak, + client.KeyCommands(), + flags.LineBreak, + version.Cmd, + flags.NewCompletionCmd(rootCmd, true), + ) + + // Add flags and prefix all env exposed with EM + executor := cli.PrepareMainCmd(rootCmd, "EM", app.DefaultCLIHome) + + err := executor.Execute() + if err != nil { + panic(fmt.Errorf("failed executing CLI command: %w", err)) + } +} + +func queryCmd(cdc *sdkcodec.Codec) *cobra.Command { + queryCmd := &cobra.Command{ + Use: "query", + Aliases: []string{"q"}, + Short: "Querying subcommands", + } + + queryCmd.AddCommand( + authcmd.GetAccountCmd(cdc), + flags.LineBreak, + authcmd.QueryTxsByEventsCmd(cdc), + authcmd.QueryTxCmd(cdc), + flags.LineBreak, + ) + + // add modules' query commands + app.ModuleBasics.AddQueryCommands(queryCmd, cdc) + + return queryCmd +} + +func txCmd(cdc *sdkcodec.Codec) *cobra.Command { + txCmd := &cobra.Command{ + Use: "tx", + Short: "Transactions subcommands", + } + + txCmd.AddCommand( + bankcmd.SendTxCmd(cdc), + flags.LineBreak, + authcmd.GetSignCommand(cdc), + authcmd.GetMultiSignCommand(cdc), + flags.LineBreak, + authcmd.GetBroadcastCommand(cdc), + authcmd.GetEncodeCommand(cdc), + authcmd.GetDecodeCommand(cdc), + flags.LineBreak, + ) + + // add modules' tx commands + app.ModuleBasics.AddTxCommands(txCmd, cdc) + + // remove auth and bank commands as they're mounted under the root tx command + var cmdsToRemove []*cobra.Command + + for _, cmd := range txCmd.Commands() { + if cmd.Use == auth.ModuleName || cmd.Use == bank.ModuleName { + cmdsToRemove = append(cmdsToRemove, cmd) + } + } + + txCmd.RemoveCommand(cmdsToRemove...) + + return txCmd +} diff --git a/cmd/ethermintd/genaccounts.go b/cmd/ethermintd/genaccounts.go new file mode 100644 index 0000000000..cd6cda7475 --- /dev/null +++ b/cmd/ethermintd/genaccounts.go @@ -0,0 +1,169 @@ +package main + +import ( + "bufio" + "errors" + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/tendermint/tendermint/libs/cli" + + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" + authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting" + "github.com/cosmos/cosmos-sdk/x/genutil" + + "github.com/cosmos/ethermint/crypto" + ethermint "github.com/cosmos/ethermint/types" + + ethcrypto "github.com/ethereum/go-ethereum/crypto" +) + +const ( + flagClientHome = "home-client" + flagVestingStart = "vesting-start-time" + flagVestingEnd = "vesting-end-time" + flagVestingAmt = "vesting-amount" +) + +// AddGenesisAccountCmd returns add-genesis-account cobra Command. +func AddGenesisAccountCmd( + ctx *server.Context, cdc *codec.Codec, defaultNodeHome, defaultClientHome string, +) *cobra.Command { + + cmd := &cobra.Command{ + Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]", + Short: "Add a genesis account to genesis.json", + Long: `Add a genesis account to genesis.json. The provided account must specify +the account address or key name and a list of initial coins. If a key name is given, +the address will be looked up in the local Keybase. The list of initial tokens must +contain valid denominations. Accounts may optionally be supplied with vesting parameters. +`, + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + config := ctx.Config + config.SetRoot(viper.GetString(cli.HomeFlag)) + + addr, err := sdk.AccAddressFromBech32(args[0]) + inBuf := bufio.NewReader(cmd.InOrStdin()) + if err != nil { + // attempt to lookup address from Keybase if no address was provided + kb, err := keys.NewKeyring( + sdk.KeyringServiceName(), + viper.GetString(flags.FlagKeyringBackend), + viper.GetString(flagClientHome), + inBuf, + crypto.EthSecp256k1Options()..., + ) + if err != nil { + return err + } + + info, err := kb.Get(args[0]) + if err != nil { + return fmt.Errorf("failed to get address from Keybase: %w", err) + } + + addr = info.GetAddress() + } + + coins, err := sdk.ParseCoins(args[1]) + if err != nil { + return fmt.Errorf("failed to parse coins: %w", err) + } + + vestingStart := viper.GetInt64(flagVestingStart) + vestingEnd := viper.GetInt64(flagVestingEnd) + vestingAmt, err := sdk.ParseCoins(viper.GetString(flagVestingAmt)) + if err != nil { + return fmt.Errorf("failed to parse vesting amount: %w", err) + } + + // create concrete account type based on input parameters + var genAccount authexported.GenesisAccount + + // balances := bank.Balance{Address: addr, Coins: coins.Sort()} + coins = coins.Sort() + baseAccount := auth.NewBaseAccount(addr, coins, nil, 0, 0) + if !vestingAmt.IsZero() { + baseVestingAccount, err := authvesting.NewBaseVestingAccount(baseAccount, vestingAmt.Sort(), vestingEnd) + if err != nil { + return err + } + + if (coins.IsZero() && !baseVestingAccount.OriginalVesting.IsZero()) || + baseVestingAccount.OriginalVesting.IsAnyGT(coins) { + return errors.New("vesting amount cannot be greater than total amount") + } + + switch { + case vestingStart != 0 && vestingEnd != 0: + genAccount = authvesting.NewContinuousVestingAccountRaw(baseVestingAccount, vestingStart) + + case vestingEnd != 0: + genAccount = authvesting.NewDelayedVestingAccountRaw(baseVestingAccount) + + default: + return errors.New("invalid vesting parameters; must supply start and end time or end time") + } + } else { + genAccount = ethermint.EthAccount{ + BaseAccount: baseAccount, + CodeHash: ethcrypto.Keccak256(nil), + } + } + + if err := genAccount.Validate(); err != nil { + return fmt.Errorf("failed to validate new genesis account: %w", err) + } + + genFile := config.GenesisFile() + appState, genDoc, err := genutil.GenesisStateFromGenFile(cdc, genFile) + if err != nil { + return fmt.Errorf("failed to unmarshal genesis state: %w", err) + } + + authGenState := auth.GetGenesisStateFromAppState(cdc, appState) + + if authGenState.Accounts.Contains(addr) { + return fmt.Errorf("cannot add account at existing address %s", addr) + } + + // Add the new account to the set of genesis accounts and sanitize the + // accounts afterwards. + authGenState.Accounts = append(authGenState.Accounts, genAccount) + authGenState.Accounts = auth.SanitizeGenesisAccounts(authGenState.Accounts) + + authGenStateBz, err := cdc.MarshalJSON(authGenState) + if err != nil { + return fmt.Errorf("failed to marshal auth genesis state: %w", err) + } + + appState[auth.ModuleName] = authGenStateBz + + appStateJSON, err := cdc.MarshalJSON(appState) + if err != nil { + return fmt.Errorf("failed to marshal application genesis state: %w", err) + } + + genDoc.AppState = appStateJSON + return genutil.ExportGenesisFile(genDoc, genFile) + }, + } + + cmd.Flags().String(cli.HomeFlag, defaultNodeHome, "node's home directory") + cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)") + cmd.Flags().String(flagClientHome, defaultClientHome, "client's home directory") + cmd.Flags().String(flagVestingAmt, "", "amount of coins for vesting accounts") + cmd.Flags().Uint64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts") + cmd.Flags().Uint64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts") + + return cmd +} diff --git a/cmd/ethermintd/main.go b/cmd/ethermintd/main.go new file mode 100644 index 0000000000..9965287e3d --- /dev/null +++ b/cmd/ethermintd/main.go @@ -0,0 +1,125 @@ +package main + +import ( + "encoding/json" + "io" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + abci "github.com/tendermint/tendermint/abci/types" + tmamino "github.com/tendermint/tendermint/crypto/encoding/amino" + "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client/flags" + clientkeys "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/server" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/cosmos/cosmos-sdk/x/staking" + + "github.com/cosmos/ethermint/app" + "github.com/cosmos/ethermint/client" + "github.com/cosmos/ethermint/codec" + "github.com/cosmos/ethermint/crypto" + ethermint "github.com/cosmos/ethermint/types" +) + +const flagInvCheckPeriod = "inv-check-period" + +var invCheckPeriod uint + +func main() { + cobra.EnableCommandSorting = false + + cdc := codec.MakeCodec(app.ModuleBasics) + + tmamino.RegisterKeyType(crypto.PubKeySecp256k1{}, crypto.PubKeyAminoName) + tmamino.RegisterKeyType(crypto.PrivKeySecp256k1{}, crypto.PrivKeyAminoName) + + keys.CryptoCdc = cdc + genutil.ModuleCdc = cdc + genutiltypes.ModuleCdc = cdc + clientkeys.KeysCdc = cdc + + config := sdk.GetConfig() + ethermint.SetBech32Prefixes(config) + ethermint.SetBip44CoinType(config) + config.Seal() + + ctx := server.NewDefaultContext() + + rootCmd := &cobra.Command{ + Use: "ethermintd", + Short: "Ethermint App Daemon (server)", + PersistentPreRunE: server.PersistentPreRunEFn(ctx), + } + // CLI commands to initialize the chain + rootCmd.AddCommand( + client.ValidateChainID( + genutilcli.InitCmd(ctx, cdc, app.ModuleBasics, app.DefaultNodeHome), + ), + genutilcli.CollectGenTxsCmd(ctx, cdc, auth.GenesisAccountIterator{}, app.DefaultNodeHome), + genutilcli.MigrateGenesisCmd(ctx, cdc), + genutilcli.GenTxCmd( + ctx, cdc, app.ModuleBasics, staking.AppModuleBasic{}, auth.GenesisAccountIterator{}, + app.DefaultNodeHome, app.DefaultCLIHome, + ), + genutilcli.ValidateGenesisCmd(ctx, cdc, app.ModuleBasics), + client.TestnetCmd(ctx, cdc, app.ModuleBasics, auth.GenesisAccountIterator{}), + // AddGenesisAccountCmd allows users to add accounts to the genesis file + AddGenesisAccountCmd(ctx, cdc, app.DefaultNodeHome, app.DefaultCLIHome), + flags.NewCompletionCmd(rootCmd, true), + ) + + // Tendermint node base commands + server.AddCommands(ctx, cdc, rootCmd, newApp, exportAppStateAndTMValidators) + + // prepare and add flags + executor := cli.PrepareBaseCmd(rootCmd, "EM", app.DefaultNodeHome) + rootCmd.PersistentFlags().UintVar(&invCheckPeriod, flagInvCheckPeriod, + 0, "Assert registered invariants every N blocks") + err := executor.Execute() + if err != nil { + panic(err) + } +} + +func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application { + return app.NewEthermintApp( + logger, + db, + traceStore, + true, + map[int64]bool{}, + 0, + baseapp.SetPruning(storetypes.NewPruningOptionsFromString(viper.GetString("pruning"))), + baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)), + baseapp.SetHaltHeight(uint64(viper.GetInt(server.FlagHaltHeight))), + ) +} + +func exportAppStateAndTMValidators( + logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string, +) (json.RawMessage, []tmtypes.GenesisValidator, error) { + + ethermintApp := app.NewEthermintApp(logger, db, traceStore, true, map[int64]bool{}, 0) + + if height != -1 { + err := ethermintApp.LoadHeight(height) + if err != nil { + return nil, nil, err + } + } + + return ethermintApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) +} diff --git a/codec/codec.go b/codec/codec.go new file mode 100644 index 0000000000..b8bf06ffaa --- /dev/null +++ b/codec/codec.go @@ -0,0 +1,31 @@ +package codec + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth/vesting" + + emintcrypto "github.com/cosmos/ethermint/crypto" + ethermint "github.com/cosmos/ethermint/types" +) + +// MakeCodec registers the necessary types and interfaces for an sdk.App. This +// codec is provided to all the modules the application depends on. +// +// NOTE: This codec will be deprecated in favor of AppCodec once all modules are +// migrated to protobuf. +func MakeCodec(bm module.BasicManager) *codec.Codec { + cdc := codec.New() + + bm.RegisterCodec(cdc) + vesting.RegisterCodec(cdc) + sdk.RegisterCodec(cdc) + emintcrypto.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + ethermint.RegisterCodec(cdc) + keys.RegisterCodec(cdc) // temporary. Used to register keyring.Info + + return cdc +} diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000000..74317c5086 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,60 @@ +# +# This codecov.yml is the default configuration for +# all repositories on Codecov. You may adjust the settings +# below in your own codecov.yml in your repository. +# +coverage: + precision: 2 + round: down + range: 70...100 + + status: + # Learn more at https://docs.codecov.io/docs/commit-status + project: + default: + threshold: 1% # allow this much decrease on project + app: + target: 70% + flags: app + modules: + target: 50% + flags: modules + core: + target: 50% + flags: core + clients: + flags: clients + changes: false + +comment: + layout: "reach, diff, files" + behavior: default # update if exists else create new + require_changes: true + +flags: + app: + paths: + - "app/" + modules: + paths: + - "x/" + - "!x/**/client/" # ignore client package + core: + paths: + - "core/" + - "crypto/" + - "types/" + clients: + paths: + - "rpc/" + - "client/" + - "x/**/client/" + +ignore: + - "docs" + - "*.md" + - "cmd" + - "x/faucet" + - "**/*.pb.go" + - "types/*.pb.go" + - "x/**/*.pb.go" diff --git a/core/chain.go b/core/chain.go index d6707950c6..1a1f28be9d 100644 --- a/core/chain.go +++ b/core/chain.go @@ -26,6 +26,8 @@ type ChainContext struct { headersByNumber map[uint64]*ethtypes.Header } +// NewChainContext generates new ChainContext based on Ethereum's core.ChainContext and +// consensus.Engine interfaces in order to process Ethereum transactions. func NewChainContext() *ChainContext { return &ChainContext{ headersByNumber: make(map[uint64]*ethtypes.Header), @@ -71,13 +73,13 @@ func (cc *ChainContext) Author(_ *ethtypes.Header) (ethcmn.Address, error) { // // TODO: Do we need to support such RPC APIs? This will tie into a bigger // discussion on if we want to support web3. -func (cc *ChainContext) APIs(_ ethcons.ChainReader) []ethrpc.API { +func (cc *ChainContext) APIs(_ ethcons.ChainHeaderReader) []ethrpc.API { return nil } // CalcDifficulty implements Ethereum's consensus.Engine interface. It currently // performs a no-op. -func (cc *ChainContext) CalcDifficulty(_ ethcons.ChainReader, _ uint64, _ *ethtypes.Header) *big.Int { +func (cc *ChainContext) CalcDifficulty(_ ethcons.ChainHeaderReader, _ uint64, _ *ethtypes.Header) *big.Int { return nil } @@ -86,9 +88,18 @@ func (cc *ChainContext) CalcDifficulty(_ ethcons.ChainReader, _ uint64, _ *ethty // // TODO: Figure out if this needs to be hooked up to any part of the ABCI? func (cc *ChainContext) Finalize( - _ ethcons.ChainReader, _ *ethtypes.Header, _ ethstate.StateDB, - _ []*ethtypes.Transaction, _ []*ethtypes.Header, _ []*ethtypes.Receipt, -) (*ethtypes.Block, error) { + _ ethcons.ChainHeaderReader, _ *ethtypes.Header, _ *ethstate.StateDB, + _ []*ethtypes.Transaction, _ []*ethtypes.Header) { +} + +// FinalizeAndAssemble runs any post-transaction state modifications (e.g. block +// rewards) and assembles the final block. +// +// Note: The block header and state database might be updated to reflect any +// consensus rules that happen at finalization (e.g. block rewards). +// TODO: Figure out if this needs to be hooked up to any part of the ABCI? +func (cc *ChainContext) FinalizeAndAssemble(_ ethcons.ChainHeaderReader, _ *ethtypes.Header, _ *ethstate.StateDB, _ []*ethtypes.Transaction, + _ []*ethtypes.Header, _ []*ethtypes.Receipt) (*ethtypes.Block, error) { return nil, nil } @@ -96,7 +107,7 @@ func (cc *ChainContext) Finalize( // performs a no-op. // // TODO: Figure out if this needs to be hooked up to any part of the ABCI? -func (cc *ChainContext) Prepare(_ ethcons.ChainReader, _ *ethtypes.Header) error { +func (cc *ChainContext) Prepare(_ ethcons.ChainHeaderReader, _ *ethtypes.Header) error { return nil } @@ -104,7 +115,7 @@ func (cc *ChainContext) Prepare(_ ethcons.ChainReader, _ *ethtypes.Header) error // performs a no-op. // // TODO: Figure out if this needs to be hooked up to any part of the ABCI? -func (cc *ChainContext) Seal(_ ethcons.ChainReader, _ *ethtypes.Block, _ chan<- *ethtypes.Block, _ <-chan struct{}) error { +func (cc *ChainContext) Seal(_ ethcons.ChainHeaderReader, _ *ethtypes.Block, _ chan<- *ethtypes.Block, _ <-chan struct{}) error { return nil } @@ -119,7 +130,7 @@ func (cc *ChainContext) SealHash(header *ethtypes.Header) ethcmn.Hash { // // TODO: Figure out if this needs to be hooked up to any part of the Cosmos SDK // handlers? -func (cc *ChainContext) VerifyHeader(_ ethcons.ChainReader, _ *ethtypes.Header, _ bool) error { +func (cc *ChainContext) VerifyHeader(_ ethcons.ChainHeaderReader, _ *ethtypes.Header, _ bool) error { return nil } @@ -128,7 +139,7 @@ func (cc *ChainContext) VerifyHeader(_ ethcons.ChainReader, _ *ethtypes.Header, // // TODO: Figure out if this needs to be hooked up to any part of the Cosmos SDK // handlers? -func (cc *ChainContext) VerifyHeaders(_ ethcons.ChainReader, _ []*ethtypes.Header, _ []bool) (chan<- struct{}, <-chan error) { +func (cc *ChainContext) VerifyHeaders(_ ethcons.ChainHeaderReader, _ []*ethtypes.Header, _ []bool) (chan<- struct{}, <-chan error) { return nil, nil } @@ -137,7 +148,7 @@ func (cc *ChainContext) VerifyHeaders(_ ethcons.ChainReader, _ []*ethtypes.Heade // // TODO: Figure out if this needs to be hooked up to any part of the Cosmos SDK // handlers? -func (cc *ChainContext) VerifySeal(_ ethcons.ChainReader, _ *ethtypes.Header) error { +func (cc *ChainContext) VerifySeal(_ ethcons.ChainHeaderReader, _ *ethtypes.Header) error { return nil } diff --git a/core/chain_test.go b/core/chain_test.go index a1f1803681..aaa81a649d 100644 --- a/core/chain_test.go +++ b/core/chain_test.go @@ -7,11 +7,12 @@ import ( "math/big" "testing" + "github.com/stretchr/testify/require" + ethcmn "github.com/ethereum/go-ethereum/common" ethcons "github.com/ethereum/go-ethereum/consensus" ethcore "github.com/ethereum/go-ethereum/core" ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/stretchr/testify/require" ) func TestChainContextInterface(t *testing.T) { @@ -71,9 +72,7 @@ func TestChainContextCalcDifficulty(t *testing.T) { func TestChainContextFinalize(t *testing.T) { cc := NewChainContext() - block, err := cc.Finalize(nil, nil, nil, nil, nil, nil) - require.Nil(t, err) - require.Nil(t, block) + cc.Finalize(nil, nil, nil, nil, nil) } func TestChainContextPrepare(t *testing.T) { diff --git a/crypto/algorithm.go b/crypto/algorithm.go new file mode 100644 index 0000000000..901ed46e5d --- /dev/null +++ b/crypto/algorithm.go @@ -0,0 +1,96 @@ +package crypto + +import ( + "fmt" + + "github.com/pkg/errors" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcutil/hdkeychain" + "github.com/tyler-smith/go-bip39" + + ethaccounts "github.com/ethereum/go-ethereum/accounts" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + + tmcrypto "github.com/tendermint/tendermint/crypto" + + "github.com/cosmos/cosmos-sdk/crypto/keys" +) + +const ( + // EthSecp256k1Type string constant for the EthSecp256k1 algorithm + EthSecp256k1Type = "eth_secp256k1" + // EthSecp256k1 defines the ECDSA secp256k1 used on Ethereum + EthSecp256k1 = keys.SigningAlgo(EthSecp256k1Type) +) + +// SupportedAlgorithms defines the list of signing algorithms used on Ethermint: +// - eth_secp256k1 (Ethereum) +// - secp256k1 (Tendermint) +var SupportedAlgorithms = []keys.SigningAlgo{EthSecp256k1, keys.Secp256k1} + +// EthSecp256k1Options defines a keys options for the ethereum Secp256k1 curve. +func EthSecp256k1Options() []keys.KeybaseOption { + return []keys.KeybaseOption{ + keys.WithKeygenFunc(EthermintKeygenFunc), + keys.WithDeriveFunc(DeriveKey), + keys.WithSupportedAlgos(SupportedAlgorithms), + keys.WithSupportedAlgosLedger(SupportedAlgorithms), + } +} + +func DeriveKey(mnemonic, bip39Passphrase, hdPath string, algo keys.SigningAlgo) ([]byte, error) { + switch algo { + case keys.Secp256k1: + return keys.StdDeriveKey(mnemonic, bip39Passphrase, hdPath, algo) + case EthSecp256k1: + return DeriveSecp256k1(mnemonic, bip39Passphrase, hdPath) + default: + return nil, errors.Wrap(keys.ErrUnsupportedSigningAlgo, string(algo)) + } +} + +// EthermintKeygenFunc is the key generation function to generate secp256k1 ToECDSA +// from ethereum. +func EthermintKeygenFunc(bz []byte, algo keys.SigningAlgo) (tmcrypto.PrivKey, error) { + if algo != EthSecp256k1 { + return nil, fmt.Errorf("signing algorithm must be %s, got %s", EthSecp256k1, algo) + } + + return PrivKeySecp256k1(bz), nil +} + +// DeriveSecp256k1 derives and returns the eth_secp256k1 private key for the given mnemonic and HD path. +func DeriveSecp256k1(mnemonic, bip39Passphrase, path string) ([]byte, error) { + hdpath, err := ethaccounts.ParseDerivationPath(path) + if err != nil { + return nil, err + } + + seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase) + if err != nil { + return nil, err + } + + masterKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams) + if err != nil { + return nil, err + } + + key := masterKey + for _, n := range hdpath { + key, err = key.Child(n) + if err != nil { + return nil, err + } + } + + privateKey, err := key.ECPrivKey() + if err != nil { + return nil, err + } + + privateKeyECDSA := privateKey.ToECDSA() + derivedKey := PrivKeySecp256k1(ethcrypto.FromECDSA(privateKeyECDSA)) + return derivedKey, nil +} diff --git a/crypto/algorithm_test.go b/crypto/algorithm_test.go new file mode 100644 index 0000000000..be911ead41 --- /dev/null +++ b/crypto/algorithm_test.go @@ -0,0 +1,123 @@ +package crypto + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + + hdwallet "github.com/miguelmota/go-ethereum-hdwallet" + + "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/keys/hd" + "github.com/cosmos/cosmos-sdk/tests" + + ethermint "github.com/cosmos/ethermint/types" +) + +func TestEthermintKeygenFunc(t *testing.T) { + privkey, err := GenerateKey() + require.NoError(t, err) + + testCases := []struct { + name string + privKey []byte + algo keys.SigningAlgo + expPass bool + }{ + { + "valid ECDSA privKey", + ethcrypto.FromECDSA(privkey.ToECDSA()), + EthSecp256k1, + true, + }, + { + "nil bytes, valid algo", + nil, + EthSecp256k1, + true, + }, + { + "empty bytes, valid algo", + []byte{}, + EthSecp256k1, + true, + }, + { + "invalid algo", + nil, + keys.MultiAlgo, + false, + }, + } + + for _, tc := range testCases { + privkey, err := EthermintKeygenFunc(tc.privKey, tc.algo) + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + require.Nil(t, privkey, tc.name) + } + } +} + +func TestKeyring(t *testing.T) { + dir, cleanup := tests.NewTestCaseDir(t) + mockIn := strings.NewReader("") + t.Cleanup(cleanup) + + kr, err := keys.NewKeyring("ethermint", keys.BackendTest, dir, mockIn, EthSecp256k1Options()...) + require.NoError(t, err) + + // fail in retrieving key + info, err := kr.Get("foo") + require.Error(t, err) + require.Nil(t, info) + + mockIn.Reset("password\npassword\n") + info, mnemonic, err := kr.CreateMnemonic("foo", keys.English, ethermint.BIP44HDPath, EthSecp256k1) + require.NoError(t, err) + require.NotEmpty(t, mnemonic) + require.Equal(t, "foo", info.GetName()) + require.Equal(t, "local", info.GetType().String()) + require.Equal(t, EthSecp256k1, info.GetAlgo()) + + params := *hd.NewFundraiserParams(0, ethermint.Bip44CoinType, 0) + hdPath := params.String() + + bz, err := DeriveKey(mnemonic, keys.DefaultBIP39Passphrase, hdPath, keys.Secp256k1) + require.NoError(t, err) + require.NotEmpty(t, bz) + + bz, err = DeriveSecp256k1(mnemonic, keys.DefaultBIP39Passphrase, hdPath) + require.NoError(t, err) + require.NotEmpty(t, bz) + + bz, err = DeriveKey(mnemonic, keys.DefaultBIP39Passphrase, hdPath, keys.SigningAlgo("")) + require.Error(t, err) + require.Empty(t, bz) + + bz, err = DeriveSecp256k1(mnemonic, keys.DefaultBIP39Passphrase, "/wrong/hdPath") + require.Error(t, err) + require.Empty(t, bz) + + bz, err = DeriveKey(mnemonic, keys.DefaultBIP39Passphrase, hdPath, EthSecp256k1) + require.NoError(t, err) + require.NotEmpty(t, bz) + + privkey := PrivKeySecp256k1(bz) + addr := common.BytesToAddress(privkey.PubKey().Address().Bytes()) + + wallet, err := hdwallet.NewFromMnemonic(mnemonic) + require.NoError(t, err) + + path := hdwallet.MustParseDerivationPath(hdPath) + + account, err := wallet.Derive(path, false) + require.NoError(t, err) + require.Equal(t, addr.String(), account.Address.String()) +} diff --git a/crypto/codec.go b/crypto/codec.go index 3c827a6ba6..02a07220cc 100644 --- a/crypto/codec.go +++ b/crypto/codec.go @@ -1,16 +1,33 @@ package crypto -import "github.com/cosmos/cosmos-sdk/codec" +import ( + cryptoamino "github.com/tendermint/tendermint/crypto/encoding/amino" -var cryptoCodec = codec.New() + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys" +) + +// CryptoCodec is the default amino codec used by ethermint +var CryptoCodec = codec.New() + +// Amino encoding names +const ( + PrivKeyAminoName = "ethermint/PrivKeySecp256k1" + PubKeyAminoName = "ethermint/PubKeySecp256k1" +) func init() { - RegisterCodec(cryptoCodec) + // replace the keyring codec with the ethermint crypto codec to prevent + // amino panics because of unregistered Priv/PubKey + keys.CryptoCdc = CryptoCodec + keys.RegisterCodec(CryptoCodec) + cryptoamino.RegisterAmino(CryptoCodec) + RegisterCodec(CryptoCodec) } // RegisterCodec registers all the necessary types with amino for the given // codec. func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(PubKeySecp256k1{}, "crypto/PubKeySecp256k1", nil) - cdc.RegisterConcrete(PrivKeySecp256k1{}, "crypto/PrivKeySecp256k1", nil) + cdc.RegisterConcrete(PubKeySecp256k1{}, PubKeyAminoName, nil) + cdc.RegisterConcrete(PrivKeySecp256k1{}, PrivKeyAminoName, nil) } diff --git a/crypto/secp256k1.go b/crypto/secp256k1.go index f57b1400c5..099e9197f1 100644 --- a/crypto/secp256k1.go +++ b/crypto/secp256k1.go @@ -17,7 +17,7 @@ var _ tmcrypto.PrivKey = PrivKeySecp256k1{} // PrivKeySecp256k1 defines a type alias for an ecdsa.PrivateKey that implements // Tendermint's PrivateKey interface. -type PrivKeySecp256k1 ecdsa.PrivateKey +type PrivKeySecp256k1 []byte // GenerateKey generates a new random private key. It returns an error upon // failure. @@ -27,17 +27,18 @@ func GenerateKey() (PrivKeySecp256k1, error) { return PrivKeySecp256k1{}, err } - return PrivKeySecp256k1(*priv), nil + return PrivKeySecp256k1(ethcrypto.FromECDSA(priv)), nil } // PubKey returns the ECDSA private key's public key. func (privkey PrivKeySecp256k1) PubKey() tmcrypto.PubKey { - return PubKeySecp256k1{privkey.PublicKey} + ecdsaPKey := privkey.ToECDSA() + return PubKeySecp256k1(ethcrypto.CompressPubkey(&ecdsaPKey.PublicKey)) } // Bytes returns the raw ECDSA private key bytes. func (privkey PrivKeySecp256k1) Bytes() []byte { - return ethcrypto.FromECDSA(privkey.ToECDSA()) + return CryptoCodec.MustMarshalBinaryBare(privkey) } // Sign creates a recoverable ECDSA signature on the secp256k1 curve over the @@ -57,8 +58,13 @@ func (privkey PrivKeySecp256k1) Equals(other tmcrypto.PrivKey) bool { } // ToECDSA returns the ECDSA private key as a reference to ecdsa.PrivateKey type. +// The function will panic if the private key is invalid. func (privkey PrivKeySecp256k1) ToECDSA() *ecdsa.PrivateKey { - return (*ecdsa.PrivateKey)(&privkey) + key, err := ethcrypto.ToECDSA(privkey) + if err != nil { + panic(err) + } + return key } // ---------------------------------------------------------------------------- @@ -66,20 +72,29 @@ func (privkey PrivKeySecp256k1) ToECDSA() *ecdsa.PrivateKey { var _ tmcrypto.PubKey = (*PubKeySecp256k1)(nil) -// PubKeySecp256k1 defines a type alias for an ecdsa.PublicKey that implements -// Tendermint's PubKey interface. -type PubKeySecp256k1 struct { - pubkey ecdsa.PublicKey -} +// PubKeySecp256k1 defines a type alias for an ecdsa.PublicKey that implements Tendermint's PubKey +// interface. It represents the 33-byte compressed public key format. +type PubKeySecp256k1 []byte // Address returns the address of the ECDSA public key. +// The function will panic if the public key is invalid. func (key PubKeySecp256k1) Address() tmcrypto.Address { - return tmcrypto.Address(ethcrypto.PubkeyToAddress(key.pubkey).Bytes()) + pubk, err := ethcrypto.DecompressPubkey(key) + if err != nil { + panic(err) + } + + return tmcrypto.Address(ethcrypto.PubkeyToAddress(*pubk).Bytes()) } // Bytes returns the raw bytes of the ECDSA public key. +// The function panics if the key cannot be marshaled to bytes. func (key PubKeySecp256k1) Bytes() []byte { - return ethcrypto.FromECDSAPub(&key.pubkey) + bz, err := CryptoCodec.MarshalBinaryBare(key) + if err != nil { + panic(err) + } + return bz } // VerifyBytes verifies that the ECDSA public key created a given signature over @@ -92,7 +107,7 @@ func (key PubKeySecp256k1) VerifyBytes(msg []byte, sig []byte) bool { } // the signature needs to be in [R || S] format when provided to VerifySignature - return ethsecp256k1.VerifySignature(key.Bytes(), ethcrypto.Keccak256Hash(msg).Bytes(), sig) + return ethsecp256k1.VerifySignature(key, ethcrypto.Keccak256Hash(msg).Bytes(), sig) } // Equals returns true if two ECDSA public keys are equal and false otherwise. diff --git a/crypto/secp256k1_test.go b/crypto/secp256k1_test.go index 5c80cdd45f..0da4a5a04e 100644 --- a/crypto/secp256k1_test.go +++ b/crypto/secp256k1_test.go @@ -1,12 +1,13 @@ package crypto import ( - "fmt" "testing" + "github.com/stretchr/testify/require" + ethcrypto "github.com/ethereum/go-ethereum/crypto" ethsecp256k1 "github.com/ethereum/go-ethereum/crypto/secp256k1" - "github.com/stretchr/testify/require" + tmcrypto "github.com/tendermint/tendermint/crypto" ) @@ -24,13 +25,13 @@ func TestPrivKeySecp256k1PrivKey(t *testing.T) { // validate Ethereum address equality addr := privKey.PubKey().Address() - expectedAddr := ethcrypto.PubkeyToAddress(privKey.PublicKey) + expectedAddr := ethcrypto.PubkeyToAddress(privKey.ToECDSA().PublicKey) require.Equal(t, expectedAddr.Bytes(), addr.Bytes()) // validate we can sign some bytes msg := []byte("hello world") sigHash := ethcrypto.Keccak256Hash(msg) - expectedSig, _ := ethsecp256k1.Sign(sigHash.Bytes(), privKey.Bytes()) + expectedSig, _ := ethsecp256k1.Sign(sigHash.Bytes(), privKey) sig, err := privKey.Sign(msg) require.NoError(t, err) @@ -55,7 +56,6 @@ func TestPrivKeySecp256k1PubKey(t *testing.T) { sig, err := privKey.Sign(msg) require.NoError(t, err) - fmt.Println("SIG LENGTH:", len(sig)) res := pubKey.VerifyBytes(msg, sig) require.True(t, res) } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..284dc4dbdb --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,82 @@ +version: "3" + +services: + ethermintdnode0: + container_name: ethermintdnode0 + image: "ethermintd/node" + ports: + - "26656-26657:26656-26657" + - "1317:1317" + - "8545:8545" + - "8546:8546" + environment: + - ID=0 + - LOG=${LOG:-ethermintd.log} + volumes: + - ./build:/ethermint:Z + networks: + localnet: + ipv4_address: 192.168.10.2 + entrypoint: "bash start.sh" + + ethermintdnode1: + container_name: ethermintdnode1 + image: "ethermintd/node" + ports: + - "26659-26660:26656-26657" + - "1318:1317" + - "8547:8545" + - "8548:8546" + environment: + - ID=1 + - LOG=${LOG:-ethermintd.log} + volumes: + - ./build:/ethermint:Z + networks: + localnet: + ipv4_address: 192.168.10.3 + entrypoint: "bash start.sh" + + ethermintdnode2: + container_name: ethermintdnode2 + image: "ethermintd/node" + environment: + - ID=2 + - LOG=${LOG:-ethermintd.log} + ports: + - "26661-26662:26656-26657" + - "1319:1317" + - "8549:8545" + - "8550:8546" + volumes: + - ./build:/ethermint:Z + networks: + localnet: + ipv4_address: 192.168.10.4 + entrypoint: "bash start.sh" + + ethermintdnode3: + container_name: ethermintdnode3 + image: "ethermintd/node" + environment: + - ID=3 + - LOG=${LOG:-ethermintd.log} + ports: + - "26663-26664:26656-26657" + - "1320:1317" + - "8551:8545" + - "8552:8546" + volumes: + - ./build:/ethermint:Z + networks: + localnet: + ipv4_address: 192.168.10.5 + entrypoint: "bash start.sh" + +networks: + localnet: + driver: bridge + ipam: + driver: default + config: + - subnet: 192.168.10.0/16 diff --git a/docs/.vuepress/components/Home.vue b/docs/.vuepress/components/Home.vue new file mode 100644 index 0000000000..ee55fc4b1e --- /dev/null +++ b/docs/.vuepress/components/Home.vue @@ -0,0 +1,512 @@ + + + diff --git a/docs/.vuepress/components/IconCode.vue b/docs/.vuepress/components/IconCode.vue new file mode 100644 index 0000000000..a8f873f601 --- /dev/null +++ b/docs/.vuepress/components/IconCode.vue @@ -0,0 +1,31 @@ + \ No newline at end of file diff --git a/docs/.vuepress/components/IconRocket.vue b/docs/.vuepress/components/IconRocket.vue new file mode 100644 index 0000000000..d084898048 --- /dev/null +++ b/docs/.vuepress/components/IconRocket.vue @@ -0,0 +1,41 @@ + \ No newline at end of file diff --git a/docs/.vuepress/components/TmIconEthereumIntro.vue b/docs/.vuepress/components/TmIconEthereumIntro.vue new file mode 100644 index 0000000000..96af4d7f97 --- /dev/null +++ b/docs/.vuepress/components/TmIconEthereumIntro.vue @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/docs/.vuepress/components/TmLogoEthereumBlack.vue b/docs/.vuepress/components/TmLogoEthereumBlack.vue new file mode 100644 index 0000000000..9756babf49 --- /dev/null +++ b/docs/.vuepress/components/TmLogoEthereumBlack.vue @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/docs/.vuepress/components/TmLogoEthereumWhite.vue b/docs/.vuepress/components/TmLogoEthereumWhite.vue new file mode 100644 index 0000000000..7952b460fe --- /dev/null +++ b/docs/.vuepress/components/TmLogoEthereumWhite.vue @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js new file mode 100644 index 0000000000..d36c9e893b --- /dev/null +++ b/docs/.vuepress/config.js @@ -0,0 +1,105 @@ +module.exports = { + theme: 'cosmos', + title: 'Ethermint Documentation', + locales: { + '/': {lang: 'en-US'}, + }, + base: process.env.VUEPRESS_BASE || '/', + themeConfig: { + repo: 'ChainSafe/ethermint', + docsRepo: 'ChainSafe/ethermint', + docsBranch: 'development', + docsDir: 'docs', + editLinks: true, + custom: true, + logo: { + src: '/logo.svg', + }, + algolia: {id: 'BH4D9OD16A', key: 'c5da4dd3636828292e3c908a0db39688', index: 'ethermint'}, + topbar: {banner: false}, + sidebar: { + auto: false, + nav: [ + { + title: 'Reference', + children: [ + {title: 'Introduction', directory: true, path: '/intro'}, + {title: 'Quick Start', directory: true, path: '/quickstart'}, + {title: 'Basics', directory: true, path: '/basics'}, + {title: 'Core Concepts', directory: true, path: '/core'}, + {title: 'Guides', directory: true, path: '/guides'} + ] + }, + {title: 'Specifications', children: [{title: 'Modules', directory: true, path: '/modules'}]}, { + title: 'Resources', + children: [ + {title: 'Ethermint API Reference', path: 'https://godoc.org/github.com/cosmos/ethermint'}, + {title: 'Cosmos REST API Spec', path: 'https://cosmos.network/rpc/'}, + {title: 'Ethereum JSON RPC API Reference', path: 'https://eth.wiki/json-rpc/API'} + ] + } + ] + }, + gutter: { + title: 'Help & Support', + chat: { + title: 'Developer Chat', + text: 'Chat with Ethermint developers on Discord.', + url: 'https://discordapp.com/channels/669268347736686612', + bg: 'linear-gradient(103.75deg, #1B1E36 0%, #22253F 100%)' + }, + forum: { + title: 'Ethermint Developer Forum', + text: 'Join the Ethermint Developer Forum to learn more.', + url: 'https://forum.cosmos.network/', + bg: 'linear-gradient(221.79deg, #3D6B99 -1.08%, #336699 95.88%)', + logo: 'ethereum-white' + }, + github: { + title: 'Found an Issue?', + text: 'Help us improve this page by suggesting edits on GitHub.', + bg: '#F8F9FC' + } + }, + footer: { + logo: '/logo-bw.svg', + textLink: {text: 'ethermint.zone', url: 'https://ethermint.zone'}, + services: [ + {service: 'github', url: 'https://github.com/ChainSafe/ethermint'}, + {service: 'twitter', url: 'https://twitter.com/chainsafeth'}, + {service: 'linkedin', url: 'https://www.linkedin.com/company/chainsafe-systems'}, + {service: 'medium', url: 'https://medium.com/chainsafe-systems'}, + ], + smallprint: + 'This website is maintained by [ChainSafe Systems](https://chainsafe.io). The contents and opinions of this website are those of Chainsafe Systems.', + links: [ + { + title: 'Documentation', + children: [ + {title: 'Cosmos SDK Docs', url: 'https://docs.cosmos.network'}, + {title: 'Ethermint Docs', url: 'https://ethereum.org/developers'}, + {title: 'Tendermint Core Docs', url: 'https://docs.tendermint.com'} + ] + }, + { + title: 'Community', + children: [ + {title: 'Cosmos Community', url: 'https://discord.gg/W8trcGV'}, + {title: 'Ethermint Forum', url: 'https://forum.cosmos.network/c/ethermint'}, + {title: 'Chainsafe Blog', url: 'https://medium.com/chainsafe-systems'} + ] + }, + { + title: 'Contributing', + children: [ + {title: 'Contributing to the docs', url: 'https://github.com/ChainSafe/ethermint/tree/development/docs'}, + {title: 'Careers at Chainsafe', url: 'https://chainsafe.io/#careers'}, { + title: 'Source code on GitHub', + url: 'https://github.com/Chainsafe/ethermint/blob/development/docs/DOCS_README.md' + } + ] + } + ] + } + }, +}; diff --git a/docs/.vuepress/public/logo-bw.svg b/docs/.vuepress/public/logo-bw.svg new file mode 100644 index 0000000000..ebd57ae472 --- /dev/null +++ b/docs/.vuepress/public/logo-bw.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/.vuepress/public/logo.svg b/docs/.vuepress/public/logo.svg new file mode 100644 index 0000000000..c50855096f --- /dev/null +++ b/docs/.vuepress/public/logo.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/.vuepress/styles/index.styl b/docs/.vuepress/styles/index.styl new file mode 100644 index 0000000000..f844b29b68 --- /dev/null +++ b/docs/.vuepress/styles/index.styl @@ -0,0 +1,3 @@ +:root + --color-link #3171B0 + --color-primary #7499BF \ No newline at end of file diff --git a/docs/DOCS_README.md b/docs/DOCS_README.md new file mode 100644 index 0000000000..d987bc2e7c --- /dev/null +++ b/docs/DOCS_README.md @@ -0,0 +1,111 @@ +# Updating the docs + +If you want to open a PR on the Cosmos SDK to update the documentation, please follow the guidelines in the [`CONTRIBUTING.md`](https://github.com/cosmos/ethermint/tree/master/CONTRIBUTING.md#updating-documentation) + +## Translating + +- Docs translations live in a `docs/country-code/` folder, where `country-code` stands for the country code of the language used (`cn` for Chinese, `kr` for Korea, `fr` for France, ...). +- Always translate content living on `master`. +- Only content under `/docs/intro/`, `/docs/basics/`, `/docs/core/`, `/docs/building-modules/` and `docs/interfaces` needs to be translated, as well as `docs/README.md`. It is also nice (but not mandatory) to translate `/docs/spec/`. +- Specify the release/tag of the translation in the README of your translation folder. Update the release/tag each time you update the translation. + +## Docs Build Workflow + +The documentation for Ethermint is hosted at https://docs.ethermint.zone/ + +built from the files in this (`/docs`) directory for +[master](https://github.com/cosmos/ethermint/tree/master/docs). + +### How It Works + +There is a CircleCI job listening for changes in the `/docs` directory, on +the `master` branch. Any updates to files in this directory +on that branch will automatically trigger a website deployment. Under the hood, +the private website repository has a `make build-docs` target consumed by a CircleCI job in that repo. + +## README + +The [README.md](./README.md) is also the landing page for the documentation +on the website. During the Jenkins build, the current commit is added to the bottom +of the README. + +## Config.js + +The [config.js](./.vuepress/config.js) generates the sidebar and Table of Contents +on the website docs. Note the use of relative links and the omission of +file extensions. Additional features are available to improve the look +of the sidebar. + +## Links + +**NOTE:** Strongly consider the existing links - both within this directory +and to the website docs - when moving or deleting files. + +Relative links should be used nearly everywhere, having discovered and weighed the following: + +### Relative + +Where is the other file, relative to the current one? + +- works both on GitHub and for the VuePress build +- confusing / annoying to have things like: `../../../../myfile.md` +- requires more updates when files are re-shuffled + +### Absolute + +Where is the other file, given the root of the repo? + +- works on GitHub, doesn't work for the VuePress build +- this is much nicer: `/docs/hereitis/myfile.md` +- if you move that file around, the links inside it are preserved (but not to it, of course) + +### Full + +The full GitHub URL to a file or directory. Used occasionally when it makes sense +to send users to the GitHub. + +## Building Locally + +Make sure you are in the `docs` directory and run the following commands: + +```bash +rm -rf node_modules +``` + +This command will remove old version of the visual theme and required packages. This step is optional. + +```bash +yarn install +``` + +Install the theme and all dependencies. + +```bash +yarn run serve +``` + +Run `pre` and `post` hooks and start a hot-reloading web-server. See output of this command for the URL (it is often [https://localhost:8080](https://localhost:8080)). + +To build documentation as a static website run `yarn run build`. You will find the website in `.vuepress/dist` directory. + +## Search + +We are using [Algolia](https://www.algolia.com) to power full-text search. This uses a public API search-only key in the `config.js` as well as a [cosmos_network.json](https://github.com/algolia/docsearch-configs/blob/master/configs/cosmos_network.json) configuration file that we can update with PRs. + +### Update and Build the RPC docs + +1. Execute the following command at the root directory to install the swagger-ui generate tool. + + ```bash + make tools + ``` + +2. Edit API docs + 1. Directly Edit API docs manually: `client/lcd/swagger-ui/swagger.yaml`. + 2. Edit API docs within the [Swagger Editor](https://editor.swagger.io/). Please refer to this [document](https://swagger.io/docs/specification/2-0/basic-structure/) for the correct structure in `.yaml`. +3. Download `swagger.yaml` and replace the old `swagger.yaml` under fold `client/lcd/swagger-ui`. +4. Compile gaiacli + + ```bash + make install + ``` diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..3a27c0349d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,60 @@ + + +# Ethermint Documentation + +## Get Started + +- **[Introduction](./intro/overview.md)**: High-level overview of Ethermint. + +## Reference + +- **[Basics](./basics/)**: Documentation on the basic concepts of Ethermint, like the standard anatomy of an application, the transaction lifecycle and accounts management. +- **[Core](./core/)**: Documentation on the core concepts of Ethermint, like `encoding`, and `events`. +- **[Building Modules](./building-modules/)**: Important concepts for module developers like `message`s, `keeper`s, `handler`s and `querier`s. +- **[Interfaces](./interfaces/)**: Documentation on building interfaces for Ethermint applications. + +## Other Resources + +- **[Module Directory](../x/)**: Module implementations and their respective documentation. +- **[Ethermint API Reference](https://godoc.org/github.com/cosmos/ethermint)**: Godocs of Ethermint. +- **[REST API spec](https://cosmos.network/rpc/)**: List of REST endpoints to interact with an full-node through REST. + +## Contribute + +See [this file](https://github.com/ChainSafe/ethermint/blob/development/docs/DOCS_README.md) for details of the build process and considerations when making changes. diff --git a/docs/architecture/README.md b/docs/architecture/README.md new file mode 100644 index 0000000000..1d83b09768 --- /dev/null +++ b/docs/architecture/README.md @@ -0,0 +1,40 @@ + + +# Architecture Decision Records (ADR) + +This is a location to record all high-level architecture decisions in Ethermint. + +You can read more about the ADR concept in this blog posts: + +- [GitHub - Why Write ADRs](https://github.blog/2020-08-13-why-write-adrs/) +- [Reverb - Documenting architecture decisions, the Reverb way](https://product.reverb.com/documenting-architecture-decisions-the-reverb-way-a3563bb24bd0#.78xhdix6t) + +An ADR should provide: + +- Context on the relevant goals and the current state +- Proposed changes to achieve the goals +- Summary of pros and cons +- References +- Changelog + +Note the distinction between an ADR and a spec. The ADR provides the context, intuition, reasoning, and +justification for a change in architecture, or for the architecture of something +new. The spec is much more compressed and streamlined summary of everything as +it stands today. + +If recorded decisions turned out to be lacking, convene a discussion, record the new decisions here, and then modify the code to match. + +Note the context/background should be written in the present tense. + +Please add a entry below in your Pull Request for an ADR. + +## ADR Table of Contents + + diff --git a/docs/architecture/adr-template.md b/docs/architecture/adr-template.md new file mode 100644 index 0000000000..aa6f84344c --- /dev/null +++ b/docs/architecture/adr-template.md @@ -0,0 +1,40 @@ +# ADR {XXX}: {TITLE} + +## Changelog + +- {date}: {changelog} + +## Status + +> A decision may be "proposed" if the project stakeholders haven't agreed with it yet, or "accepted" once it is agreed. If a later ADR changes or reverses a decision, it may be marked as "deprecated" or "superseded" with a reference to its replacement. +> {Deprecated|Proposed|Accepted} {Implemented|Not Implemented} + +## Context + +> This section describes the forces at play, including technological, political, social, and project local. These forces are probably in tension, and should be called out as such. The language in this section is value-neutral. It is simply describing facts. +> {context body} + +## Decision + +> This section describes our response to these forces. It is stated in full sentences, with active voice. "We will ..." +> {decision body} + +## Consequences + +> This section describes the resulting context, after applying the decision. All consequences should be listed here, not just the "positive" ones. A particular decision may have positive, negative, and neutral consequences, but all of them affect the team and project in the future. + +### Positive + +{positive consequences} + +### Negative + +{negative consequences} + +### Neutral + +{neutral consequences} + +## References + +- {reference link} diff --git a/docs/basics/README.md b/docs/basics/README.md new file mode 100644 index 0000000000..ff42b3b60f --- /dev/null +++ b/docs/basics/README.md @@ -0,0 +1,17 @@ + + +# Basics + +This repository contains reference documentation on the basic concepts of Ethermint. + +1. [Accounts](./accounts.md) +2. [Gas and Fees](./gas.md) +3. [Lifecycle of a transaction](./transactions.md) +4. [Photon](./photon.md) +5. [JSON-RPC Server](./json_rpc.md) + +After reading the basics, head on to the [Core Reference](../core/README.md) for more advanced material. diff --git a/docs/basics/accounts.md b/docs/basics/accounts.md new file mode 100644 index 0000000000..12817324a0 --- /dev/null +++ b/docs/basics/accounts.md @@ -0,0 +1,85 @@ + + +# Accounts + +This document describes the in-built accounts system of Ethermint. {synopsis} + +## Pre-requisite Readings + +- [Cosmos SDK Accounts](https://docs.cosmos.network/master/basics/accounts.html) {prereq} +- [Ethereum Accounts](https://ethereum.org/en/whitepaper/#ethereum-accounts) {prereq} + +## Ethermint Accounts + +Ethermint defines its own custom `Account` type that uses Ethereum's ECDSA secp256k1 curve for keys. This +satisfies the [EIP84](https://github.com/ethereum/EIPs/issues/84) for full [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) paths. +The root HD path for Ethermint-based accounts is `m/44'/60'/0'/0`. + ++++ https://github.com/ChainSafe/ethermint/blob/v0.1.0/types/account.go#L31-L36 + +## Addresses and Public Keys + +There are 3 main types of `Addresses`/`PubKeys` available by default on Ethermint: + +- Addresses and Keys for **accounts**, which identify users (e.g. the sender of a `message`). They are derived using the **`eth_secp256k1`** curve. +- Addresses and Keys for **validator operators**, which identify the operators of validators. They are derived using the **`eth_secp256k1`** curve. +- Addresses and Keys for **consensus nodes**, which identify the validator nodes participating in consensus. They are derived using the **`ed25519`** curve. + +| | Address bech32 Prefix | Pubkey bech32 Prefix | Curve | Address byte length | Pubkey byte length | +|--------------------|-----------------------|----------------------|-----------------|---------------------|--------------------| +| Accounts | `eth` | `ethpub` | `eth_secp256k1` | `20` | `33` (compressed) | +| Validator Operator | `ethvaloper` | `ethvaloperpub` | `eth_secp256k1` | `20` | `33` (compressed) | +| Consensus Nodes | `ethvalcons` | `ethvalconspub` | `ed25519` | `20` | `32` | + +## Address formats for clients + +`EthAccount`s have can be represented in both [Bech32](https://en.bitcoin.it/wiki/Bech32) and hex format for Ethereum's Web3 tooling compatibility. + +The Bech32 format is the default format for Cosmos-SDK queries and transactions through CLI and REST +clients. The hex format on the other hand, is the Ethereum `common.Address` representation of a +Cosmos `sdk.AccAddress`. + +- Address (Bech32): `eth1crwhac03z2pcgu88jfnqnwu66xlthlz2rhljah` +- Address ([EIP55](https://eips.ethereum.org/EIPS/eip-55) Hex): `0xc0dd7ee1f112838470e7926609bb9ad1bebbfc4a` +- Compressed Public Key (Bech32): `ethpub1pfqnmk6pqnwwuw0h9hj58t2hyzwvqc3truhhp5tl5hfucezcfy2rs8470nkyzju2vmk645fzmw2wveaqcqek767kwa0es9rmxe9nmmjq84cpny3fvj6tpg` + +You can query an account address using the Cosmos CLI or REST clients: + +```bash +# NOTE: the --output (-o) flag will define the output format in JSON or YAML (text) +ethermintcli q auth account $(ethermintcli keys show -a) -o text +| + address: eth1f8rqrfwut7ngkxwth0gt99h0lxnxsp09ngvzwl + eth_address: 0x49c601A5DC5FA68b19CBbbd0b296eFF9a66805e5 + coins: + - denom: aphoton + amount: "1000000000000000000" + - denom: stake + amount: "999999999900000000" + public_key: ethpub1pfqnmkepqw45vpsn6dzvm7k22zrghx0nfewjdfacy7wyycv5evfk57kyhwr8cqj5r4x + account_number: 0 + sequence: 1 + code_hash: c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 +``` + +``` bash +# GET /auth/accounts/{address} +curl -X GET "/auth/accounts/eth1f8rqrfwut7ngkxwth0gt99h0lxnxsp09ngvzwl" -H "accept: application/json" +``` + +::: tip +The Cosmos SDK Keyring output (i.e `ethermintcli keys`) only supports addresses and public keys in Bech32 format. +::: + +To retrieve the Ethereum hex address using Web3, use the JSON-RPC `eth_accounts` endpoint: + +```bash +# query against a local node +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_accounts","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:26664 +``` + +## Next {hide} + +Learn about Ethermint [transactions](./transactions.md) {hide} diff --git a/docs/basics/gas.md b/docs/basics/gas.md new file mode 100644 index 0000000000..db43f387e1 --- /dev/null +++ b/docs/basics/gas.md @@ -0,0 +1,29 @@ + + +# Gas and Fees + +Learn about the differences between `Gas` and `Fees` in Ethereum and Cosmos. {synopsis} + +## Introduction to `Gas` in the SDK + + + +## Matching EVM Gas consumption + + + +## Gas refunds + + + +## AnteHandler + +The `AnteHandler` is a special `handler` that is run for every transaction during `CheckTx` and `DeliverTx`, before the `handler` of each `message` in the transaction. `AnteHandler`s have a different signature than `handler`s + + + +## Next {hide} + +Learn about the [Photon](./photon.md) token {hide} diff --git a/docs/basics/img/photon.png b/docs/basics/img/photon.png new file mode 100644 index 0000000000..2549bd430b Binary files /dev/null and b/docs/basics/img/photon.png differ diff --git a/docs/basics/json_rpc.md b/docs/basics/json_rpc.md new file mode 100644 index 0000000000..58a5db0aa1 --- /dev/null +++ b/docs/basics/json_rpc.md @@ -0,0 +1,898 @@ + + +# JSON-RPC Server + +Check the JSON-RPC methods and namespaces supported on Ethermint. {synopsis} + +## Pre-requisite Readings + +- [Ethereum JSON-RPC](https://eth.wiki/json-rpc/API) {prereq} +- [Geth JSON-RPC APIs](https://geth.ethereum.org/docs/rpc/server) {prereq} + +## JSON-RPC Methods + +| Method | Namespace | Implemented | Notes | +|-----------------------------------------------------------------------------------|-----------|-------------|---------------------------| +| [`web3_clientVersion`](#web3-clientversion) | Web3 | ✔ | | +| [`web3_sha3`](#web3-sha3) | Web3 | ✔ | | +| [`net_version`](#net-version) | Net | ✔ | | +| `net_peerCount` | Net | | | +| `net_listening` | Net | | | +| [`eth_protocolVersion`](#eth-protocolversion) | Eth | ✔ | | +| [`eth_syncing`](#eth-syncing) | Eth | ✔ | | +| [`eth_gasPrice`](#eth-gasprice) | Eth | ✔ | | +| [`eth_accounts`](#eth-accounts) | Eth | ✔ | | +| [`eth_blockNumber`](#eth-blocknumber) | Eth | ✔ | | +| [`eth_getBalance`](#eth-getbalance) | Eth | ✔ | | +| [`eth_getStorageAt`](#eth-getstorageat) | Eth | ✔ | | +| [`eth_getTransactionCount`](#eth-gettransactioncount) | Eth | ✔ | | +| [`eth_getBlockTransactionCountByNumber`](#eth-getblocktransactioncountbynumber) | Eth | ✔ | | +| [`eth_getBlockTransactionCountByHash`](#eth-getblocktransactioncountbyhash) | Eth | ✔ | | +| [`eth_getCode`](#eth-getcode) | Eth | ✔ | | +| [`eth_sign`](#eth-sign) | Eth | ✔ | | +| [`eth_sendTransaction`](#eth-sendtransaction) | Eth | ✔ | | +| [`eth_sendRawTransaction`](#eth-sendrawtransaction) | Eth | ✔ | | +| [`eth_call`](#eth-call) | Eth | ✔ | | +| [`eth_estimateGas`](#eth-estimategas) | Eth | ✔ | | +| [`eth_getBlockByNumber`](#eth-getblockbynumber) | Eth | ✔ | | +| [`eth_getBlockByHash`](#eth-getblockbyhash) | Eth | ✔ | | +| [`eth_getTransactionByHash`](#eth-gettransactionbyhash) | Eth | ✔ | | +| [`eth_getTransactionByBlockHashAndIndex`](#eth-gettransactionbyblockhashandindex) | Eth | ✔ | | +| [`eth_getTransactionReceipt`](#eth-gettransactionreceipt) | Eth | ✔ | | +| [`eth_newFilter`](#eth-newfilter) | Eth | ✔ | | +| [`eth_newBlockFilter`](#eth-newblockfilter) | Eth | ✔ | | +| [`eth_newPendingTransactionFilter`](#eth-newpendingtransactionfilter) | Eth | ✔ | | +| [`eth_uninstallFilter`](#eth-uninstallfilter) | Eth | ✔ | | +| [`eth_getFilterChanges`](#eth-getfilterchanges) | Eth | ✔ | | +| [`eth_getLogs`](#eth-getlogs) | Eth | ✔ | | +| `eth_getTransactionbyBlockNumberAndIndex` | Eth | | | +| `eth_getWork` | Eth | | | +| `eth_submitWork` | Eth | | | +| `eth_submitHashrate` | Eth | | | +| `eth_getCompilers` | Eth | | | +| `eth_compileLLL` | Eth | | | +| `eth_compileSolidity` | Eth | | | +| `eth_compileSerpent` | Eth | | | +| `eth_signTransaction` | Eth | | | +| `eth_mining` | Eth | N/A | Not relevant to Ethermint | +| `eth_coinbase` | Eth | N/A | Not relevant to Ethermint | +| `eth_hashrate` | Eth | N/A | Not relevant to Ethermint | +| `eth_getUncleCountByBlockHash` | Eth | N/A | Not relevant to Ethermint | +| `eth_getUncleCountByBlockNumber` | Eth | N/A | Not relevant to Ethermint | +| `eth_getUncleByBlockHashAndIndex` | Eth | N/A | Not relevant to Ethermint | +| `eth_getUncleByBlockNumberAndIndex` | Eth | N/A | Not relevant to Ethermint | +| [`eth_subscribe`](#eth-subscribe) | Websocket | ✔ | | +| [`eth_unsubscribe`](#eth-unsubscribe) | Websocket | ✔ | | +| [`personal_importRawKey`](#personal-importrawkey) | Personal | ✔ | | +| [`personal_listAccounts`](#personal-listaccounts) | Personal | ✔ | | +| [`personal_lockAccount`](#personal-lockaccount) | Personal | ✔ | | +| [`personal_newAccount`](#personal-newaccount) | Personal | ✔ | | +| [`personal_unlockAccount`](#personal-unlockaccount) | Personal | ✔ | | +| [`personal_sendTransaction`](#personal-sendtransaction) | Personal | ✔ | | +| [`personal_sign`](#personal-sign) | Personal | ✔ | | +| [`personal_ecRecover`](#personal-ecrecover) | Personal | ✔ | | +| `db_putString` | DB | | | +| `db_getString` | DB | | | +| `db_putHex` | DB | | | +| `db_getHex` | DB | | | +| `shh_post` | SSH | | | +| `shh_version` | SSH | | | +| `shh_newIdentity` | SSH | | | +| `shh_hasIdentity` | SSH | | | +| `shh_newGroup` | SSH | | | +| `shh_addToGroup` | SSH | | | +| `shh_newFilter` | SSH | | | +| `shh_uninstallFilter` | SSH | | | +| `shh_getFilterChanges` | SSH | | | +| `shh_getMessages` | SSH | | | +| `admin_addPeer` | Admin | | | +| `admin_datadir` | Admin | | | +| `admin_nodeInfo` | Admin | | | +| `admin_peers` | Admin | | | +| `admin_startRPC` | Admin | | | +| `admin_startWS` | Admin | | | +| `admin_stopRPC` | Admin | | | +| `admin_stopWS` | Admin | | | +| `clique_getSnapshot` | Clique | | | +| `clique_getSnapshotAtHash` | Clique | | | +| `clique_getSigners` | Clique | | | +| `clique_proposals` | Clique | | | +| `clique_propose` | Clique | | | +| `clique_discard` | Clique | | | +| `clique_status` | Clique | | | +| `debug_backtraceAt` | Debug | | | +| `debug_blockProfile` | Debug | | | +| `debug_cpuProfile` | Debug | | | +| `debug_dumpBlock` | Debug | | | +| `debug_gcStats` | Debug | | | +| `debug_getBlockRlp` | Debug | | | +| `debug_goTrace` | Debug | | | +| `debug_memStats` | Debug | | | +| `debug_seedHash` | Debug | | | +| `debug_setHead` | Debug | | | +| `debug_setBlockProfileRate` | Debug | | | +| `debug_stacks` | Debug | | | +| `debug_startCPUProfile` | Debug | | | +| `debug_startGoTrace` | Debug | | | +| `debug_stopCPUProfile` | Debug | | | +| `debug_stopGoTrace` | Debug | | | +| `debug_traceBlock` | Debug | | | +| `debug_traceBlockByNumber` | Debug | | | +| `debug_traceBlockByHash` | Debug | | | +| `debug_traceBlockFromFile` | Debug | | | +| `debug_standardTraceBlockToFile` | Debug | | | +| `debug_standardTraceBadBlockToFile` | Debug | | | +| `debug_traceTransaction` | Debug | | | +| `debug_verbosity` | Debug | | | +| `debug_vmodule` | Debug | | | +| `debug_writeBlockProfile` | Debug | | | +| `debug_writeMemProfile` | Debug | | | +| `les_serverInfo` | Les | | | +| `les_clientInfo` | Les | | | +| `les_priorityClientInfo` | Les | | | +| `les_addBalance` | Les | | | +| `les_setClientParams` | Les | | | +| `les_setDefaultParams` | Les | | | +| `les_latestCheckpoint` | Les | | | +| `les_getCheckpoint` | Les | | | +| `les_getCheckpointContractAddress` | Les | | | +| `miner_getHashrate` | Miner | | | +| `miner_setExtra` | Miner | | | +| `miner_setGasPrice` | Miner | | | +| `miner_start` | Miner | | | +| `miner_stop` | Miner | | | +| `miner_setEtherbase` | Miner | | | +| `txpool_content` | TXPool | | | +| `txpool_inspect` | TXPool | | | +| `txpool_status` | TXPool | | | + + +:::tip +Block Number can be entered as a Hex string, `"earliest"`, `"latest"` or `"pending"`. +::: + +Below is a list of the RPC methods, the parameters and an example response from the namespaces. + +## Web3 Methods + +### web3_clientVersion + +Get the web3 client version. + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":67}' -H "Content-Type: application/json" http://localhost:8545 + +// Result + {"jsonrpc":"2.0","id":1,"result":"Ethermint/0.0.0+/linux/go1.14"} +``` + +### web3_sha3 + +Returns Keccak-256 (not the standardized SHA3-256) of the given data. + +#### Parameters + +- The data to convert into a SHA3 hash + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"web3_sha3","params":["0x67656c6c6f20776f726c64"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0x1b84adea42d5b7d192fd8a61a85b25abe0757e9a65cab1da470258914053823f"} +``` + +## Net Methods + +### net_version + +Returns the current network id. + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"net_version","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"8"} +``` + +## Eth Methods + +### eth_protocolVersion + +Returns the current ethereum protocol version. + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_protocolVersion","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0x3f"} +``` + +### eth_syncing + +The sync status object may need to be different depending on the details of Tendermint's sync protocol. However, the 'synced' result is simply a boolean, and can easily be derived from Tendermint's internal sync state. + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":false} +``` + +### eth_gasPrice + +Returns the current gas price in aphotons. + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0x0"} +``` + +### eth_accounts + +Returns array of all eth accounts. + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_accounts","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":["0x3b7252d007059ffc82d16d022da3cbf9992d2f70","0xddd64b4712f7c8f1ace3c145c950339eddaf221d","0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0"]} +``` + +### eth_blockNumber + +Returns the current block height. + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0x66"} +``` + +### eth_getBalance + +Returns the account balance for a given account address and Block Number. + +#### Parameters + +- Account Address + +- Block Number + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0", "0x0"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0x36354d5575577c8000"} +``` + +### eth_getStorageAt + +Returns the storage address for a given account address. + +#### Parameters + +- Account Address + +- Integer of the position in the storage + +- Block Number + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getStorageAt","params":["0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0", "0" "latest"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000000000000000000"} +``` + +### eth_getTransactionCount + +Returns the total transaction for a given account address and Block Number. + +#### Parameters + +- Account Address + +- Block Number + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["0x7bf7b17da59880d9bcca24915679668db75f9397", "0x0"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0x8"} +``` + +### eth_getBlockTransactionCountByNumber + +Returns the total transaction count for a given block number. + +#### Parameters + +- Block number + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getBlockTransactionCountByNumber","params":["0x1"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result + {"jsonrpc":"2.0","id":1,"result":{"difficulty":null,"extraData":"0x0","gasLimit":"0xffffffff","gasUsed":"0x0","hash":"0x8101cc04aea3341a6d4b3ced715e3f38de1e72867d6c0db5f5247d1a42fbb085","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","nonce":null,"number":"0x17d","parentHash":"0x70445488069d2584fea7d18c829e179322e2b2185b25430850deced481ca2e77","sha3Uncles":null,"size":"0x1df","stateRoot":"0x269bb17fe7adb8dd5f15f57b717979f82078d6b7a675c1ba1b0da2d27e415fcc","timestamp":"0x5f5ba97c","totalDifficulty":null,"transactions":[],"transactionsRoot":"0x","uncles":[]}} +``` + +### eth_getBlockTransactionCountByHash + +Returns the total transaction count for a given block hash. + +#### Parameters + +- Block Hash + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getBlockTransactionCountByHash","params":["0x8101cc04aea3341a6d4b3ced715e3f38de1e72867d6c0db5f5247d1a42fbb085"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0x3"} +``` + +### eth_getCode + +Returns the code for a given account address and Block Number. + +#### Parameters + +- Account Address + +- Block Number + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getCode","params":["0x7bf7b17da59880d9bcca24915679668db75f9397", "0x0"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0xef616c92f3cfc9e92dc270d6acff9cea213cecc7020a76ee4395af09bdceb4837a1ebdb5735e11e7d3adb6104e0c3ac55180b4ddf5e54d022cc5e8837f6a4f971b"} +``` + +### eth_sign + +The sign method calculates an Ethereum specific signature with: sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))). + +By adding a prefix to the message makes the calculated signature recognisable as an Ethereum specific signature. This prevents misuse where a malicious DApp can sign arbitrary data (e.g. transaction) and use the signature to impersonate the victim. + +::: warning +the address to sign with must be unlocked. +::: + +#### Parameters + +- Account Address + +- Message to sign + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_sign","params":["0x3b7252d007059ffc82d16d022da3cbf9992d2f70", "0xdeadbeaf"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0x909809c76ed2a5d38733de39207d0f411222b9b49c64a192bf649cb13f63f37b45acb4f6939facb4f1c277bc70fb00407564140c0f18600ac44388f2c1dfd1dc1b"} +``` + +### eth_sendTransaction + +Sends transaction from given account to a given account. + +#### Parameters + + - Object containing: + + from: DATA, 20 Bytes - The address the transaction is send from. + + to: DATA, 20 Bytes - (optional when creating new contract) The address the transaction is directed to. + + gas: QUANTITY - (optional, default: 90000) Integer of the gas provided for the transaction execution. It will return unused gas. + + gasPrice: QUANTITY - (optional, default: To-Be-Determined) Integer of the gasPrice used for each paid gas + + value: QUANTITY - value sent with this transaction + + data: DATA - The compiled code of a contract OR the hash of the invoked method signature and encoded parameters. For details see Ethereum Contract ABI + + nonce: QUANTITY - (optional) Integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce. + + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from":"0x3b7252d007059ffc82d16d022da3cbf9992d2f70", "to":"0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0", "value":"0x16345785d8a0000", "gasLimit":"0x5208", "gasPrice":"0x55ae82600"}],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0x33653249db68ebe5c7ae36d93c9b2abc10745c80a72f591e296f598e2d4709f6"} +``` + +### eth_sendRawTransaction + +Creates new message call transaction or a contract creation for signed transactions. + +You can get signed transaction data using the personal_sign method + +#### Parameters + +- The signed transaction data + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_sendRawTransaction","params":["0xf9ff74c86aefeb5f6019d77280bbb44fb695b4d45cfe97e6eed7acd62905f4a85034d5c68ed25a2e7a8eeb9baf1b8401e4f865d92ec48c1763bf649e354d900b1c"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000000000000000000"} +``` + +### eth_call + +Executes a new message call immediately without creating a transaction on the block chain. + +#### Parameters + +- Object containing: + + from: DATA, 20 Bytes - (optional) The address the transaction is sent from. + + to: DATA, 20 Bytes - The address the transaction is directed to. + + gas: QUANTITY - gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions. + + gasPrice: QUANTITY - gasPrice used for each paid gas + + value: QUANTITY - value sent with this transaction + + data: DATA - (optional) Hash of the method signature and encoded parameters. For details see Ethereum Contract ABI in the Solidity documentation + +- Block number + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"from":"0x3b7252d007059ffc82d16d022da3cbf9992d2f70", "to":"0xddd64b4712f7c8f1ace3c145c950339eddaf221d", "gas":"0x5208", "gasPrice":"0x55ae82600", "value":"0x16345785d8a0000", "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"}, "0x0"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0x"} +``` + +### eth_estimateGas + +Returns an estimate value of the gas required to send the transaction. + +#### Parameters + +- Object containing: + + from: DATA, 20 Bytes - The address the transaction is send from. + + to: DATA, 20 Bytes - (optional when creating new contract) The address the transaction is directed to. + + value: QUANTITY - value sent with this transaction + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_estimateGas","params":[{"from":"0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0", "to":"0x3b7252d007059ffc82d16d022da3cbf9992d2f70", "value":"0x16345785d8a00000"}],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0x1199b"} +``` + +### eth_getBlockByNumber + +Returns information about a block by block number. + +#### Parameters + +- Block Number + +- If true it returns the full transaction objects, if false only the hashes of the transactions. + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x1", false],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":{"difficulty":null,"extraData":"0x0","gasLimit":"0xffffffff","gasUsed":null,"hash":"0xabac6416f737a0eb54f47495b60246d405d138a6a64946458cf6cbeae0d48465","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","nonce":null,"number":"0x1","parentHash":"0x","sha3Uncles":null,"size":"0x9b","stateRoot":"0x","timestamp":"0x5f5bd3e5","totalDifficulty":null,"transactions":[],"transactionsRoot":"0x","uncles":[]}} +``` + +### eth_getBlockByHash + +Returns the block info given the hash found in the command above and a bool. + +#### Parameters +- Hash of a block. + +- If true it returns the full transaction objects, if false only the hashes of the transactions. + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getBlockByHash","params":["0x1b9911f57c13e5160d567ea6cf5b545413f96b95e43ec6e02787043351fb2cc4", false],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":{"difficulty":null,"extraData":"0x0","gasLimit":"0xffffffff","gasUsed":null,"hash":"0x1b9911f57c13e5160d567ea6cf5b545413f96b95e43ec6e02787043351fb2cc4","logsBloom":"0x00000000100000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000002000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","nonce":null,"number":"0xc","parentHash":"0x404e58f31a9ede1b614b98701d6b0fbf1450f186842dbcf6426dd16811a5ca0d","sha3Uncles":null,"size":"0x307","stateRoot":"0x599ccdb111fc62c6398dc39be957df8e97bf8ab72ce6c06ff10641a92b754627","timestamp":"0x5f5fdbbd","totalDifficulty":null,"transactions":["0xae64961cb206a9773a6e5efeb337773a6fd0a2085ce480a174135a029afea615"],"transactionsRoot":"0x4764dba431128836fa919b83d314ba9cc000e75f38e1c31a60484409acea777b","uncles":[]}} +``` + +### eth_getTransactionByHash + +Returns transaction details given the ethereum tx something. + +#### Parameters + +- hash of a transaction + +```json +// Request +curl localhost:8545 -H "Content-Type:application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionByHash","params":["0xec5fa15e1368d6ac314f9f64118c5794f076f63c02e66f97ea5fe1de761a8973"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":{"blockHash":"0x7a7398cc11d9c4c8e6f53e0c73824297aceafdab62db9e4b867a0da694384864","blockNumber":"0x188","from":"0x3b7252d007059ffc82d16d022da3cbf9992d2f70","gas":"0x147ee","gasPrice":"0x3b9aca00","hash":"0xec5fa15e1368d6ac314f9f64118c5794f076f63c02e66f97ea5fe1de761a8973","input":"0x6dba746c","nonce":"0x18","to":"0xa655256f589060437e5ffe2246dec385d040f148","transactionIndex":"0x0","value":"0x0","v":"0xa96","r":"0x6db399d694a452fb4106419140a6e5dbbe6817743a0f6f695a651e6576e59a5e","s":"0x25dd6ab1f936d0280d2fed0caeb0ebe5b9a46de6d8cb08ad8fd2c88deb55fc31"}} +``` + +### eth_getTransactionByBlockHashAndIndex + +Returns transaction details given the block hash and the transaction index. + +#### Parameters + +- Hash of a block. + +- Transaction index position. + +```json +// Request +curl localhost:8545 -H "Content-Type:application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionByBlockHashAndIndex","params":["0x1b9911f57c13e5160d567ea6cf5b545413f96b95e43ec6e02787043351fb2cc4", "0x0"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":{"blockHash":"0x1b9911f57c13e5160d567ea6cf5b545413f96b95e43ec6e02787043351fb2cc4","blockNumber":"0xc","from":"0xddd64b4712f7c8f1ace3c145c950339eddaf221d","gas":"0x4c4b40","gasPrice":"0x3b9aca00","hash":"0xae64961cb206a9773a6e5efeb337773a6fd0a2085ce480a174135a029afea615","input":"0x4f2be91f","nonce":"0x0","to":"0x439c697e0742a0ddb124a376efd62a72a94ac35a","transactionIndex":"0x0","value":"0x0","v":"0xa96","r":"0xced57d973e58b0f634f776d57daf41d3d3387ceb347a3a72ca0746e5ec2b709e","s":"0x384e89e209a5eb147a2bac3a4e399507400ac7b29cd155531f9d6203a89db3f2"}} +``` + +### eth_getTransactionReceipt + +Returns the receipt of a transaction by transaction hash. + +#### Parameters + +- Hash of a transaction + +```json +// Request +curl localhost:8545 -H "Content-Type:application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["0xae64961cb206a9773a6e5efeb337773a6fd0a2085ce480a174135a029afea614"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":{"blockHash":"0x1b9911f57c13e5160d567ea6cf5b545413f96b95e43ec6e02787043351fb2cc4","blockNumber":"0xc","contractAddress":"0x0000000000000000000000000000000000000000","cumulativeGasUsed":null,"from":"0xddd64b4712f7c8f1ace3c145c950339eddaf221d","gasUsed":"0x5289","logs":[{"address":"0x439c697e0742a0ddb124a376efd62a72a94ac35a","topics":["0x64a55044d1f2eddebe1b90e8e2853e8e96931cefadbfa0b2ceb34bee36061941"],"data":"0x0000000000000000000000000000000000000000000000000000000000000002","blockNumber":"0xc","transactionHash":"0xae64961cb206a9773a6e5efeb337773a6fd0a2085ce480a174135a029afea615","transactionIndex":"0x0","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","logIndex":"0x0","removed":false},{"address":"0x439c697e0742a0ddb124a376efd62a72a94ac35a","topics":["0x938d2ee5be9cfb0f7270ee2eff90507e94b37625d9d2b3a61c97d30a4560b829"],"data":"0x0000000000000000000000000000000000000000000000000000000000000002","blockNumber":"0xc","transactionHash":"0xae64961cb206a9773a6e5efeb337773a6fd0a2085ce480a174135a029afea615","transactionIndex":"0x0","blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","logIndex":"0x1","removed":false}],"logsBloom":"0x00000000100000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000002000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000","status":"0x1","to":"0x439c697e0742a0ddb124a376efd62a72a94ac35a","transactionHash":"0xae64961cb206a9773a6e5efeb337773a6fd0a2085ce480a174135a029afea615","transactionIndex":"0x0"}} +``` + +### eth_newFilter + +Create new filter using topics of some kind. + +#### Parameters + +- hash of a transaction + +```json +// Request +curl localhost:8545 -H "Content-Type:application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_newFilter","params":[{"topics":["0x0000000000000000000000000000000000000000000000000000000012341234"]}],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0xdc714a4a2e3c39dc0b0b84d66a3ccb00"} +``` + +### eth_newBlockFilter + +Creates a filter in the node, to notify when a new block arrives. + + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_newBlockFilter","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0x3503de5f0c766c68f78a03a3b05036a5"} +``` + +### eth_newPendingTransactionFilter + +Creates a filter in the node, to notify when new pending transactions arrive. + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_newPendingTransactionFilter","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0x9daacfb5893d946997d3801ea18e9902"} +``` + +### eth_uninstallFilter + +Removes the filter with the given filter id. Returns true if the filter was successfully uninstalled, otherwise false. + +#### Parameters + +- The filter id + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_uninstallFilter","params":["0xb91b6608b61bf56288a661a1bd5eb34a"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":true} +``` + +### eth_getFilterChanges + +Polling method for a filter, which returns an array of logs which occurred since last poll. + +#### Parameters + +- The filter id + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getFilterChanges","params":["0x127e9eca4f7751fb4e5cb5291ad8b455"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":["0xc6f08d183a81e149896fc5317c872f9092068e88e956ca1864e9bd4c81c09b44","0x3ca6dfb5be15549d721d1b3d10c1bec50ed6217c9ac7b61df361fac9692a27e5","0x776fffac134171acb1ebf2e59856625501ad5ccc5c4c8fe0359e0d4dff8919f2","0x84123103704dbd738c089276ab2b04b5936330b24f6e78453c4ba8bf4848aaf9","0xffddbe5bd8e8aa41e44002daa9ea89ade9e6980a0d83f51d104cf16498827eca","0x53430e49963e8ae32605d8f22dec2e757a691e6436d593854ca4d9383eeab86a","0x975948058c9351a91fbec332ca00dda39d1a919f5f16b996a4c7e30c38ba423b","0x619e37e32024c8efef7f7220e6caff4ee1d682ea78b2ac91e0a6b30850dc0677","0x31a5d985a40d08303ac68000ce008df512bcd1a911c497415c97f0624b4a271a","0x91dcf1fce4503a8dbb3e6fb61073f25cd31d69c766ecba639fefde4436e59d07","0x606d9e0143cfdb410a6812c590a8135b5c6b5c59eec26d760d5cd930aa47257d","0xd3c00b859b29b20ba654415eef648ef58251389c73a138580db87675b0d5465f","0x954391f0eb50888be90489898016ebb54f750f612f3adec2a00854955d5e52d8","0x698905f06aff921a9e9fcef39b8b0d107747c3e6204d2ea79cf4c12debf8d253","0x9fcafec5721938a06eb8e2951ede4b6ef8fae54a8c8f85f3166ec9782a0032b5","0xaec6d3364e47a5716ba69e4705f3c705d017f81298859589591183bfea87be7a","0x91bf2ee13319b6eaca96ed89c126437b66c4df1b13560c6a9bb18556ee3b7e1f","0x4f426dc1fc0ea8149052033065b237892d2d34927b2d558ab50c5a7fb98d6e79","0xdd809fb07e5aab638fef5311371b4e2b27c9c9a6183fde0cdd2b7724f6d2a89b","0x7e12fc92ab953e233a304959a2a8474d96195e71efd9388fdceb1326a577811a","0x30618ef6b490c3cc9979c47163459db37c1a1e0aa5793c56accd417f9d89973b","0x614609f06ee24bae7408e45895b1a25e6b19a8159aeea7a95c9d1339d9ba286f","0x115ddc6d533620040791d241f01f1c5ae3d9d1a8f64b15af5e9793e4d9096e22","0xb7458c9323beeca2cd54f32a6af5671f3cd5a7a251aed9d82bdd6ebe5f56305b","0x573dd48a5ba7bf4cc3d49597cd7419f75ecc9897258f1ebadebd670446d0d358","0xcb6670918439f9698413b53f3b5336d82ca4be152fdefaacf45e052fff6262fc","0xf3fe2a8945abafd269ab97bfdc80b3dbff2202ffdce59a227f952874b966b230","0x989980707007533cc0840a079f77f261a2e818abae1a1ffd3af02f3fff1d35fd","0x886b6ae365fec996be8a9a2c31cf4cda97ff8352908be2c83f17abd66ef1591e","0xfd90df68706ef95a62b317de93d6899a9bd6c80416e42d007f5c30fcdedfce24","0x7af8491fbb0373886d9032bb74e0ef52ed9e100f260b79bd15f46126b38cbede","0x91d1e2cd55533cf7dd5de86c9aa73295e811b1279be193d429bbd6ba83810e16","0x6b65b3128c2104005a04923288fe2aa33a2477a4962bef70532f94cab582f2a7"]} +``` + + + +### eth_getLogs + +Returns an array of all logs matching a given filter object. + +#### Parameters + +- Object containing: + + fromBlock: QUANTITY|TAG - (optional, default: "latest") Integer block number, or "latest" for the last mined block or "pending", "earliest" for not yet mined transactions. + + toBlock: QUANTITY|TAG - (optional, default: "latest") Integer block number, or "latest" for the last mined block or "pending", "earliest" for not yet mined transactions. + + address: DATA|Array, 20 Bytes - (optional) Contract address or a list of addresses from which logs should originate. + + topics: Array of DATA, - (optional) Array of 32 Bytes DATA topics. Topics are order-dependent. Each topic can also be an array of DATA with “or” options. + + blockhash: (optional, future) With the addition of EIP-234, blockHash will be a new filter option which restricts the logs returned to the single block with the 32-byte hash blockHash. Using blockHash is equivalent to fromBlock = toBlock = the block number with hash blockHash. If blockHash is present in in the filter criteria, then neither fromBlock nor toBlock are allowed. + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getLogs","params":[{"topics":["0x775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd738898","0x0000000000000000000000000000000000000000000000000000000000000011"], "fromBlock":"latest"}],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":[]} +``` + +## WebSocket Methods + +Read about websockets in [events](./../quickstart/events.md) {hide} + +### eth_subscribe + +subscribe using JSON-RPC notifications. This allows clients to wait for events instead of polling for them. + +It works by subscribing to particular events. The node will return a subscription id. For each event that matches the subscription a notification with relevant data is send together with the subscription id. + +#### Parameters + +- Subscription Name + +- Optional Arguments + +```json +// Request +{"id": 1, "method": "eth_subscribe", "params": ["newHeads", {"includeTransactions": true}]} + +// Result +< {"jsonrpc":"2.0","result":"0x34da6f29e3e953af4d0c7c58658fd525","id":1} +``` + +### eth_unsubscribe + +Unsubscribe from an event using the subscription id + +#### Parameters + +- Subscription ID + +```json +// Request +{"id": 1, "method": "eth_unsubscribe", "params": ["0x34da6f29e3e953af4d0c7c58658fd525"]} + +// Result +{"jsonrpc":"2.0","result":true,"id":1} +``` + +## Personal Methods + +### personal_importRawKey + +Imports the given unencrypted private key (hex string) into the key store, encrypting it with the passphrase. + +Returns the address of the new account. + +:::warning +Currently, this is not implemented since the feature is not supported by the keys +::: + +#### Parameters + +- Hex encoded ECDSA key + +- Passphrase + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"personal_importRawKey","params":["c5bd76cd0cd948de17a31261567d219576e992d9066fe1a6bca97496dec634e2c8e06f8949773b300b9f73fabbbc7710d5d6691e96bcf3c9145e15daf6fe07b9", "the key is this"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +``` + +### personal_listAccounts + +Returns a list of addresses for accounts this node manages. + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"personal_listAccounts","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":["0x3b7252d007059ffc82d16d022da3cbf9992d2f70","0xddd64b4712f7c8f1ace3c145c950339eddaf221d","0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0"]} +``` + +### personal_lockAccount + +Removes the private key with given address from memory. The account can no longer be used to send transactions. + +#### Parameters + +- Account Address + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"personal_lockAccount","params":["0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":true} +``` + +### personal_newAccount + +Generates a new private key and stores it in the key store directory. The key file is encrypted with the given passphrase. Returns the address of the new account. + +#### Parameters + +- Passphrase + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"personal_newAccount","params":["This is the passphrase"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0xf0e4086ad1c6aab5d42161d5baaae2f9ad0571c0"} +``` + +### personal_unlockAccount + +Decrypts the key with the given address from the key store. + +Both passphrase and unlock duration are optional when using the JavaScript console. The unencrypted key will be held in memory until the unlock duration expires. If the unlock duration defaults to 300 seconds. An explicit duration of zero seconds unlocks the key until geth exits. + +The account can be used with eth_sign and eth_sendTransaction while it is unlocked. + +#### Parameters + +- Account Address + +- Passphrase + +- Duration + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"personal_unlockAccount","params":["0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0", "secret passphrase", 30],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":true} +``` + +### personal_sendTransaction + +Validate the given passphrase and submit transaction. + +The transaction is the same argument as for eth_sendTransaction and contains the from address. If the passphrase can be used to decrypt the private key belogging to tx.from the transaction is verified, signed and send onto the network. + +:::warning +The account is not unlocked globally in the node and cannot be used in other RPC calls. +::: + +#### Parameters + + - Object containing: + + from: DATA, 20 Bytes - The address the transaction is send from. + + to: DATA, 20 Bytes - (optional when creating new contract) The address the transaction is directed to. + + value: QUANTITY - value sent with this transaction + +- Passphrase + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"personal_sendTransaction","params":[{"from":"0x3b7252d007059ffc82d16d022da3cbf9992d2f70","to":"0xddd64b4712f7c8f1ace3c145c950339eddaf221d", "value":"0x16345785d8a0000"}, "passphrase"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0xd2a31ec1b89615c8d1f4d08fe4e4182efa4a9c0d5758ace6676f485ea60e154c"} +``` + +### personal_sign + +The sign method calculates an Ethereum specific signature with: sign(keccack256("\x19Ethereum Signed Message:\n" + len(message) + message))). + +#### Parameters + +- Message + +- Account Address + +- Password + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"personal_sign","params":["0xdeadbeaf", "0x3b7252d007059ffc82d16d022da3cbf9992d2f70", "password"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0xf9ff74c86aefeb5f6019d77280bbb44fb695b4d45cfe97e6eed7acd62905f4a85034d5c68ed25a2e7a8eeb9baf1b8401e4f865d92ec48c1763bf649e354d900b1c"} +``` + +### personal_ecRecover + +ecRecover returns the address associated with the private key that was used to calculate the signature in personal_sign. + +#### Parameters + +- Message + +- Signature returned from personal_sign + +```json +// Request +curl -X POST --data '{"jsonrpc":"2.0","method":"personal_ecRecover","params":["0xdeadbeaf", "0xf9ff74c86aefeb5f6019d77280bbb44fb695b4d45cfe97e6eed7acd62905f4a85034d5c68ed25a2e7a8eeb9baf1b8401e4f865d92ec48c1763bf649e354d900b1c"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +// Result +{"jsonrpc":"2.0","id":1,"result":"0x3b7252d007059ffc82d16d022da3cbf9992d2f70"} +``` + +## Next {hide} + +Learn about the [encoding](./../core/encoding.md) formats used on Ethermint {hide} diff --git a/docs/basics/photon.md b/docs/basics/photon.md new file mode 100644 index 0000000000..0ecbe5d156 --- /dev/null +++ b/docs/basics/photon.md @@ -0,0 +1,33 @@ + + +# Photon + +Learn about the Photon, Ethermint's staking token. {synopsis} + +## Introduction + +::: tip +The photon's initial distribution and supply is still TBD and will be announced in the future. +::: + +The photon is the staking token used in Ethermint. + +## Base Denomination + +Ethermint uses [Atto](https://en.wikipedia.org/wiki/Atto-) Photon as the base denomination to maintain parity with Ethereum. + +``` +1 photon = 1×10⁻¹⁸ aphoton +``` + +This matches Ethereum denomination of: + +``` +1 ETH = 1x10⁻¹⁸ wei +``` + +## Next {hide} + +Learn about the supported [JSON-RPC](./json_rpc.md) methods on Ethermint {hide} diff --git a/docs/basics/transactions.md b/docs/basics/transactions.md new file mode 100644 index 0000000000..cdb425fa69 --- /dev/null +++ b/docs/basics/transactions.md @@ -0,0 +1,45 @@ + + +# Transactions + +## Routing + +Ethermint needs to parse and handle transactions routed for both the EVM and for the Cosmos hub. We +attempt to achieve this by mimicking [Geth's](https://github.com/ethereum/go-ethereum) `Transaction` +structure and treat it as a unique Cosmos SDK message type. An Ethereum transaction is a single +[`sdk.Msg`](https://godoc.org/github.com/cosmos/cosmos-sdk/types#Msg) contained in an +[`auth.StdTx`](https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth#StdTx). All relevant Ethereum +transaction information is contained in this message. This includes the signature, gas, payload, +etc. + +Being that Ethermint implements the Tendermint ABCI application interface, as transactions are +consumed, they are passed through a series of handlers. Once such handler, the `AnteHandler`, is +responsible for performing preliminary message execution business logic such as fee payment, +signature verification, etc. This is particular to Cosmos SDK routed transactions. Ethereum routed +transactions will bypass this as the EVM handles the same business logic. + +Ethereum routed transactions coming from a web3 source are expected to be RLP encoded, however all +internal interaction between Ethermint and Tendermint will utilize one of the supported encoding +formats: Protobuf, Amino or Hybrid of the previous two. + +## Transaction formats + + + +- Cosmos transactions +- Ethereum transaction + +## Signatures + +Ethermint supports [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) +signatures. A `Transaction` is expected to have a single signature for Ethereum routed transactions. +However, just as in Cosmos, Ethermint will support multiple signers for non-Ethereum transactions. +Signatures over the `Transaction` type are identical to Ethereum and the signatures will not be +duplicated in the embedding +[`auth.StdTx`](https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth#StdTx). + +## Next {hide} + +Learn about how [gas](./gas.md) is used on Ethermint {hide} diff --git a/docs/core/README.md b/docs/core/README.md new file mode 100644 index 0000000000..746c8fefa7 --- /dev/null +++ b/docs/core/README.md @@ -0,0 +1,13 @@ + + +# Core Concepts + +This repository contains reference documentation on the core concepts of Ethermint. + +1. [Encoding](./encoding.md) + +After reading the core concepts, head on to the [guides](../guides/README.md) to learn how to use Ethereum tooling with Ethermint. diff --git a/docs/core/encoding.md b/docs/core/encoding.md new file mode 100644 index 0000000000..23c97091a2 --- /dev/null +++ b/docs/core/encoding.md @@ -0,0 +1,73 @@ + + +# Encoding + +The `codec` is used everywhere in the Cosmos SDK to encode and decode structs and interfaces. The specific codec used in the Cosmos SDK is called `go-amino`. {synopsis} + +## Pre-requisite Readings + +- [Cosmos SDK Encoding](https://docs.cosmos.network/master/core/encoding.html) {prereq} + +## Encoding Formats + +The Cosmos SDK utilizes two binary wire encoding protocols, [Amino](https://github.com/tendermint/go-amino/) +and [Protocol Buffers](https://developers.google.com/protocol-buffers), where Amino +is an object encoding specification. It is a subset of Proto3 with an extension for +interface support. See the [Proto3 spec](https://developers.google.com/protocol-buffers/docs/proto3) +for more information on Proto3, which Amino is largely compatible with (but not with Proto2). + +Due to Amino having significant performance drawbacks, being reflection-based, and +not having any meaningful cross-language/client support, Protocol Buffers, specifically +[gogoprotobuf](https://github.com/gogo/protobuf/), is being used in place of Amino. +Note, this process of using Protocol Buffers over Amino is still an ongoing process. + +Binary wire encoding of types in the Cosmos SDK can be broken down into two main +categories, client encoding and store encoding. Client encoding mainly revolves +around transaction processing and signing, whereas store encoding revolves around +types used in state-machine transitions and what is ultimately stored in the Merkle +tree. + +For store encoding, protobuf definitions can exist for any type and will typically +have an Amino-based "intermediary" type. Specifically, the protobuf-based type +definition is used for serialization and persistence, whereas the Amino-based type +is used for business logic in the state-machine where they may converted back-n-forth. +Note, the Amino-based types may slowly be phased-out in the future so developers +should take note to use the protobuf message definitions where possible. + +In the `codec` package, there exists two core interfaces, `Marshaler` and `ProtoMarshaler`, +where the former encapsulates the current Amino interface except it operates on +types implementing the latter instead of generic `interface{}` types. + +In addition, there exists three implementations of `Marshaler`. The first being +`AminoCodec`, where both binary and JSON serialization is handled via Amino. The +second being `ProtoCodec`, where both binary and JSON serialization is handled +via Protobuf. Finally, `HybridCodec`, a codec that utilizes Protobuf for binary +serialization and Amino for JSON serialization. The `HybridCodec` is typically +the codec that used in majority in situations as it's easier to use for client +and state serialization. + +This means that modules may use Amino or Protobuf encoding but the types must +implement `ProtoMarshaler`. If modules wish to avoid implementing this interface +for their types, they may use an Amino codec directly. + +### Amino + +Every module uses an Amino codec to serialize types and interfaces. This codec typically +has types and interfaces registered in that module's domain only (e.g. messages), +but there are exceptions like `x/gov`. Each module exposes a `RegisterCodec` function +that allows a user to provide a codec and have all the types registered. An application +will call this method for each necessary module. + +### Protobuf + + + +## RLP + + + +## Next {hide} + +Learn how to deploy a Solidity smart contract on Ethermint using [Truffle](./../guides/truffle.md) {hide} diff --git a/docs/guides/README.md b/docs/guides/README.md new file mode 100644 index 0000000000..44b54cb81d --- /dev/null +++ b/docs/guides/README.md @@ -0,0 +1,14 @@ + + +# Guides + +This section contains different guides to use popular Ethereum tools with Ethermint. + +1. [Truffle](./truffle.md) +2. [Metamask](./metamask.md) +3. [Remix](./remix.md) +4. [Deploy Testnet on Cloud Provider](./cloud_testnet.md) diff --git a/docs/guides/cloud_testnet.md b/docs/guides/cloud_testnet.md new file mode 100644 index 0000000000..ddd2622ec9 --- /dev/null +++ b/docs/guides/cloud_testnet.md @@ -0,0 +1,88 @@ + + +# Deploy Testnet on Cloud Provider + +Learn how to deploy testnet to different cloud providers. {synopsis} + +## Pre-requisite Readings + +- [Testnet Quickstart](./../quickstart/testnet.md) {prereq} + +## Digital Ocean + +### Account Setup + +Head over to [Digital Ocean](https://www.digitalocean.com/) and create an account. + +DigitalOcean will want a public key that it can place on any Droplets we start, so that we can access them with a key that we know only we have. + +Let's create an SSH keypair now using `ssh-keygen -t rsa -b 4096` + +This will ask you for a file where you want to save the key which you can call something like - `digital-ocean-key`. + +It'll also ask for a passphrase - feel free to set one if you wish or you could leave it empty. If you created it in the same folder as we've been working out of, you'll see two files - one called `digital-ocean-key` and one called `digital-ocean-key.pub` - these are respectively your private and public keys. + +In your DigitalOcean account, on the bottom left hand side, there is a link for `'Security'`. Follow this link, and the next page will have an option to add an SSH key + +Click `'Add an SSH key'` and you'll be presented with a dialog to enter your key. Simply copy the contents of your `digital-ocean-key.pub` into the large text box (you can get the contents printed to the terminal with `cat digital-ocean-key.pub`). + +### Create Droplet + +Once you've added your SSH key. click on the `'Droplets'` link on the left, and then on the next page click `'Create Droplet'`. + +On this page, you'll be presented with a number of options for configuring your DigitalOcean Droplet, including the distribution, the plan, the size/cost per month, region, and authentication. Feel free to choose whichever settings work best for you. + +Under `'Authentication'`, select `'SSH Key'`, and select which keys you would like to use (like the one you created in the last step). You can also name your Droplet if you wish. When you're finished, click `'Create Droplet'` at the bottom. + +Wait a minute for your Droplet to start up. It'll appear under the `'Droplets'` panel with a green dot next to it when it is up and ready. At this point, we're ready to connect to it. + +### Deploy to Droplet + +#### Connect to Droplet + +Click on the started Droplet, and you'll see details about it. At the moment, we're interested in the IP address - this is the address that the Droplet is at on the internet. + +To access it, we'll need to connect to it using our previously created private key. From the same folder as that private key, run: + +```bash +ssh -i digital-ocean-key root@ +``` + +Now you are connected to the droplet. + +#### Install Ethermint + +Clone and build Ethermint in the droplet using `git`: + +```bash +go install https://github.com/ChainSafe/ethermint.git +``` + +Check that the binaries have been successfuly installed: + +```bash +ethermintd -h +ethermintcli -h +``` + +### Copy the Genesis File + +To connect the node to the existing testnet, fetch the testnet's `genesis.json` file and copy it into the new droplets config directory (i.e `$HOME/.ethermintd/config/genesis.json`). + +To do this ssh into both the testnet droplet and the new node droplet. + +On your local machine copy the genesis.json file from the testnet droplet to the new droplet using: + +```bash +scp -3 root@:$HOME/.ethermintd/config/genesis.json root@:$HOME/.ethermintd/config/genesis.json +``` + +### Start the Node + +Once the genesis file is copied over run `ethermind start` inside the node droplet. + +## Next {hide} + +Follow [Deploy node to public testnet](./deploy_node_on_public_testnet.md) \ No newline at end of file diff --git a/docs/guides/img/metamask_import.png b/docs/guides/img/metamask_import.png new file mode 100644 index 0000000000..21ab24f8da Binary files /dev/null and b/docs/guides/img/metamask_import.png differ diff --git a/docs/guides/img/metamask_network_settings.png b/docs/guides/img/metamask_network_settings.png new file mode 100644 index 0000000000..8ff683bf3b Binary files /dev/null and b/docs/guides/img/metamask_network_settings.png differ diff --git a/docs/guides/img/remix_deploy.png b/docs/guides/img/remix_deploy.png new file mode 100644 index 0000000000..3f2d53fd8d Binary files /dev/null and b/docs/guides/img/remix_deploy.png differ diff --git a/docs/guides/img/remix_deployed.png b/docs/guides/img/remix_deployed.png new file mode 100644 index 0000000000..26c4bc94cf Binary files /dev/null and b/docs/guides/img/remix_deployed.png differ diff --git a/docs/guides/img/remix_interact.png b/docs/guides/img/remix_interact.png new file mode 100644 index 0000000000..c38321d141 Binary files /dev/null and b/docs/guides/img/remix_interact.png differ diff --git a/docs/guides/metamask.md b/docs/guides/metamask.md new file mode 100644 index 0000000000..7f00e00710 --- /dev/null +++ b/docs/guides/metamask.md @@ -0,0 +1,66 @@ + + +# Metamask + +Connect your Metamask wallet with Ethermint on a localnet mode. {synopsis} + +## Start node and REST server + +Start the Ethermint node using your terminal: + +```bash +ethermintd start --pruning=nothing --rpc.unsafe --log_level "main:info,state:info,mempool:info" +``` + +::: tip +You can also start a node from scratch by running `./init.sh` from the Ethermint repository directory. This will generate a key called `mykey` that you can use on the next step. +::: + +In another tab start the REST server. Here replace `mykey` with the name of the key that you want to use and set the `chain-id` the chain identifier of your application. + +```bash +ethermintcli rest-server --laddr "tcp://localhost:8545" --unlock-key mykey --chain-id 1 +``` + +## Adding a custom Network for Ethermint + +One og the main limitations of using the default `Localhost 8545` network is that the tokens will be represented as `ETH`. + +Open the Metamask extension on your browser, you may have to log in to your Metamask account if you +are not already. Then click the top right circle and go to `Settings` > `Networks`. Press the `Add +Network` button and fill the form as shown below with your application `ChainID`. + +![metamask networks settings](./img/metamask_network_settings.png) + +## Import Account to Metamask + +Then close the settings, and go to `My Accounts` (top right circle) and select `Import Account`. You should see and image like the following one: + +![metamask import account page](./img/metamask_import.png) + +Now you can export your private key from the terminal using the following command. Again, make sure +to replace `mykey` with the name of the key that you want to export: + +```bash +ethermintcli keys unsafe-export-eth-key mykey +``` + +Go back to the browser and select the `Private Key` option. Then paste the private key exported from +the `unsafe-export-eth-key` command. + +Your account balance should show up as `1 APHOTON` and do transfers as usual. + +::: tip +If it takes some time to load the balance of the account, change the network to `Main Ethereum +Network` (or any other than `Localhost 8545` or `Ethermint`) and then switch back to `Ethermint`. +::: + +## Downloading State + +to see metamask logs, go to top right circle -> settings -> advanced -> download state logs. if you search through the json file for the account address you'll find the tx history + +## Known issues + +Currently, it's not possible to add custom tokens (even for APhotons) unless you deploy a token contract (eg: ERC20). diff --git a/docs/guides/remix.md b/docs/guides/remix.md new file mode 100644 index 0000000000..5377d65e31 --- /dev/null +++ b/docs/guides/remix.md @@ -0,0 +1,43 @@ + + +# Remix + +Set up a Remix Ethermint local development environment. {synopsis} + +## Pre-requisite Readings + +- [Installation](./../quickstart/installation.md) {prereq} +- [Run a node](./../quickstart/run_node.md) {prereq} +- [Metamask](./metamask.md) {prereq} + +[Remix](http://remix.ethereum.org/) is an in-browser IDE for [Solidity](https://github.com/ethereum/solidity) smart contracts. In this guide, we will learn how to deploy a contract to a running Ethermint network through Remix and interact with it. + +## Connect Ethermint account to Remix + +First, follow the steps in the [Metamask guide](./metamask.md) to import your Ethermint private key into Metamask. Start the Ethermint daemon and rest server. + +Once that is complete, go to [Remix](http://remix.ethereum.org/). There are some contracts in the File Explorer. Select any of these contracts. In this example, we use `Counter.sol` from the [Truffle](./truffle.md) guide. On the left-most bar, select the Solidity Compiler and compile the contract. + +Next, select the `Deploy and Run` option. Select `injected web3` as the environment. This will open a metamask popup for you to confirm connecting your Metamask to Remix. Hit confirm. + +You should see your account show up in the left-hand panel. + +![remix connected to ethermint](./img/remix_deploy.png) + +## Deploy and Interact + +Now that your account is connected, you are able to deploy the contract. Press the `Deploy` button. A metamask pop-up will appear asking you to confirm. Confirm the transaction. You should see a log for the deployment transaction in the ethermint daemon logs: + +```bash +I[2020-07-15|17:26:43.155] Added good transaction module=mempool tx=877A8E6600FA27EC2B2362719274314977B243671DC4E5F8796ED97FFC0CBE42 res="&{CheckTx:log:\"[]\" gas_wanted:121193 }" height=31 total=1 +``` + +Once the contract has been successfully deployed, you will see it show up in the `Deployed Contracts` section in the left-hand side, as well as a green check in the Remix console showing the transaction details. + +![deployed contract through remix](./img/remix_deployed.png) + +Now, you are able to interact with the contract through Remix. For `Counter.sol`, click `add`. This will open a Metamask pop-up asking you to confirm. Confirm the transaction. Then, click `getCounter` to get the count, which should be `1`. + +![interacting with deployed contract through remix](./img/remix_interact.png) diff --git a/docs/guides/truffle.md b/docs/guides/truffle.md new file mode 100644 index 0000000000..9c08780962 --- /dev/null +++ b/docs/guides/truffle.md @@ -0,0 +1,167 @@ + + +# Truffle + +Set up a Truffle Ethermint local development environment. {synopsis} + +## Pre-requisite Readings + +- [Installation](./../quickstart/installation.md) {prereq} +- [Run a node](./../quickstart/run_node.md) {prereq} + +[Truffle](https://www.trufflesuite.com/truffle) is a development framework for deploying and managing [Solidity](https://github.com/ethereum/solidity) smart contracts. In this guide, we will learn how to deploy a contract to a running Ethermint network. + +## Install dependencies + +First, install the latest Truffle version on your machine globally. + +```bash +yarn install truffle -g +``` + +You will also need to install Ethermint. Check this [document](./../quickstart/installation.md) for the full instructions. + +## Create Truffle Project + +In this step we will create a simple counter contract. Feel free to skip this step if you already have your own compiled contract. + +Create a new directory to host the contracts and initialize it + +```bash +mkdir ethermint-truffle +cd ethermint-truffle +``` + +Initialize the Truffle suite with: + +```bash +truffle init +``` + +Create `contracts/Counter.sol` containing the following contract: + +```javascript +pragma solidity ^0.5.11; + +contract Counter { + uint256 counter = 0; + + function add() public { + counter++; + } + + function subtract() public { + counter--; + } + + function getCounter() public view returns (uint256) { + return counter; + } +} +``` + +Compile the contract using the `compile` command: + +```bash +truffle compile +``` + +Create `test/counter_test.js` containing the following tests in Javascript using [Mocha](https://mochajs.org/): + +```javascript +const Counter = artifacts.require("Counter") + +contract('Counter', accounts => { + const from = accounts[0] + let counter + + before(async() => { + counter = await Counter.new() + }) + + it('should add', async() => { + await counter.add() + let count = await counter.getCounter() + assert(count == 1, `count was ${count}`) + }) +}) +``` + +## Truffle configuration + +Open `truffle-config.js` and uncomment the `development` section in `networks`: + +```javascript + development: { + host: "127.0.0.1", // Localhost (default: none) + port: 8545, // Standard Ethereum port (default: none) + network_id: "*", // Any network (default: none) + }, +``` + +This will allow your contract to connect to your Ethermint local node. + +## Start Node and REST server + +Start your local node using the following command on the Terminal + +```bash +# on the ~/ethermint/ directory +init.sh +``` + +::: tip +For further information on how to run a node, please refer to [this](./../quickstart/run_node.md) quickstart document. +::: + +In another Terminal wintdow/tab, start the [REST and JSON-RPC server](./../quickstart/clients.md#rest-and-tendermint-rpc.md): + +```bash +ethermintcli rest-server --laddr "tcp://localhost:8545" --unlock-key mykey--chain-id 8 --trace +``` + +## Deploy contract + +Back in the Truffle terminal, migrate the contract using + +```bash +truffle migrate --network development +``` + +You should see incoming deployment logs in the Ethermint daemon Terminal tab for each transaction (one to deploy `Migrations.sol` and the oether to deploy `Counter.sol`). + +```bash +I[2020-07-15|17:35:59.934] Added good transaction module=mempool tx=22245B935689918D332F58E82690F02073F0453D54D5944B6D64AAF1F21974E2 res="&{CheckTx:log:\"[]\" gas_wanted:6721975 }" height=3 total=1 +I[2020-07-15|17:36:02.065] Executed block module=state height=4 validTxs=1 invalidTxs=0 +I[2020-07-15|17:36:02.068] Committed state module=state height=4 txs=1 appHash=76BA85365F10A59FE24ADCA87544191C2D72B9FB5630466C5B71E878F9C0A111 +I[2020-07-15|17:36:02.981] Added good transaction module=mempool tx=84516B4588CBB21E6D562A6A295F1F8876076A0CFF2EF1B0EC670AD8D8BB5425 res="&{CheckTx:log:\"[]\" gas_wanted:6721975 }" height=4 total=1 +``` + +## Run Truffle tests + +Now, you can run the Truffle tests using the Ethermint node using the `test` command: + +```bash +truffle test --network development + +Using network 'development'. + + +Compiling your contracts... +=========================== +> Everything is up to date, there is nothing to compile. + + + + Contract: Counter + ✓ should add (5036ms) + + + 1 passing (10s) +``` + +## Next {hide} + +Learn how to connect Ethermint to [Metamask](./../guides/metamask.md) {hide} diff --git a/docs/intro/README.md b/docs/intro/README.md index 61ef3acb3c..24f8a5f5a9 100644 --- a/docs/intro/README.md +++ b/docs/intro/README.md @@ -1,50 +1,18 @@ -# Introduction - -## What is Ethermint - -Ethermint is a high throughput PoS blockchain that is fully compatible and -interoperable with Ethereum. In other words, it allows for running vanilla Ethereum -on top of [Tendermint](https://github.com/tendermint/tendermint) consensus via -the [Cosmos SDK](https://github.com/cosmos/cosmos-sdk/). This allows developers -to have all the desired features of Ethereum, while at the same time benefit -from Tendermint’s PoS implementation. Also, because it is built on top of the -Cosmos SDK, it will be able to exchange value with the rest of the Cosmos Ecosystem. - -Here’s a glance at some of the key features of Ethermint: + -* Web3 compatibility -* High throughput -* Horizontal scalability -* Transaction finality - -Ethermint enables these key features through: - -* Implementing Tendermint's ABCI application interface to manage the base Blockchain -* Leveraging [modules](https://github.com/cosmos/cosmos-sdk/tree/master/x/) and other mechanisms implemented by the Cosmos SDK -* Utilizing [`geth`](https://github.com/ethereum/go-ethereum) as a library to avoid code reuse and improve maintainability -* Exposing a fully compatible Web3 RPC layer for interacting with the system - -The sum of these features allows developers to leverage existing Ethereum ecosystem -tooling and software to seamlessly deploy smart contracts which interact with the rest of the Cosmos -ecosystem! +# Introduction -## In-depth Topics +This folder contains introduction material for Ethermint. -### Tendermint Core & the Application Blockchain Interface (ABCI) +1. [Overview](./overview.md) +1. [Architecture](./architecture.md) -Tendermint consists of two chief technical components: a blockchain consensus -engine and a generic application interface. The consensus engine, called -Tendermint Core, ensures that the same transactions are recorded on every machine -in the same order. The application interface, called the Application Blockchain -Interface (ABCI), enables the transactions to be processed in any programming -language. +After reading the introduction material, head over to the [basics](../basics/README.md) to learn more. -Tendermint has evolved to be a general purpose blockchain consensus engine that -can host arbitrary application states. Since Tendermint can replicate arbitrary -applications, it can be used as a plug-and-play replacement for the consensus -engines of other blockchains. Ethermint is such an example of an ABCI application -replacing Ethereum's PoW via Tendermint's consensus engine. +## Next {hide} -Another example of a cryptocurrency application built on Tendermint is the Cosmos -network. Tendermint is able to decompose the blockchain design by offering a very -simple API (ie. the ABCI) between the application process and consensus process. +Get an high-level [overview](./overview.md) of Ethermint {hide} diff --git a/docs/intro/architecture.md b/docs/intro/architecture.md new file mode 100644 index 0000000000..cad74168d3 --- /dev/null +++ b/docs/intro/architecture.md @@ -0,0 +1,38 @@ + + +# Architecture + +Learn how Ethermint's architecture leverages the Cosmos SDK Proof-of-Stake functionallity, EVM compatibility and fast-finality from Tendermint Core's BFT consensus. {synopsis} + +## Cosmos-SDK + + + +## Tendermint Core & the Application Blockchain Interface (ABCI) + +Tendermint consists of two chief technical components: a blockchain consensus +engine and a generic application interface. The consensus engine, called +Tendermint Core, ensures that the same transactions are recorded on every machine +in the same order. The application interface, called the Application Blockchain +Interface (ABCI), enables the transactions to be processed in any programming +language. + +Tendermint has evolved to be a general purpose blockchain consensus engine that +can host arbitrary application states. Since Tendermint can replicate arbitrary +applications, it can be used as a plug-and-play replacement for the consensus +engines of other blockchains. Ethermint is such an example of an ABCI application +replacing Ethereum's PoW via Tendermint's consensus engine. + +Another example of a cryptocurrency application built on Tendermint is the Cosmos +network. Tendermint is able to decompose the blockchain design by offering a very +simple API (ie. the ABCI) between the application process and consensus process. + +## EVM module + + + +## Next {hide} + +Learn how to run an Ethermint [node](./../quickstart/run_node.md) {hide} diff --git a/docs/intro/overview.md b/docs/intro/overview.md new file mode 100644 index 0000000000..85c8015dd0 --- /dev/null +++ b/docs/intro/overview.md @@ -0,0 +1,39 @@ + + +# High-level Overview + +## What is Ethermint + +Ethermint is a scalable, high-throughput Proof-of-Stake blockchain that is fully compatible and +interoperable with Ethereum. It's built using the [Cosmos SDK](https://github.com/cosmos/cosmos-sdk/) which runs on top of [Tendermint Core](https://github.com/tendermint/tendermint) consensus engine. + +Ethermint allows for running vanilla Ethereum as a [Cosmos](https://cosmos.network/) application-specific blockchain. This allows developers +to have all the desired features of Ethereum, while at the same time, benefit +from Tendermint’s PoS implementation. Also, because it is built on top of the +Cosmos SDK, it will be able to exchange value with the rest of the Cosmos Ecosystem through the Inter Blockchain Communication Protocol (IBC). + +### Features + +Here’s a glance at some of the key features of Ethermint: + +* Web3 compatibility +* High throughput via [Tendermint Core](https://github.com/tendermint/tendermint) +* Horizontal scalability via [IBC](https://github.com/cosmos/ics) +* Fast transaction finality +* [Hard Spoon](https://blog.cosmos.network/introducing-the-hard-spoon-4a9288d3f0df) + +Ethermint enables these key features through: + +* Implementing Tendermint Core's ABCI application interface to manage the blockchain +* Leveraging [modules](https://github.com/cosmos/cosmos-sdk/tree/master/x/) and other mechanisms implemented by the Cosmos SDK +* Utilizing [`geth`](https://github.com/ethereum/go-ethereum) as a library to avoid code reuse and improve maintainability. +* Exposing a fully compatible Web3 RPC layer for interacting with existing Ethereum clients and tooling (Metamask, Remix, Truffle, etc). + +The sum of these features allows developers to leverage existing Ethereum ecosystem tooling and +software to seamlessly deploy smart contracts which interact with the rest of the Cosmos ecosystem! + +## Next {hide} + +Learn about Ethermint's [architecture](./architectures.md) {hide} diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000000..d42edc976b --- /dev/null +++ b/docs/package.json @@ -0,0 +1,33 @@ +{ + "name": "docs", + "version": "1.0.0", + "description": "Ethermint Documentation", + "main": "index.js", + "scripts": { + "preserve": "./pre.sh", + "serve": "trap 'exit 0' SIGINT; vuepress dev --no-cache", + "postserve": "./post.sh", + "prebuild": "./pre.sh", + "build": "trap 'exit 0' SIGINT; vuepress build --no-cache", + "postbuild": "./post.sh" + }, + "keywords": [ + "ethermint", + "cosmos", + "ethereum", + "blockchain", + "cryptocurrency" + ], + "author": "ChainSafe Systems", + "license": "ISC", + "dependencies": { + "entities": "^2.0.3", + "vuepress-theme-cosmos": "^1.0.172" + }, + "devDependencies": { + "watchpack": "^1.7.2" + }, + "resolutions": { + "serialize-javascript": "^4.0.0" + } +} diff --git a/docs/post.sh b/docs/post.sh new file mode 100755 index 0000000000..4bc4e6416a --- /dev/null +++ b/docs/post.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +# Modules +rm -rf modules diff --git a/docs/pre.sh b/docs/pre.sh new file mode 100755 index 0000000000..8fc60db93b --- /dev/null +++ b/docs/pre.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +mkdir -p modules + +for D in ../x/*; do + if [ -d "${D}" ]; then + rm -rf "modules/$(echo $D | awk -F/ '{print $NF}')" + mkdir -p "modules/$(echo $D | awk -F/ '{print $NF}')" && cp -r $D/spec/* "$_" + fi +done + +cat ../x/README.md | sed 's/\.\/x/\/modules/g' | sed 's/spec\/README.md//g' \ No newline at end of file diff --git a/docs/quickstart/README.md b/docs/quickstart/README.md new file mode 100644 index 0000000000..f4e17d5072 --- /dev/null +++ b/docs/quickstart/README.md @@ -0,0 +1,23 @@ + + +# Quick Start + +This repository contains reference documentation on how to install and run an Etheremint full node. + +1. [Installation](./run_node.md) +2. [Run a Node](./run_node.md) +3. [Testnet](./testnet.md) +4. [Validator Setup](./validator-setup.md) +5. [Upgrade](./upgrade.md) +6. [Clients](./clients.md) +7. [Events](./events.md) + +After going throught the Quick Start contents, head over to the [basics](./../basics/README.md) to learn more. + +## Next {hide} + +Learn how to [install](./../quickstart/intallation.md) Ethermint {hide} diff --git a/docs/quickstart/clients.md b/docs/quickstart/clients.md new file mode 100644 index 0000000000..da3ba5bc9c --- /dev/null +++ b/docs/quickstart/clients.md @@ -0,0 +1,62 @@ + + +# Clients + +Learn how to connect a client to a running node. {synopsis} + +## Pre-requisite Readings + +- [Run a Node](./run_node.md) {prereq} + +## Client Integrations + +### Command Line Interface + +Ethermint is integrated with a CLI client that can be used to send transactions and query the state from each module. + +```bash +# available query commands +ethermintcli query -h + +# available transaction commands +ethermintcli tx -h +``` + +### Client Servers + +The Ethermint client supports both [REST endpoints](https://cosmos.network/rpc) from the SDK and Ethereum's [JSON-RPC](https://eth.wiki/json-rpc/API). + +#### REST and Tendermint RPC + +Ethermint exposes REST endpoints for all the integrated Cosmos-SDK modules. This makes it easier for wallets and block explorers to interact with the proof-of-stake logic. + +To run the REST Server, you need to run the Ethermint daemon (`ethermintd`) and then execute (in another +process): + +```bash +ethermintcli rest-server --laddr "tcp://localhost:8545" --unlock-key $KEY --chain-id $CHAINID --trace +``` + +You should see the logs from the REST and the RPC server. + +```bash +I[2020-07-17|16:54:35.037] Starting application REST service (chain-id: "8")... module=rest-server +I[2020-07-17|16:54:35.037] Starting RPC HTTP server on 127.0.0.1:8545 module=rest-server +``` + +#### Ethereum JSON-RPC server + +Ethermint also supports most of the standard web3 [JSON-RPC +APIs](https://eth.wiki/json-rpc/API) to connect with existing web3 tooling. + +::: tip +Some of the JSON-RPC API [namespaces](https://geth.ethereum.org/docs/rpc/server) are currently under development. +::: + +To connect to the JSON-PRC server, use the `rest-server` command as shown on the section above. Then, you can point any Ethereum development tooling to `http://localhost:8545` or whatever port you choose with the listen address flag (`--laddr`). + +## Next {hide} + +Process and subscribe to [events](./events.md) via websockets {hide} diff --git a/docs/quickstart/events.md b/docs/quickstart/events.md new file mode 100644 index 0000000000..fef3f29a96 --- /dev/null +++ b/docs/quickstart/events.md @@ -0,0 +1,134 @@ + + +# Events + +`Event`s are objects that contain information about the execution of the application. They are +mainly used by service providers like block explorers and wallet to track the execution of various +messages and index transactions. {synopsis} + +## Pre-requisite Readings + +- [Cosmos SDK Events](https://docs.cosmos.network/master/core/events.html) {prereq} +- [Ethereum's PubSub JSON-RPC API](https://geth.ethereum.org/docs/rpc/pubsub) {prereq} + +## Subscribing to Events + +### SDK and Tendermint Events + +It is possible to subscribe to `Events` via Tendermint's [Websocket](https://tendermint.com/docs/app-dev/subscribing-to-events-via-websocket.html#subscribing-to-events-via-websocket). +This is done by calling the `subscribe` RPC method via Websocket: + +```json +{ + "jsonrpc": "2.0", + "method": "subscribe", + "id": "0", + "params": { + "query": "tm.event='eventCategory' AND eventType.eventAttribute='attributeValue'" + } +} +``` + +The main `eventCategory` you can subscribe to are: + +- `NewBlock`: Contains `events` triggered during `BeginBlock` and `EndBlock`. +- `Tx`: Contains `events` triggered during `DeliverTx` (i.e. transaction processing). +- `ValidatorSetUpdates`: Contains validator set updates for the block. + +These events are triggered from the `state` package after a block is committed. You can get the full +list of `event` categories +[here](https://godoc.org/github.com/tendermint/tendermint/types#pkg-constants). + +The `type` and `attribute` value of the `query` allow you to filter the specific `event` you are +looking for. For example, a `MsgEthereumTx` transaction triggers an `event` of type `ethermint` and +has `sender` and `recipient` as `attributes`. Subscribing to this `event` would be done like so: + +```json +{ + "jsonrpc": "2.0", + "method": "subscribe", + "id": "0", + "params": { + "query": "tm.event='Tx' AND ethereum.recipient='hexAddress'" + } +} +``` + +where `hexAddress` is an Ethereum hex address (eg: `0x1122334455667788990011223344556677889900`). + +### Ethereum JSON-RPC Events + +Ethermint also supports the Ethereum [JSON-RPC](https://eth.wiki/json-rpc/API) filters calls to +subscribe to [state logs](https://eth.wiki/json-rpc/API#eth_newfilter), +[blocks](https://eth.wiki/json-rpc/API#eth_newblockfilter) or [pending +transactions](https://eth.wiki/json-rpc/API#eth_newpendingtransactionfilter) changes. + +Under the hood, it uses the Tendermint RPC client's event system to process subscriptions that are +then formatted to Ethereum-compatible events. + +```bash +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_newBlockFilter","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +{"jsonrpc":"2.0","id":1,"result":"0x3503de5f0c766c68f78a03a3b05036a5"} +``` + +Then you can check if the state chages with the [`eth_getFilterChanges`](https://eth.wiki/json-rpc/API#eth_getfilterchanges) call: + +```bash +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getFilterChanges","params":["0x3503de5f0c766c68f78a03a3b05036a5"],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +{"jsonrpc":"2.0","id":1,"result":["0x7d44dceff05d5963b5bc81df7e9f79b27e777b0a03a6feca09f3447b99c6fa71","0x3961e4050c27ce0145d375255b3cb829a5b4e795ac475c05a219b3733723d376","0xd7a497f95167d63e6feca70f344d9f6e843d097b62729b8f43bdcd5febf142ab","0x55d80a4ba6ef54f2a8c0b99589d017b810ed13a1fda6a111e1b87725bc8ceb0e","0x9e8b92c17280dd05f2562af6eea3285181c562ebf41fc758527d4c30364bcbc4","0x7353a4b9d6b35c9eafeccaf9722dd293c46ae2ffd4093b2367165c3620a0c7c9","0x026d91bda61c8789c59632c349b38fd7e7557e6b598b94879654a644cfa75f30","0x73e3245d4ddc3bba48fa67633f9993c6e11728a36401fa1206437f8be94ef1d3"]} +``` + +## Websocket Connection + +### Tendermint Websocket + +To start a connection with the Tendermint websocket you need to define the address with the `--node` +flag when initializing the REST server (default `tcp://localhost:26657`): + +```bash +ethermintcli rest-server --laddr "tcp://localhost:8545" --node "tcp://localhost:8080" --unlock-key --chain-id +``` + +Then, start a websocket subscription with [ws](https://github.com/hashrocket/ws) + +```bash +# connect to tendermint websocet at port 8080 as defined above +ws ws://localhost:8080/websocket + +# subscribe to new Tendermint block headers +> { "jsonrpc": "2.0", "method": "subscribe", "params": ["tm.event='NewBlockHeader'"], "id": 1 } +``` + +### Ethereum Websocket + +Since Ethermint runs uses Tendermint Core as it's consensus Engine and it's built with the Cosmos +SDK framework, it inherits the event format from them. However, in order to support the native Web3 +compatibility for websockets of the [Ethereum's +PubSubAPI](https://geth.ethereum.org/docs/rpc/pubsub), Ethermint needs to cast the Tendermint +responses retreived into the Ethereum types. + +You can start a connection with the Ethereum websocket using the `--wsport` flag when initializing +the REST server (default `8546`): + +```bash +ethermintcli rest-server --laddr "tcp://localhost:8545" --wsport 8546 --unlock-key --chain-id +``` + +Then, start a websocket subscription with [ws](https://github.com/hashrocket/ws) + +```bash +# connect to tendermint websocet at port 8546 as defined above +ws ws://localhost:8546/ + +# subscribe to new Ethereum-formatted block Headers +> {"id": 1, "method": "eth_subscribe", "params": ["newHeads", {}]} +< {"jsonrpc":"2.0","result":"0x44e010cb2c3161e9c02207ff172166ef","id":1} +``` + +## Next {hide} + +Learn about Ethermint [accounts](./../basic/accounts.md) {hide} diff --git a/docs/quickstart/installation.md b/docs/quickstart/installation.md new file mode 100644 index 0000000000..7312386e6f --- /dev/null +++ b/docs/quickstart/installation.md @@ -0,0 +1,50 @@ + + +# Installation + +## Binaries + +Clone and build Ethermint using `git`: + +```bash +git clone https://github.com/ChainSafe/ethermint.git +cd ethermint +make install +``` + +Check that the binaries have been successfuly installed: + +```bash +ethermintd -h +ethermintcli -h +``` + +## Docker + +You can build Ethermint using Docker by running: + +```bash +make docker-build +``` + +This will install the binaries on the `./build` directory. Now, check that the binaries have been +successfuly installed: + +```bash +ethermintd -h +ethermintcli -h +``` + +## Releases + +::: warning +Ethermint is under VERY ACTIVE DEVELOPMENT and should be treated as pre-alpha software. This means it is not meant to be run in production, its APIs are subject to change without warning and should not be relied upon, and it should not be used to hold any value. We will remove this warning when we have a release that is stable, secure, and properly tested. +::: + +You can also download a specific release available on the [Ethermint repository](https://github.com/ChainSafe/ethermint/releases) + +## Next {hide} + +Learn how to [run a node](./.run_node.md) {hide} diff --git a/docs/quickstart/run_node.md b/docs/quickstart/run_node.md new file mode 100644 index 0000000000..1561ef0000 --- /dev/null +++ b/docs/quickstart/run_node.md @@ -0,0 +1,123 @@ + + +# Run a Node + +Run a local node and start the REST and JSON-RPC clients {synopsis} + +## Pre-requisite Readings + +- [Installation](./installation.md) {prereq} + +## Automated deployment + +Run the local node with faucet enabled: + +::: warning +The script below will remove any pre-existing binaries installed. Use the manual deploy if you want +to keep your binaries and configuration files. +::: + +```bash +./init.sh +``` + +In another terminal window or tab, run the Ethereum JSON-RPC server as well as the SDK REST server: + +```bash +ethermintcli rest-server --laddr "tcp://localhost:8545" --unlock-key mykey --chain-id 8 +``` + +## Manual deployment + +The instructions for setting up a brand new full node from scratch are the the same as running a +[single node local testnet](./testnet.md#single-node-local-manual-testnet). + +## Start node + +To start your node, just type: + +```bash +ethermintd start +``` + +## Key Management + +To run a node with the same key every time: replace `ethermintcli keys add $KEY` in `./init.sh` with: + +```bash +echo "your mnemonic here" | ethermintcli keys add $KEY --recover +``` + +::: tip +Ethermint currently only supports 24 word mnemonics. +::: + +You can generate a new key/mnemonic with: + +```bash +ethermintcli keys add $KEY +``` + +To export your ethermint key as an ethereum private key (for use with Metamask for example): + +```bash +ethermintcli keys unsafe-export-eth-key $KEY +``` + +For more about the available key commands, use the `--help` flag + +```bash +ethermintcli keys -h +``` + +### Keyring backend options + +The instructions above include commands to use `test` as the `keyring-backend`. This is an unsecured +keyring that doesn't require entering a password and should not be used in production. Otherwise, +Ethermint supports using a file or OS keyring backend for key storage. To create and use a file +stored key instead of defaulting to the OS keyring, add the flag `--keyring-backend file` to any +relevant command and the password prompt will occur through the command line. This can also be saved +as a CLI config option with: + +```bash +ethermintcli config keyring-backend file +``` + +## Clearing data from chain + +### Reset Data + +Alternatively, you can **reset** the blockchain database, remove the node's address book files, and reset the `priv_validator.json` to the genesis state. + +::: danger +If you are running a **validator node**, always be careful when doing `ethermintd unsafe-reset-all`. You should never use this command if you are not switching `chain-id`. +::: + +::: danger +**IMPORTANT**: Make sure that every node has a unique `priv_validator.json`. **Do not** copy the `priv_validator.json` from an old node to multiple new nodes. Running two nodes with the same `priv_validator.json` will cause you to double sign! +::: + +First, remove the outdated files and reset the data. + +```bash +rm $HOME/.ethermintd/config/addrbook.json $HOME/.ethermintd/config/genesis.json +ethermintd unsafe-reset-all +``` + +Your node is now in a pristine state while keeping the original `priv_validator.json` and `config.toml`. If you had any sentry nodes or full nodes setup before, your node will still try to connect to them, but may fail if they haven't also been upgraded. + +### Delete Data + +Data for the Daemon and CLI binaries should be stored at `~/.ethermintd` and `~/.ethermintcli`, respectively by default. To **delete** the existing binaries and configuration, run: + +```bash +rm -rf ~/.emint* +``` + +To clear all data except key storage (if keyring backend chosen) and then you can rerun the full node installation commands from above to start the node again. + +## Next {hide} + +Learn about running a Ethermint [testnet](./testnet.md) {hide} diff --git a/docs/quickstart/testnet.md b/docs/quickstart/testnet.md new file mode 100644 index 0000000000..6303c05c04 --- /dev/null +++ b/docs/quickstart/testnet.md @@ -0,0 +1,340 @@ + + +# Testnet + +Learn how to deploy a local testnet or connect to an existing public one {synopsis} + +## Pre-requisite Readings + +- [Install Ethermint](./installation.md) {prereq} +- [Install Docker](https://docs.docker.com/engine/installation/) {prereq} +- [Install docker-compose](https://docs.docker.com/compose/install/) {prereq} + + +## Single-node, Local, Manual Testnet + +This guide helps you create a single validator node that runs a network locally for testing and other development related uses. + +### Initialize node + +```bash +$MONIKER=testing +$KEY=mykey +$CHAINID="ethermint-1" + +ethermintd init $MONIKER --chain-id=$CHAINID +``` + +::: warning +Monikers can contain only ASCII characters. Using Unicode characters will render your node unreachable. +::: + +You can edit this `moniker` later, in the `$(HOME)/.ethermintd/config/config.toml` file: + +```toml +# A custom human readable name for this node +moniker = "" +``` + +You can edit the `$HOME/.ethermintd/config/app.toml` file in order to enable the anti spam mechanism and reject incoming transactions with less than the minimum gas prices: + +```toml +# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +##### main base config options ##### + +# The minimum gas prices a validator is willing to accept for processing a +# transaction. A transaction's fees must meet the minimum of any denomination +# specified in this config (e.g. 10aphoton). + +minimum-gas-prices = "" +``` + +### Genesis Procedure + +```bash +# Create a key to hold your account +ethermintcli keys add $KEY + +# Add that key into the genesis.app_state.accounts array in the genesis file +# NOTE: this command lets you set the number of coins. Make sure this account has some coins +# with the genesis.app_state.staking.params.bond_denom denom, the default is staking +ethermintd add-genesis-account $(ethermintcli keys show validator -a) 1000000000stake,10000000000aphoton + +# Generate the transaction that creates your validator +ethermintd gentx --name $KEY + +# Add the generated bonding transaction to the genesis file +ethermintd collect-gentxs + +# Finally, check the correctness of the genesis.json file +ethermintd validate-genesis +``` + +### Run Testnet + +Now its safe to start the daemon: + +```bash +ethermintd start +``` + +You can then stop the node using Ctrl+C. + +## Multi-node, Local, Automated Testnet + +### Build Testnet & Start Testnet + +To build start a 4 node testnet run: + +```bash +make localnet-start +``` + +This command creates a 4-node network using the `ethermintdnode` Docker image. +The ports for each node are found in this table: + +| Node ID | P2P Port | Tendermint RPC Port | REST/ Ethereum JSON-RPC Port | WebSocket Port | +|------------------|----------|---------------------|------------------------------|----------------| +| `ethermintnode0` | `26656` | `26657` | `8545` | `8546` | +| `ethermintnode1` | `26659` | `26660` | `8547` | `8548` | +| `ethermintnode2` | `26661` | `26662` | `8549` | `8550` | +| `ethermintnode3` | `26663` | `26664` | `8551` | `8552` | + +To update the binary, just rebuild it and restart the nodes + +```bash +make localnet-start +``` + +The command above command will run containers in the background using Docker compose. You will see the network being created: + +```bash +... +Creating network "chainsafe-ethermint_localnet" with driver "bridge" +Creating ethermintdnode0 ... done +Creating ethermintdnode2 ... done +Creating ethermintdnode1 ... done +Creating ethermintdnode3 ... done +``` + + +### Stop Testnet + +Once you are done, execute: + +```bash +make localnet-stop +``` + +### Configuration + +The `make localnet-start` creates files for a 4-node testnet in `./build` by +calling the `ethermintd testnet` command. This outputs a handful of files in the +`./build` directory: + +```bash +tree -L 3 build/ + +build/ +├── ethermintcli +├── ethermintd +├── gentxs +│   ├── node0.json +│   ├── node1.json +│   ├── node2.json +│   └── node3.json +├── node0 +│   ├── ethermintcli +│   │   ├── key_seed.json +│   │   └── keyring-test-cosmos +│   └── ethermintd +│   ├── config +│   ├── data +│   └── ethermintd.log +├── node1 +│   ├── ethermintcli +│   │   ├── key_seed.json +│   │   └── keyring-test-cosmos +│   └── ethermintd +│   ├── config +│   ├── data +│   └── ethermintd.log +├── node2 +│   ├── ethermintcli +│   │   ├── key_seed.json +│   │   └── keyring-test-cosmos +│   └── ethermintd +│   ├── config +│   ├── data +│   └── ethermintd.log +└── node3 + ├── ethermintcli + │   ├── key_seed.json + │   └── keyring-test-cosmos + └── ethermintd + ├── config + ├── data + └── ethermintd.log +``` + +Each `./build/nodeN` directory is mounted to the `/ethermintd` directory in each container. + +### Logging + +In order to see the logs of a particular node you can use the following command: + +```bash +# node 0: daemon logs +docker exec ethermintdnode0 tail ethermintd.log + +# node 0: REST & RPC logs +docker exec ethermintdnode0 tail ethermintcli.log +``` + +The logs for the daemon will look like: + +```bash +I[2020-07-29|17:33:52.452] starting ABCI with Tendermint module=main +E[2020-07-29|17:33:53.394] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 272a247b837653cf068d39efd4c407ffbd9a0e6f@192.168.10.5:26656" +E[2020-07-29|17:33:53.394] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 3e05d3637b7ebf4fc0948bbef01b54d670aa810a@192.168.10.4:26656" +E[2020-07-29|17:33:53.394] Can't add peer's address to addrbook module=p2p err="Cannot add non-routable address 689f8606ede0b26ad5b79ae244c14cc67ab4efe7@192.168.10.3:26656" +I[2020-07-29|17:33:58.828] Executed block module=state height=88 validTxs=0 invalidTxs=0 +I[2020-07-29|17:33:58.830] Committed state module=state height=88 txs=0 appHash=90CC5FA53CF8B5EC49653A14DA20888AD81C92FCF646F04D501453FD89FCC791 +I[2020-07-29|17:34:04.032] Executed block module=state height=89 validTxs=0 invalidTxs=0 +I[2020-07-29|17:34:04.034] Committed state module=state height=89 txs=0 appHash=0B54C4DB1A0DACB1EEDCD662B221C048C826D309FD2A2F31FF26BAE8D2D7D8D7 +I[2020-07-29|17:34:09.381] Executed block module=state height=90 validTxs=0 invalidTxs=0 +I[2020-07-29|17:34:09.383] Committed state module=state height=90 txs=0 appHash=75FD1EE834F0669D5E717C812F36B21D5F20B3CCBB45E8B8D415CB9C4513DE51 +I[2020-07-29|17:34:14.700] Executed block module=state height=91 validTxs=0 invalidTxs=0 +``` + +::: tip +You can disregard the `Can't add peer's address to addrbook` warning. As long as the blocks are +being produced and the app hashes are the same for each node, there should not be any issues. +::: + +Whereas the logs for the REST & RPC server would look like: + +```bash +I[2020-07-30|09:39:17.488] Starting application REST service (chain-id: "7305661614933169792")... module=rest-server +I[2020-07-30|09:39:17.488] Starting RPC HTTP server on 127.0.0.1:8545 module=rest-server +... +``` + +#### Follow Logs + +You can also watch logs as they are produced via Docker with the `--follow` (`-f`) flag, for +example: + +```bash +docker logs -f ethermintdnode0 +``` + +### Interact With the Testnet + +#### Ethereum JSON RPC & Websocket Ports + +To interact with the testnet via WebSockets or RPC/API, you will send your request to the corresponding ports: + +| Eth JSON-RPC | Eth WS | +|--------------|--------| +| `8545` | `8546` | + +You can send a curl command such as: + +```bash +curl -X POST --data '{"jsonrpc":"2.0","method":"eth_accounts","params":[],"id":1}' -H "Content-Type: application/json" 192.162.10.1:8545 +``` + +::: tip +The IP address will be the public IP of the docker container. +::: + +Additional instructions on how to interact with the WebSocket can be found on the [events documentation](./events.md#ethereum-websocket). + +### Keys & Accounts + +To interact with `ethermintcli` and start querying state or creating txs, you use the +`ethermintcli` directory of any given node as your `home`, for example: + +```bash +ethermintcli keys list --home ./build/node0/ethermintcli +``` + +Now that accounts exists, you may create new accounts and send those accounts +funds! + +::: tip +**Note**: Each node's seed is located at `./build/nodeN/ethermintcli/key_seed.json` and can be restored to the CLI using the `ethermintcli keys add --restore` command +::: + +### Special Binaries + +If you have multiple binaries with different names, you can specify which one to run with the BINARY environment variable. The path of the binary is relative to the attached volume. For example: + +```bash +# Run with custom binary +BINARY=ethermint make localnet-start +``` + +## Multi-node, Public, Manual Testnet + +If you are looking to connect to a persistent public testnet. You will need to manually configure your node. + +### Genesis and Seeds + +#### Copy the Genesis File + +::: tip +If you want to start a network from scratch, you will need to start the [genesis procedure](#genesis-procedure) by creating a `genesis.json` and submit + collect the genesis transactions from the [validators](./validator-setup.md). +::: + +If you want to connect to an existing testnet, fetch the testnet's `genesis.json` file and copy it into the `ethermintd`'s config directory (i.e `$HOME/.ethermintd/config/genesis.json`). + +Then verify the correctness of the genesis configuration file: + +```bash +ethermintd validate-genesis +``` + +#### Add Seed Nodes + +Your node needs to know how to find peers. You'll need to add healthy seed nodes to `$HOME/.ethermintd/config/config.toml`. If those seeds aren't working, you can find more seeds and persistent peers on an existing explorer. + +For more information on seeds and peers, you can the Tendermint [P2P documentation](https://docs.tendermint.com/master/spec/p2p/peer.html). + +#### Start testnet + +The final step is to [start the nodes](./run_node.md#start-node). Once enough voting power (+2/3) from the genesis validators is up-and-running, the testnet will start producing blocks. + +## Testnet faucet + +Once the ethermint daemon is up and running, you can request tokens to your address using the `faucet` module: + +```bash +# query your initial balance +ethermintcli q bank balances $(ethermintcli keys show -a) + +# send a tx to request tokens to your account address +ethermintcli tx faucet request 100aphoton --from + +# query your balance after the request +ethermintcli q bank balances $(ethermintcli keys show -a) +``` + +You can also check to total amount funded by the faucet and the total supply of the chain via: + +```bash +# total amount funded by the faucet +ethermintcli q faucet funded + +# total supply +ethermintcli q supply total +``` + +## Next {hide} + +Learn about how to setup a [validator](./validator-setup.md) node on Ethermint {hide} diff --git a/docs/quickstart/upgrade.md b/docs/quickstart/upgrade.md new file mode 100644 index 0000000000..52c4c78535 --- /dev/null +++ b/docs/quickstart/upgrade.md @@ -0,0 +1,97 @@ + + +# Upgrade Node + +Learn how to upgrade your full node to the latest software version {synopsis} + +## Software Upgrade + +These instructions are for full nodes that have ran on previous versions of and would like to upgrade to the latest testnet. + +First, stop your instance of `ethermintd`. Next, upgrade the software: + +```bash +cd ethermint +git fetch --all && git checkout +make install +``` + +::: tip +If you have issues at this step, please check that you have the latest stable version of GO installed. +::: + +You will need to ensure that the version installed matches the one needed for th testnet. Check the Ethermint [release page](https://github.com/ChainSafe/ethermint/releases) for details on each release. + +## Upgrade Genesis File + +:::warning +If the new version you are upgrading to has breaking changes, you will have to restart your chain. If it is **not** breaking, you can skip to [Restart](#restart-node). +::: + +To upgrade the genesis file, you can either fetch it from a trusted source or export it locally using the `ethermintd export` command. + +### Fetch from a Trusted Source + +If you are joining an existing testnet, you can fetch the genesis from the appropriate testnet source/repository where the genesis file is hosted. + +Save the new genesis as `new_genesis.json`. Then, replace the old `genesis.json` with `new_genesis.json`. + +```bash +cd $HOME/.ethermintd/config +cp -f genesis.json new_genesis.json +mv new_genesis.json genesis.json +``` + +Finally, go to the [reset data](./run_node.md#reset-data) section. + +### Export State to a new Genesis locally + +Ethermint can dump the entire application state to a JSON file. This, besides upgrades, can be +useful for manual analysis of the state at a given height. + +Export state with: + +```bash +ethermintd export > new_genesis.json +``` + +You can also export state from a particular height (at the end of processing the block of that height): + +```bash +ethermintd export --height [height] > new_genesis.json +``` + +If you plan to start a new network for 0 height (i.e genesis) from the exported state, export with the `--for-zero-height` flag: + +```bash +ethermintd export --height [height] --for-zero-height > new_genesis.json +``` + +Then, replace the old `genesis.json` with `new_genesis.json`. + +```bash +cp -f genesis.json new_genesis.json +mv new_genesis.json genesis.json +``` + +At this point, you might want to run a script to update the exported genesis into a genesis state that is compatible with your new version. + +You can use the `migrate` command to migrate from a given version to the next one (eg: `v0.X.X` to `v1.X.X`): + +```bash +ethermintd migrate [target-version] [/path/to/genesis.json] --chain-id= --genesis-time= +``` + +## Restart Node + +To restart your node once the new genesis has been updated, use the `start` command: + +```bash +ethermintd start +``` + +## Next {hide} + +Learn about how to setup a [validator](./validator-setup.md) node on Ethermint {hide} diff --git a/docs/quickstart/validator-setup.md b/docs/quickstart/validator-setup.md new file mode 100644 index 0000000000..5aef4fd3b1 --- /dev/null +++ b/docs/quickstart/validator-setup.md @@ -0,0 +1,124 @@ + + +# Run a Validator + +Configure a validator node to propose blocks and earn staking rewards {synopsis} + +## Pre-requisite Readings + +- [Installation](./installation.md) {prereq} +- [Run a Full Node](./run_node.md) {prereq} + +## What is a Validator? + +[Validators](https://hub.cosmos.network/master/validators/overview.html) are responsible for committing new blocks to the blockchain through voting. A validator's stake is slashed if they become unavailable or sign blocks at the same height. Please read about [Sentry Node Architecture](https://hub.cosmos.network/master/validators/validator-faq.html#how-can-validators-protect-themselves-from-denial-of-service-attacks) to protect your node from DDOS attacks and to ensure high-availability. + +::: danger Warning +If you want to become a validator for `mainnet`, you should [research security](https://hub.cosmos.network/master/validators/security.html). +::: + +You may want to skip the next section if you have already set up a [full node](../emint-tutorials/join-mainnet.md). + +## Create Your Validator + +Your `cosmosvalconspub` consensus public key fron tendermint can be used to create a new validator by staking tokens. You can find your validator pubkey by running: + +```bash +ethermintd tendermint show-validator +``` + +To create your validator, just use the following command: + +```bash +ethermintcli tx staking create-validator \ + --amount=1000000aphoton \ + --pubkey=$(ethermintd tendermint show-validator) \ + --moniker= \ + --chain-id= \ + --commission-rate="0.10" \ + --commission-max-rate="0.20" \ + --commission-max-change-rate="0.01" \ + --min-self-delegation="1" \ + --gas="auto" \ + --gas-prices="0.025uatom" \ + --from= +``` + +::: tip +When specifying commission parameters, the `commission-max-change-rate` is used to measure % _point_ change over the `commission-rate`. E.g. 1% to 2% is a 100% rate increase, but only 1 percentage point. +::: + +::: tip +`Min-self-delegation` is a stritly positive integer that represents the minimum amount of self-delegated voting power your validator must always have. A `min-self-delegation` of 1 means your validator will never have a self-delegation lower than `1000000aphoton` +::: + +You can confirm that you are in the validator set by using a third party explorer. + +## Genesis Transactions + +A genesis transaction (aka `gentx`) is a JSON file carrying a self-delegation from a validator. All genesis transactions are collected by a genesis coordinator and validated against an initial `genesis.json` file. + +A `gentx` does three things: + +1. Makes the `validator` account you created into a validator operator account (i.e. the account that controls the validator). +2. Self-delegates the provided `amount` of staking tokens. +3. Link the operator account with a Tendermint node pubkey that will be used for signing blocks. If no `--pubkey` flag is provided, it defaults to the local node pubkey created via the `ethermintd init` command above. + +If you want to participate in genesis as a validator, you need to justify that +you have some stake at genesis, create one (or multiple) transactions to bond this stake to your validator address, and include this transaction in the genesis file. + +Your `cosmosvalconspub`, as shown on the section above, can be used to create a validator transaction on genesis as well. + +Next, craft your `ethermintd gentx` command: + +::: tip +When specifying commission parameters, the `commission-max-change-rate` is used to measure % _point_ change over the `commission-rate`. E.g. 1% to 2% is a 100% rate increase, but only 1 percentage point. +::: + +```bash +ethermintd gentx \ + --amount \ + --commission-rate \ + --commission-max-rate \ + --commission-max-change-rate \ + --pubkey $(ethermintd tendermint show-validator) \ + --name $KEY +``` + +::: tip +For more on `gentx`, use the help flag: `ethermintd gentx -h` +::: + +## Confirm Your Validator is Running + +Your validator is active if the following command returns anything: + +```bash +ethermintcli query tendermint-validator-set | grep "$(ethermintd tendermint show-validator)" +``` + +You should now see your validator in one of the block explorers. You are looking for the `bech32` +encoded `address` in the `~/.ethermintd/config/priv_validator.json` file. + +::: tip +To be in the validator set, you need to have more total voting power than the 100th validator. +::: + +## Halt Your Validator Node + +When attempting to perform routine maintenance or planning for an upcoming coordinated +upgrade, it can be useful to have your validator systematically and gracefully halt the chain and shutdown the node. + +You can achieve this by setting one of the following flags during when using the `ethermintd start` command: + +- `--halt-height`: to the block height at which to shutdown the node +- `--halt-time`: to the minimum block time (in Unix seconds) at which to shutdown the node + +The node will stop processing blocks with a zero exit code at that given height/time after +committing the block. + +## Next {hide} + +Start and connect a [client](./clients.md) to a running network {hide} diff --git a/docs/spec/evm/README.md b/docs/spec/evm/README.md deleted file mode 100644 index 5dc2294016..0000000000 --- a/docs/spec/evm/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# EVM - -TODO diff --git a/docs/spec/transactions/README.md b/docs/spec/transactions/README.md deleted file mode 100644 index 7030250761..0000000000 --- a/docs/spec/transactions/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# Transactions - -> NOTE: The specification documented below is still highly active in development -and subject to change. - -## Routing - -Ethermint needs to parse and handle transactions routed for both the EVM and for -the Cosmos hub. We attempt to achieve this by mimicking -[Geth's](https://github.com/ethereum/go-ethereum) `Transaction` structure and -treat it as a unique Cosmos SDK message type. An Ethereum transaction is a single -[`sdk.Msg`](https://godoc.org/github.com/cosmos/cosmos-sdk/types#Msg) contained -in an [`auth.StdTx`](https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth#StdTx). -All relevant Ethereum transaction information is contained in this message. This -includes the signature, gas, payload, etc. - -Being that Ethermint implements the Tendermint ABCI application interface, as -transactions are consumed, they are passed through a series of handlers. Once such -handler, the `AnteHandler`, is responsible for performing preliminary message -execution business logic such as fee payment, signature verification, etc. This is -particular to Cosmos SDK routed transactions. Ethereum routed transactions will -bypass this as the EVM handles the same business logic. - -Ethereum routed transactions coming from a web3 source are expected to be RLP -encoded, however all internal interaction between Ethermint and Tendermint will -utilize Amino encoding. - -__Note__: Our goal is to utilize Geth/Turbo-Geth as a library, at least as much -as possible, so it should be expected that these types and the operations you may -perform on them will keep in line with Ethereum (e.g. signature algorithms and -gas/fees). In addition, we aim to have existing tooling and frameworks in the -Ethereum ecosystem have 100% compatibility with creating transactions in Ethermint. - -## Signatures - -Ethermint supports [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) -signatures. A `Transaction` is expected to have a single signature for Ethereum -routed transactions. However, just as in Cosmos, Ethermint will support multiple -signers for non-Ethereum transactions. Signatures over the -`Transaction` type are identical to Ethereum and the signatures will not be duplicated -in the embedding [`auth.StdTx`](https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth#StdTx). - -## Gas & Fees - -TODO diff --git a/docs/yarn.lock b/docs/yarn.lock new file mode 100644 index 0000000000..1c9124b4b2 --- /dev/null +++ b/docs/yarn.lock @@ -0,0 +1,8506 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@algolia/cache-browser-local-storage@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.4.0.tgz#f58055bdf798d7b31b6d5f86e465cb0fc7dd6694" + integrity sha512-2AiKgN7DpFypkRCRkpqH7waXXyFdcnsPWzmN8sLHrB/FfXqgmsQb3pGft+9YHZIDQ0vAnfgMxSGgMhMGW+0Qnw== + dependencies: + "@algolia/cache-common" "4.4.0" + +"@algolia/cache-common@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.4.0.tgz#bfe84790230f5d2de495238b29e9397c5ed2b26e" + integrity sha512-PrIgoMnXaDWUfwOekahro543pgcJfgRu/nd/ZQS5ffem3+Ow725eZY6HDpPaQ1k3cvLii9JH6V2sNJConjqUKA== + +"@algolia/cache-in-memory@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.4.0.tgz#54a089094c2afa5b9cacab4b60a5f1ba29013a7c" + integrity sha512-9+XlUB0baDU/Dp9URRHPp6Q37YmTO0QmgPWt9+n+wqZrRL0jR3Jezr4jCT7RemqGMxBiR+YpnqaUv0orpb0ptw== + dependencies: + "@algolia/cache-common" "4.4.0" + +"@algolia/client-account@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.4.0.tgz#7dbeff83e1c85d853b3ad224674a924e02b94d1b" + integrity sha512-Kynu3cMEs0clTLf674rtrCF+FWR/JwlQxKlIWsPzvLBRmNXdvYej9YBcNaOr4OTQFCCZn9JVE8ib91Z7J4IL1Q== + dependencies: + "@algolia/client-common" "4.4.0" + "@algolia/client-search" "4.4.0" + "@algolia/transporter" "4.4.0" + +"@algolia/client-analytics@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.4.0.tgz#50dde68b067c615fc91434c98db9b5ca429be33d" + integrity sha512-GQyjQimKAc9sZbafxln9Wk7j4pEYiORv28MZkZ+0Bjt7WNXIeO7OgOOECVpQHm9buyV6hCKpNtJcbb5/syRzdQ== + dependencies: + "@algolia/client-common" "4.4.0" + "@algolia/client-search" "4.4.0" + "@algolia/requester-common" "4.4.0" + "@algolia/transporter" "4.4.0" + +"@algolia/client-common@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.4.0.tgz#b9fa987bc7a148f9756da59ada51fe2494a4aa9a" + integrity sha512-a3yr6UhzjWPHDG/8iGp9UvrDOm1aeHVWJIf0Nj/cIvqX5tNCEIo4IMe59ovApkDgLOIpt/cLsyhn9/FiPXRhJA== + dependencies: + "@algolia/requester-common" "4.4.0" + "@algolia/transporter" "4.4.0" + +"@algolia/client-recommendation@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@algolia/client-recommendation/-/client-recommendation-4.4.0.tgz#82410f7a346ed8518b8dcd28bc47571e850ab74f" + integrity sha512-sBszbQH46rko6w2fdEG77ma8+fAg0SDkLZGxWhv4trgcnYGUBFl2dcpEPt/6koto9b4XYlf+eh+qi6iGvYqRPg== + dependencies: + "@algolia/client-common" "4.4.0" + "@algolia/requester-common" "4.4.0" + "@algolia/transporter" "4.4.0" + +"@algolia/client-search@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.4.0.tgz#c1e107206f3ae719cd3a9877889eea5e5cbcdc62" + integrity sha512-jqWcxCUyPPHnHreoMb2PnN9iHTP+V/nL62R84XuTRDE3VgTnhm4ZnqyuRdzZQqaz+gNy5znav64TmQ9FN9WW5g== + dependencies: + "@algolia/client-common" "4.4.0" + "@algolia/requester-common" "4.4.0" + "@algolia/transporter" "4.4.0" + +"@algolia/logger-common@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.4.0.tgz#8115d95d5f6227f0127d33130a9c4622cde64f6f" + integrity sha512-2vjmSENLaKNuF+ytRDysfWxxgFG95WXCHwHbueThdPMCK3hskkwqJ0Y/pugKfzl+54mZxegb4BYfgcCeuaHVUw== + +"@algolia/logger-console@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.4.0.tgz#1e0eaaf0879f152f9a1fa333c4cd8cb55e071552" + integrity sha512-st/GUWyKvr6YM72OOfF+RmpdVGda3BPXbQ+chpntUq1WyVkyZXGjSmH1IcBVlua27GzxabwOUYON39cF3x10/g== + dependencies: + "@algolia/logger-common" "4.4.0" + +"@algolia/requester-browser-xhr@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.4.0.tgz#f5877397ed92d2d64d08846ea969aeb559a5efb6" + integrity sha512-V3a4hXlNch355GnWaT1f5QfXhROpsjT6sd0Znq29gAhwLqfBExhLW6Khdkv5pENC0Qy7ClVhdXFrBL9QCQer1g== + dependencies: + "@algolia/requester-common" "4.4.0" + +"@algolia/requester-common@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.4.0.tgz#0e977939aae32ff81a6d27480a71771a65db6051" + integrity sha512-jPinHlFJEFokxQ5b3JWyjQKKn+FMy0hH99PApzOgQAYOSiFRXiPEZp6LeIexDeLLu7Y3eRt/3nHvjPKa6PmRRw== + +"@algolia/requester-node-http@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.4.0.tgz#6ffba93d54eeadf64cb1be67fae5c4e3f7c8f390" + integrity sha512-b7HC9C/GHxiV4+0GpCRTtjscvwarPr3dGm4CAhb6AkNjgjRcFUNr1NfsF75w3WVmzmt79/7QZihddztDdVMGjw== + dependencies: + "@algolia/requester-common" "4.4.0" + +"@algolia/transporter@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.4.0.tgz#6ec79aac43bc515c8e4f6d6e27dc8d8cd7112f7e" + integrity sha512-Xxzq91DEEeKIzT3DU46n4LEyTGAKZNtSHc2H9wvIY5MYwhZwEribmXXZ6k8W1FvBvzggv3juu0SP+xwGoR7F0w== + dependencies: + "@algolia/cache-common" "4.4.0" + "@algolia/logger-common" "4.4.0" + "@algolia/requester-common" "4.4.0" + +"@ant-design-vue/babel-helper-vue-transform-on@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@ant-design-vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.0.1.tgz#d219d92f4e1fc5e7add211c347c7fa000518b623" + integrity sha512-dOAPf/tCM2lCG8FhvOMFBaOdMElMEGhOoocMXEWvHW2l1KIex+UibDcq4bdBEJpDMLrnbNOqci9E7P2dARP6lg== + +"@ant-design-vue/babel-plugin-jsx@^1.0.0-0": + version "1.0.0-rc.1" + resolved "https://registry.yarnpkg.com/@ant-design-vue/babel-plugin-jsx/-/babel-plugin-jsx-1.0.0-rc.1.tgz#ae56cecbda9f08691bcf92dfe98e2416e77d758b" + integrity sha512-x7PfAHSs5/emIuey1Df7Bh/vJU27S9KBdufzoAA7kgwTpEpY85R7CXD9gl6sJFB7aG2pZpl4Tmm+FsHlzgp7fA== + dependencies: + "@ant-design-vue/babel-helper-vue-transform-on" "^1.0.0" + "@babel/helper-module-imports" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.0.0" + "@babel/traverse" "^7.0.0" + "@babel/types" "^7.0.0" + camelcase "^6.0.0" + html-tags "^3.1.0" + svg-tags "^1.0.0" + +"@babel/code-frame@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/compat-data@^7.10.4", "@babel/compat-data@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.11.0.tgz#e9f73efe09af1355b723a7f39b11bad637d7c99c" + integrity sha512-TPSvJfv73ng0pfnEOh17bYMPQbI95+nGWc71Ss4vZdRBHTDqmM9Z8ZV4rYz8Ks7sfzc95n30k6ODIq5UGnXcYQ== + dependencies: + browserslist "^4.12.0" + invariant "^2.2.4" + semver "^5.5.0" + +"@babel/core@^7.11.0", "@babel/core@^7.8.4": + version "7.11.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651" + integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.6" + "@babel/helper-module-transforms" "^7.11.0" + "@babel/helpers" "^7.10.4" + "@babel/parser" "^7.11.5" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.11.5" + "@babel/types" "^7.11.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.11.5", "@babel/generator@^7.11.6": + version "7.11.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" + integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA== + dependencies: + "@babel/types" "^7.11.5" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-annotate-as-pure@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3" + integrity sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3" + integrity sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-compilation-targets@^7.10.4", "@babel/helper-compilation-targets@^7.9.6": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz#804ae8e3f04376607cc791b9d47d540276332bd2" + integrity sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ== + dependencies: + "@babel/compat-data" "^7.10.4" + browserslist "^4.12.0" + invariant "^2.2.4" + levenary "^1.1.1" + semver "^5.5.0" + +"@babel/helper-create-class-features-plugin@^7.10.4", "@babel/helper-create-class-features-plugin@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d" + integrity sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-member-expression-to-functions" "^7.10.5" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" + +"@babel/helper-create-regexp-features-plugin@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8" + integrity sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-regex" "^7.10.4" + regexpu-core "^4.7.0" + +"@babel/helper-define-map@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz#b53c10db78a640800152692b13393147acb9bb30" + integrity sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/types" "^7.10.5" + lodash "^4.17.19" + +"@babel/helper-explode-assignable-expression@^7.10.4": + version "7.11.4" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz#2d8e3470252cc17aba917ede7803d4a7a276a41b" + integrity sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" + integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== + dependencies: + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-get-function-arity@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" + integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-hoist-variables@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e" + integrity sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-member-expression-to-functions@^7.10.4", "@babel/helper-member-expression-to-functions@^7.10.5": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df" + integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" + integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" + integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/template" "^7.10.4" + "@babel/types" "^7.11.0" + lodash "^4.17.19" + +"@babel/helper-optimise-call-expression@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" + integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + +"@babel/helper-regex@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0" + integrity sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg== + dependencies: + lodash "^4.17.19" + +"@babel/helper-remap-async-to-generator@^7.10.4": + version "7.11.4" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz#4474ea9f7438f18575e30b0cac784045b402a12d" + integrity sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-wrap-function" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-replace-supers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" + integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-simple-access@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" + integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw== + dependencies: + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helper-skip-transparent-expression-wrappers@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz#eec162f112c2f58d3af0af125e3bb57665146729" + integrity sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" + integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== + dependencies: + "@babel/types" "^7.11.0" + +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + +"@babel/helper-wrap-function@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87" + integrity sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/helpers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044" + integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.10.4", "@babel/parser@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" + integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== + +"@babel/plugin-proposal-async-generator-functions@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558" + integrity sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.10.4" + "@babel/plugin-syntax-async-generators" "^7.8.0" + +"@babel/plugin-proposal-class-properties@^7.10.4", "@babel/plugin-proposal-class-properties@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz#a33bf632da390a59c7a8c570045d1115cd778807" + integrity sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-proposal-decorators@^7.8.3": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.10.5.tgz#42898bba478bc4b1ae242a703a953a7ad350ffb4" + integrity sha512-Sc5TAQSZuLzgY0664mMDn24Vw2P8g/VhyLyGPaWiHahhgLqeZvcGeyBZOrJW0oSKIK2mvQ22a1ENXBIQLhrEiQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-decorators" "^7.10.4" + +"@babel/plugin-proposal-dynamic-import@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz#ba57a26cb98b37741e9d5bca1b8b0ddf8291f17e" + integrity sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + +"@babel/plugin-proposal-export-namespace-from@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz#570d883b91031637b3e2958eea3c438e62c05f54" + integrity sha512-aNdf0LY6/3WXkhh0Fdb6Zk9j1NMD8ovj3F6r0+3j837Pn1S1PdNtcwJ5EG9WkVPNHPxyJDaxMaAOVq4eki0qbg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz#593e59c63528160233bd321b1aebe0820c2341db" + integrity sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.0" + +"@babel/plugin-proposal-logical-assignment-operators@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz#9f80e482c03083c87125dee10026b58527ea20c8" + integrity sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz#02a7e961fc32e6d5b2db0649e01bf80ddee7e04a" + integrity sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + +"@babel/plugin-proposal-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz#ce1590ff0a65ad12970a609d78855e9a4c1aef06" + integrity sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz#bd81f95a1f746760ea43b6c2d3d62b11790ad0af" + integrity sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.10.4" + +"@babel/plugin-proposal-optional-catch-binding@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz#31c938309d24a78a49d68fdabffaa863758554dd" + integrity sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + +"@babel/plugin-proposal-optional-chaining@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz#de5866d0646f6afdaab8a566382fe3a221755076" + integrity sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + +"@babel/plugin-proposal-private-methods@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz#b160d972b8fdba5c7d111a145fc8c421fc2a6909" + integrity sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-proposal-unicode-property-regex@^7.10.4", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz#4483cda53041ce3413b7fe2f00022665ddfaa75d" + integrity sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-async-generators@^7.8.0": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz#6644e6a0baa55a61f9e3231f6c9eeb6ee46c124c" + integrity sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-decorators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.10.4.tgz#6853085b2c429f9d322d02f5a635018cdeb2360c" + integrity sha512-2NaoC6fAk2VMdhY1eerkfHV+lVYC1u8b+jmRJISqANCJlTxYy19HGdIkkQtix2UtkcPuPu+IlDgrVseZnU03bw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-json-strings@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.2.0", "@babel/plugin-syntax-jsx@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.4.tgz#39abaae3cbf710c4373d8429484e6ba21340166c" + integrity sha512-KCg9mio9jwiARCB7WAcQ7Y1q+qicILjoK8LP/VkPkEKaf5dkaZZK1EcTe91a3JJlZ3qy6L5s9X52boEYi8DM9g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-top-level-await@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz#4bbeb8917b54fcf768364e0a81f560e33a3ef57d" + integrity sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-arrow-functions@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz#e22960d77e697c74f41c501d44d73dbf8a6a64cd" + integrity sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-async-to-generator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz#41a5017e49eb6f3cda9392a51eef29405b245a37" + integrity sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.10.4" + +"@babel/plugin-transform-block-scoped-functions@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz#1afa595744f75e43a91af73b0d998ecfe4ebc2e8" + integrity sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-block-scoping@^7.10.4": + version "7.11.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz#5b7efe98852bef8d652c0b28144cd93a9e4b5215" + integrity sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-classes@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz#405136af2b3e218bc4a1926228bc917ab1a0adc7" + integrity sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-define-map" "^7.10.4" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz#9ded83a816e82ded28d52d4b4ecbdd810cdfc0eb" + integrity sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-destructuring@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz#70ddd2b3d1bea83d01509e9bb25ddb3a74fc85e5" + integrity sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-dotall-regex@^7.10.4", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz#469c2062105c1eb6a040eaf4fac4b488078395ee" + integrity sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-duplicate-keys@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz#697e50c9fee14380fe843d1f306b295617431e47" + integrity sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-exponentiation-operator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz#5ae338c57f8cf4001bdb35607ae66b92d665af2e" + integrity sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-for-of@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz#c08892e8819d3a5db29031b115af511dbbfebae9" + integrity sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz#6a467880e0fc9638514ba369111811ddbe2644b7" + integrity sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz#9f42ba0841100a135f22712d0e391c462f571f3c" + integrity sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-member-expression-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz#b1ec44fcf195afcb8db2c62cd8e551c881baf8b7" + integrity sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-modules-amd@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz#1b9cddaf05d9e88b3aad339cb3e445c4f020a9b1" + integrity sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw== + dependencies: + "@babel/helper-module-transforms" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-commonjs@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz#66667c3eeda1ebf7896d41f1f16b17105a2fbca0" + integrity sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w== + dependencies: + "@babel/helper-module-transforms" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-systemjs@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz#6270099c854066681bae9e05f87e1b9cadbe8c85" + integrity sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw== + dependencies: + "@babel/helper-hoist-variables" "^7.10.4" + "@babel/helper-module-transforms" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-umd@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz#9a8481fe81b824654b3a0b65da3df89f3d21839e" + integrity sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA== + dependencies: + "@babel/helper-module-transforms" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz#78b4d978810b6f3bcf03f9e318f2fc0ed41aecb6" + integrity sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + +"@babel/plugin-transform-new-target@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz#9097d753cb7b024cb7381a3b2e52e9513a9c6888" + integrity sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-object-super@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz#d7146c4d139433e7a6526f888c667e314a093894" + integrity sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + +"@babel/plugin-transform-parameters@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz#59d339d58d0b1950435f4043e74e2510005e2c4a" + integrity sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw== + dependencies: + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-property-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz#f6fe54b6590352298785b83edd815d214c42e3c0" + integrity sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-regenerator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz#2015e59d839074e76838de2159db421966fd8b63" + integrity sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw== + dependencies: + regenerator-transform "^0.14.2" + +"@babel/plugin-transform-reserved-words@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz#8f2682bcdcef9ed327e1b0861585d7013f8a54dd" + integrity sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-runtime@^7.11.0": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.11.5.tgz#f108bc8e0cf33c37da031c097d1df470b3a293fc" + integrity sha512-9aIoee+EhjySZ6vY5hnLjigHzunBlscx9ANKutkeWTJTx6m5Rbq6Ic01tLvO54lSusR+BxV7u4UDdCmXv5aagg== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + resolve "^1.8.1" + semver "^5.5.1" + +"@babel/plugin-transform-shorthand-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz#9fd25ec5cdd555bb7f473e5e6ee1c971eede4dd6" + integrity sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-spread@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz#fa84d300f5e4f57752fe41a6d1b3c554f13f17cc" + integrity sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" + +"@babel/plugin-transform-sticky-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz#8f3889ee8657581130a29d9cc91d7c73b7c4a28d" + integrity sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-regex" "^7.10.4" + +"@babel/plugin-transform-template-literals@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz#78bc5d626a6642db3312d9d0f001f5e7639fde8c" + integrity sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-typeof-symbol@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz#9509f1a7eec31c4edbffe137c16cc33ff0bc5bfc" + integrity sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-unicode-escapes@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007" + integrity sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-unicode-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz#e56d71f9282fac6db09c82742055576d5e6d80a8" + integrity sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/preset-env@^7.11.0": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.11.5.tgz#18cb4b9379e3e92ffea92c07471a99a2914e4272" + integrity sha512-kXqmW1jVcnB2cdueV+fyBM8estd5mlNfaQi6lwLgRwCby4edpavgbFhiBNjmWA3JpB/yZGSISa7Srf+TwxDQoA== + dependencies: + "@babel/compat-data" "^7.11.0" + "@babel/helper-compilation-targets" "^7.10.4" + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-proposal-async-generator-functions" "^7.10.4" + "@babel/plugin-proposal-class-properties" "^7.10.4" + "@babel/plugin-proposal-dynamic-import" "^7.10.4" + "@babel/plugin-proposal-export-namespace-from" "^7.10.4" + "@babel/plugin-proposal-json-strings" "^7.10.4" + "@babel/plugin-proposal-logical-assignment-operators" "^7.11.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.4" + "@babel/plugin-proposal-numeric-separator" "^7.10.4" + "@babel/plugin-proposal-object-rest-spread" "^7.11.0" + "@babel/plugin-proposal-optional-catch-binding" "^7.10.4" + "@babel/plugin-proposal-optional-chaining" "^7.11.0" + "@babel/plugin-proposal-private-methods" "^7.10.4" + "@babel/plugin-proposal-unicode-property-regex" "^7.10.4" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-class-properties" "^7.10.4" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-syntax-top-level-await" "^7.10.4" + "@babel/plugin-transform-arrow-functions" "^7.10.4" + "@babel/plugin-transform-async-to-generator" "^7.10.4" + "@babel/plugin-transform-block-scoped-functions" "^7.10.4" + "@babel/plugin-transform-block-scoping" "^7.10.4" + "@babel/plugin-transform-classes" "^7.10.4" + "@babel/plugin-transform-computed-properties" "^7.10.4" + "@babel/plugin-transform-destructuring" "^7.10.4" + "@babel/plugin-transform-dotall-regex" "^7.10.4" + "@babel/plugin-transform-duplicate-keys" "^7.10.4" + "@babel/plugin-transform-exponentiation-operator" "^7.10.4" + "@babel/plugin-transform-for-of" "^7.10.4" + "@babel/plugin-transform-function-name" "^7.10.4" + "@babel/plugin-transform-literals" "^7.10.4" + "@babel/plugin-transform-member-expression-literals" "^7.10.4" + "@babel/plugin-transform-modules-amd" "^7.10.4" + "@babel/plugin-transform-modules-commonjs" "^7.10.4" + "@babel/plugin-transform-modules-systemjs" "^7.10.4" + "@babel/plugin-transform-modules-umd" "^7.10.4" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.10.4" + "@babel/plugin-transform-new-target" "^7.10.4" + "@babel/plugin-transform-object-super" "^7.10.4" + "@babel/plugin-transform-parameters" "^7.10.4" + "@babel/plugin-transform-property-literals" "^7.10.4" + "@babel/plugin-transform-regenerator" "^7.10.4" + "@babel/plugin-transform-reserved-words" "^7.10.4" + "@babel/plugin-transform-shorthand-properties" "^7.10.4" + "@babel/plugin-transform-spread" "^7.11.0" + "@babel/plugin-transform-sticky-regex" "^7.10.4" + "@babel/plugin-transform-template-literals" "^7.10.4" + "@babel/plugin-transform-typeof-symbol" "^7.10.4" + "@babel/plugin-transform-unicode-escapes" "^7.10.4" + "@babel/plugin-transform-unicode-regex" "^7.10.4" + "@babel/preset-modules" "^0.1.3" + "@babel/types" "^7.11.5" + browserslist "^4.12.0" + core-js-compat "^3.6.2" + invariant "^2.2.2" + levenary "^1.1.1" + semver "^5.5.0" + +"@babel/preset-modules@^0.1.3": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e" + integrity sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/runtime@^7.11.0", "@babel/runtime@^7.8.4": + version "7.11.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" + integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" + integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.10.4" + "@babel/types" "^7.10.4" + +"@babel/traverse@^7.0.0", "@babel/traverse@^7.10.4", "@babel/traverse@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3" + integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.11.5" + "@babel/types" "^7.11.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + +"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.4.4": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" + integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + +"@cosmos-ui/vue@^0.33.0": + version "0.33.0" + resolved "https://registry.yarnpkg.com/@cosmos-ui/vue/-/vue-0.33.0.tgz#ddc7bdbafafb3a7b009a7cbc583850811b6fc8ba" + integrity sha512-fHRQcxd66nohpeUMiNm6A8XHhFtanrle/7RPv+XQH0ztbUzO4vyCjCI0FQd7QJsmlGWF/il7b9esunM0Y4Lx+A== + dependencies: + algoliasearch "^4.1.0" + axios "^0.19.2" + clipboard-copy "^3.1.0" + fuse.js "^3.4.6" + hotkeys-js "^3.7.3" + js-base64 "^2.5.2" + lodash "^4.17.15" + markdown-it "^10.0.0" + prismjs "^1.19.0" + querystring "^0.2.0" + tiny-cookie "^2.3.1" + vue "^2.6.10" + +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@types/babel-types@*", "@types/babel-types@^7.0.0": + version "7.0.9" + resolved "https://registry.yarnpkg.com/@types/babel-types/-/babel-types-7.0.9.tgz#01d7b86949f455402a94c788883fe4ba574cad41" + integrity sha512-qZLoYeXSTgQuK1h7QQS16hqLGdmqtRmN8w/rl3Au/l5x/zkHx+a4VHrHyBsi1I1vtK2oBHxSzKIu0R5p6spdOA== + +"@types/babylon@^6.16.2": + version "6.16.5" + resolved "https://registry.yarnpkg.com/@types/babylon/-/babylon-6.16.5.tgz#1c5641db69eb8cdf378edd25b4be7754beeb48b4" + integrity sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w== + dependencies: + "@types/babel-types" "*" + +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + +"@types/glob@^7.1.1": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" + integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + +"@types/json-schema@^7.0.5": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" + integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== + +"@types/minimatch@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== + +"@types/node@*": + version "14.6.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.4.tgz#a145cc0bb14ef9c4777361b7bbafa5cf8e3acb5a" + integrity sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ== + +"@types/q@^1.5.1": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24" + integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== + +"@vue/babel-helper-vue-jsx-merge-props@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.0.0.tgz#048fe579958da408fb7a8b2a3ec050b50a661040" + integrity sha512-6tyf5Cqm4m6v7buITuwS+jHzPlIPxbFzEhXR5JGZpbrvOcp1hiQKckd305/3C7C36wFekNTQSxAtgeM0j0yoUw== + +"@vue/babel-plugin-transform-vue-jsx@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.1.2.tgz#c0a3e6efc022e75e4247b448a8fc6b86f03e91c0" + integrity sha512-YfdaoSMvD1nj7+DsrwfTvTnhDXI7bsuh+Y5qWwvQXlD24uLgnsoww3qbiZvWf/EoviZMrvqkqN4CBw0W3BWUTQ== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.2.0" + "@vue/babel-helper-vue-jsx-merge-props" "^1.0.0" + html-tags "^2.0.0" + lodash.kebabcase "^4.1.1" + svg-tags "^1.0.0" + +"@vue/babel-preset-app@^4.1.2": + version "4.5.4" + resolved "https://registry.yarnpkg.com/@vue/babel-preset-app/-/babel-preset-app-4.5.4.tgz#bb164e8ab55673c561e6e83511631eda19efd7e4" + integrity sha512-a+2s/lL3fE3h9/ekvpMVLhZTDjR3xt+jnpTwuQtEZ3KIuzFHxbmwAjueRZh6BKEGfB6kgZ3KqZHFX3vx/DRJ4w== + dependencies: + "@ant-design-vue/babel-plugin-jsx" "^1.0.0-0" + "@babel/core" "^7.11.0" + "@babel/helper-compilation-targets" "^7.9.6" + "@babel/helper-module-imports" "^7.8.3" + "@babel/plugin-proposal-class-properties" "^7.8.3" + "@babel/plugin-proposal-decorators" "^7.8.3" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-jsx" "^7.8.3" + "@babel/plugin-transform-runtime" "^7.11.0" + "@babel/preset-env" "^7.11.0" + "@babel/runtime" "^7.11.0" + "@vue/babel-preset-jsx" "^1.1.2" + babel-plugin-dynamic-import-node "^2.3.3" + core-js "^3.6.5" + core-js-compat "^3.6.5" + semver "^6.1.0" + +"@vue/babel-preset-jsx@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@vue/babel-preset-jsx/-/babel-preset-jsx-1.1.2.tgz#2e169eb4c204ea37ca66c2ea85a880bfc99d4f20" + integrity sha512-zDpVnFpeC9YXmvGIDSsKNdL7qCG2rA3gjywLYHPCKDT10erjxF4U+6ay9X6TW5fl4GsDlJp9bVfAVQAAVzxxvQ== + dependencies: + "@vue/babel-helper-vue-jsx-merge-props" "^1.0.0" + "@vue/babel-plugin-transform-vue-jsx" "^1.1.2" + "@vue/babel-sugar-functional-vue" "^1.1.2" + "@vue/babel-sugar-inject-h" "^1.1.2" + "@vue/babel-sugar-v-model" "^1.1.2" + "@vue/babel-sugar-v-on" "^1.1.2" + +"@vue/babel-sugar-functional-vue@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.1.2.tgz#f7e24fba09e6f1ee70104560a8808057555f1a9a" + integrity sha512-YhmdJQSVEFF5ETJXzrMpj0nkCXEa39TvVxJTuVjzvP2rgKhdMmQzlJuMv/HpadhZaRVMCCF3AEjjJcK5q/cYzQ== + dependencies: + "@babel/plugin-syntax-jsx" "^7.2.0" + +"@vue/babel-sugar-inject-h@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.1.2.tgz#8a5276b6d8e2ed16ffc8078aad94236274e6edf0" + integrity sha512-VRSENdTvD5htpnVp7i7DNuChR5rVMcORdXjvv5HVvpdKHzDZAYiLSD+GhnhxLm3/dMuk8pSzV+k28ECkiN5m8w== + dependencies: + "@babel/plugin-syntax-jsx" "^7.2.0" + +"@vue/babel-sugar-v-model@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.1.2.tgz#1ff6fd1b800223fc9cb1e84dceb5e52d737a8192" + integrity sha512-vLXPvNq8vDtt0u9LqFdpGM9W9IWDmCmCyJXuozlq4F4UYVleXJ2Fa+3JsnTZNJcG+pLjjfnEGHci2339Kj5sGg== + dependencies: + "@babel/plugin-syntax-jsx" "^7.2.0" + "@vue/babel-helper-vue-jsx-merge-props" "^1.0.0" + "@vue/babel-plugin-transform-vue-jsx" "^1.1.2" + camelcase "^5.0.0" + html-tags "^2.0.0" + svg-tags "^1.0.0" + +"@vue/babel-sugar-v-on@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.1.2.tgz#b2ef99b8f2fab09fbead25aad70ef42e1cf5b13b" + integrity sha512-T8ZCwC8Jp2uRtcZ88YwZtZXe7eQrJcfRq0uTFy6ShbwYJyz5qWskRFoVsdTi9o0WEhmQXxhQUewodOSCUPVmsQ== + dependencies: + "@babel/plugin-syntax-jsx" "^7.2.0" + "@vue/babel-plugin-transform-vue-jsx" "^1.1.2" + camelcase "^5.0.0" + +"@vue/component-compiler-utils@^3.1.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-3.2.0.tgz#8f85182ceed28e9b3c75313de669f83166d11e5d" + integrity sha512-lejBLa7xAMsfiZfNp7Kv51zOzifnb29FwdnMLa96z26kXErPFioSf9BMcePVIQ6/Gc6/mC0UrPpxAWIHyae0vw== + dependencies: + consolidate "^0.15.1" + hash-sum "^1.0.2" + lru-cache "^4.1.2" + merge-source-map "^1.1.0" + postcss "^7.0.14" + postcss-selector-parser "^6.0.2" + source-map "~0.6.1" + vue-template-es2015-compiler "^1.9.0" + optionalDependencies: + prettier "^1.18.2" + +"@vuepress/core@1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@vuepress/core/-/core-1.5.4.tgz#036d28d6cc8a0928913116de5ebe80b0b4a9ac1b" + integrity sha512-RaHJiX0Yno4S3zoV64JNd3xE55sza8rayyWvXAJY381XVMxKrsLBrgW6ntNYSkzGnZcxi6fwMV/CVOUhEtkEkA== + dependencies: + "@babel/core" "^7.8.4" + "@vue/babel-preset-app" "^4.1.2" + "@vuepress/markdown" "1.5.4" + "@vuepress/markdown-loader" "1.5.4" + "@vuepress/plugin-last-updated" "1.5.4" + "@vuepress/plugin-register-components" "1.5.4" + "@vuepress/shared-utils" "1.5.4" + autoprefixer "^9.5.1" + babel-loader "^8.0.4" + cache-loader "^3.0.0" + chokidar "^2.0.3" + connect-history-api-fallback "^1.5.0" + copy-webpack-plugin "^5.0.2" + core-js "^3.6.4" + cross-spawn "^6.0.5" + css-loader "^2.1.1" + file-loader "^3.0.1" + js-yaml "^3.13.1" + lru-cache "^5.1.1" + mini-css-extract-plugin "0.6.0" + optimize-css-assets-webpack-plugin "^5.0.1" + portfinder "^1.0.13" + postcss-loader "^3.0.0" + postcss-safe-parser "^4.0.1" + toml "^3.0.0" + url-loader "^1.0.1" + vue "^2.6.10" + vue-loader "^15.7.1" + vue-router "^3.1.3" + vue-server-renderer "^2.6.10" + vue-template-compiler "^2.6.10" + vuepress-html-webpack-plugin "^3.2.0" + vuepress-plugin-container "^2.0.2" + webpack "^4.8.1" + webpack-chain "^6.0.0" + webpack-dev-server "^3.5.1" + webpack-merge "^4.1.2" + webpackbar "3.2.0" + +"@vuepress/markdown-loader@1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@vuepress/markdown-loader/-/markdown-loader-1.5.4.tgz#9ba49bbe9c94ed792714589aef6a20c7ed0ac822" + integrity sha512-3R5quGIXQm7gfPWN67SVZ9OBA7VrGEEXJjjV01MYkbfhqVGgO6lBRq73Og0XdKs4RPx4nqJUPthhL8FJVNRTIg== + dependencies: + "@vuepress/markdown" "1.5.4" + loader-utils "^1.1.0" + lru-cache "^5.1.1" + +"@vuepress/markdown@1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@vuepress/markdown/-/markdown-1.5.4.tgz#d9736db430034b7b6058696c4da1cc211032bbea" + integrity sha512-bgrR9LTcAa2O0WipTbH3OFKeAfXc/2oU6cUIoMkyihSKUo1Mr5yt1XKM7vHe1uFEZygNr8EAemep8chsuVuISA== + dependencies: + "@vuepress/shared-utils" "1.5.4" + markdown-it "^8.4.1" + markdown-it-anchor "^5.0.2" + markdown-it-chain "^1.3.0" + markdown-it-emoji "^1.4.0" + markdown-it-table-of-contents "^0.4.0" + prismjs "^1.13.0" + +"@vuepress/plugin-active-header-links@1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-active-header-links/-/plugin-active-header-links-1.5.4.tgz#ffbfbce0d5932091043b766757683ca3b5420aef" + integrity sha512-FI1Dr/44HVqxLMRSuaVEEwegGVEGFlaWYE3nsXwL7klKr6c+2kXHEw9rSQlAxzJyzVfovTk4dd+s/AMOKuLGZQ== + dependencies: + lodash.debounce "^4.0.8" + +"@vuepress/plugin-google-analytics@1.5.3": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-google-analytics/-/plugin-google-analytics-1.5.3.tgz#61510b1619cfffdb173c95a8e6e411817829591c" + integrity sha512-wVcQb4luvK9C/apvGJZG+fvoGQRJQ4rc2fWbn6MxlTN8xTFH5RkQHXzHWVqvUYLBc2gMP67lRdgZephdYpoYNA== + +"@vuepress/plugin-last-updated@1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-last-updated/-/plugin-last-updated-1.5.4.tgz#6f3f9fe720ce7f883c37ddc71ac02fe8f36bbfe4" + integrity sha512-9kezBCxPM+cevKRNML6Q7v6qkI8NQvKbVkwohlzsElM8FBmjlZmgFyZje66ksTnb/U6ogazCCq9jdOyipNcQ2A== + dependencies: + cross-spawn "^6.0.5" + +"@vuepress/plugin-nprogress@1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-nprogress/-/plugin-nprogress-1.5.4.tgz#b818ebcac5addb6488bf50eb21585450f52ae40c" + integrity sha512-2bGKoO/o2e5mIfOU80q+AkxOK5wVijA/+8jGjSQVf2ccMpJw+Ly1mMi69r81Q0QkEihgfI9VN42a5+a6LUgPBw== + dependencies: + nprogress "^0.2.0" + +"@vuepress/plugin-register-components@1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-register-components/-/plugin-register-components-1.5.4.tgz#2f62d0790471ef53935ff2c808d8045c0473067f" + integrity sha512-Y1U9j6unZp1ZhnHjQ9yOPY+vxldUA3C1EwT6UgI75j5gxa5Hz6NakoIo6mbhaYHlGmx33o/MXrxufLPapo/YlQ== + dependencies: + "@vuepress/shared-utils" "1.5.4" + +"@vuepress/plugin-search@1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@vuepress/plugin-search/-/plugin-search-1.5.4.tgz#3360445e9ecf8bdcb5497ab1c0f46d8aecc9ab6c" + integrity sha512-wikU9XYiZ3Olbii0lI+56mcSdpzHHkduVBMB4MNEV5iob23qDxGPmvfZirjsZV20w1UnLRptERyHtZkTLW9Mbg== + +"@vuepress/shared-utils@1.5.4", "@vuepress/shared-utils@^1.2.0": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@vuepress/shared-utils/-/shared-utils-1.5.4.tgz#d2c8693b8cd354d3a13a76f8f4259335e5540099" + integrity sha512-HCeMPEAPjFN1Ongii0BUCI1iB4gBBiQ4PUgh7F4IGG8yBg4tMqWO4NHqCuDCuGEvK7lgHy8veto0SsSvdSKp3g== + dependencies: + chalk "^2.3.2" + escape-html "^1.0.3" + fs-extra "^7.0.1" + globby "^9.2.0" + gray-matter "^4.0.1" + hash-sum "^1.0.2" + semver "^6.0.0" + toml "^3.0.0" + upath "^1.1.0" + +"@vuepress/theme-default@1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@vuepress/theme-default/-/theme-default-1.5.4.tgz#77db27fe7c3ced15a970644df0202b0effbe865f" + integrity sha512-kHst1yXzqTiocVU7w9x4cfJ08vR9ZbREC6kTRtH1ytQSEUL5tM0b9HFicfg1kDp7YNq2qntRro+WmfjU9Ps/eg== + dependencies: + "@vuepress/plugin-active-header-links" "1.5.4" + "@vuepress/plugin-nprogress" "1.5.4" + "@vuepress/plugin-search" "1.5.4" + docsearch.js "^2.5.2" + lodash "^4.17.15" + stylus "^0.54.5" + stylus-loader "^3.0.2" + vuepress-plugin-container "^2.0.2" + vuepress-plugin-smooth-scroll "^0.0.3" + +"@webassemblyjs/ast@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" + integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== + dependencies: + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" + +"@webassemblyjs/floating-point-hex-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" + integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== + +"@webassemblyjs/helper-api-error@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" + integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== + +"@webassemblyjs/helper-buffer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" + integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== + +"@webassemblyjs/helper-code-frame@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" + integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== + dependencies: + "@webassemblyjs/wast-printer" "1.9.0" + +"@webassemblyjs/helper-fsm@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" + integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== + +"@webassemblyjs/helper-module-context@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" + integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== + dependencies: + "@webassemblyjs/ast" "1.9.0" + +"@webassemblyjs/helper-wasm-bytecode@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" + integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== + +"@webassemblyjs/helper-wasm-section@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" + integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + +"@webassemblyjs/ieee754@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" + integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" + integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" + integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== + +"@webassemblyjs/wasm-edit@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" + integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/helper-wasm-section" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-opt" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + "@webassemblyjs/wast-printer" "1.9.0" + +"@webassemblyjs/wasm-gen@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" + integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" + +"@webassemblyjs/wasm-opt@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" + integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + +"@webassemblyjs/wasm-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" + integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" + +"@webassemblyjs/wast-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" + integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/floating-point-hex-parser" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-code-frame" "1.9.0" + "@webassemblyjs/helper-fsm" "1.9.0" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/wast-printer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" + integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" + "@xtuc/long" "4.2.2" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-globals@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf" + integrity sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8= + dependencies: + acorn "^4.0.4" + +acorn@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= + +acorn@^4.0.4, acorn@~4.0.2: + version "4.0.13" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" + integrity sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c= + +acorn@^6.4.1: + version "6.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" + integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== + +agentkeepalive@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef" + integrity sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8= + +ajv-errors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" + integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== + +ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: + version "6.12.4" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" + integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +algoliasearch@^3.24.5: + version "3.35.1" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-3.35.1.tgz#297d15f534a3507cab2f5dfb996019cac7568f0c" + integrity sha512-K4yKVhaHkXfJ/xcUnil04xiSrB8B8yHZoFEhWNpXg23eiCnqvTZw1tn/SqvdsANlYHLJlKl0qi3I/Q2Sqo7LwQ== + dependencies: + agentkeepalive "^2.2.0" + debug "^2.6.9" + envify "^4.0.0" + es6-promise "^4.1.0" + events "^1.1.0" + foreach "^2.0.5" + global "^4.3.2" + inherits "^2.0.1" + isarray "^2.0.1" + load-script "^1.0.0" + object-keys "^1.0.11" + querystring-es3 "^0.2.1" + reduce "^1.0.1" + semver "^5.1.0" + tunnel-agent "^0.6.0" + +algoliasearch@^4.1.0, algoliasearch@^4.2.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.4.0.tgz#25c356d8bdcf7e3f941633f61e1ac111ddcba404" + integrity sha512-Ag3wxe/nSodNl/1KbHibtkh7TNLptKE300/wnGVtszRjXivaWD6333nUpCumrYObHym/fHMHyLcmQYezXbAIWQ== + dependencies: + "@algolia/cache-browser-local-storage" "4.4.0" + "@algolia/cache-common" "4.4.0" + "@algolia/cache-in-memory" "4.4.0" + "@algolia/client-account" "4.4.0" + "@algolia/client-analytics" "4.4.0" + "@algolia/client-common" "4.4.0" + "@algolia/client-recommendation" "4.4.0" + "@algolia/client-search" "4.4.0" + "@algolia/logger-common" "4.4.0" + "@algolia/logger-console" "4.4.0" + "@algolia/requester-browser-xhr" "4.4.0" + "@algolia/requester-common" "4.4.0" + "@algolia/requester-node-http" "4.4.0" + "@algolia/transporter" "4.4.0" + +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + integrity sha1-DNkKVhCT810KmSVsIrcGlDP60Rc= + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + +alphanum-sort@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= + +ansi-align@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" + integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw== + dependencies: + string-width "^3.0.0" + +ansi-colors@^3.0.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== + +ansi-escapes@^4.1.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + +ansi-html@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" + integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +aproba@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-flatten@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + +array-union@^1.0.1, array-union@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= + +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assert@^1.1.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== + dependencies: + object-assign "^4.1.1" + util "0.10.3" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@^2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +autocomplete.js@0.36.0: + version "0.36.0" + resolved "https://registry.yarnpkg.com/autocomplete.js/-/autocomplete.js-0.36.0.tgz#94fe775fe64b6cd42e622d076dc7fd26bedd837b" + integrity sha512-jEwUXnVMeCHHutUt10i/8ZiRaCb0Wo+ZyKxeGsYwBDtw6EJHqEeDrq4UwZRD8YBSvp3g6klP678il2eeiVXN2Q== + dependencies: + immediate "^3.2.3" + +autoprefixer@^9.5.1: + version "9.8.6" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" + integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== + dependencies: + browserslist "^4.12.0" + caniuse-lite "^1.0.30001109" + colorette "^1.2.1" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^7.0.32" + postcss-value-parser "^4.1.0" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.10.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428" + integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA== + +axios@^0.19.2: + version "0.19.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" + integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== + dependencies: + follow-redirects "1.5.10" + +babel-loader@^8.0.4: + version "8.1.0" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" + integrity sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw== + dependencies: + find-cache-dir "^2.1.0" + loader-utils "^1.4.0" + mkdirp "^0.5.3" + pify "^4.0.1" + schema-utils "^2.6.5" + +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== + dependencies: + object.assign "^4.1.0" + +babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base64-js@^1.0.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +big.js@^3.1.3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" + integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q== + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +binary-extensions@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" + integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bluebird@^3.1.1, bluebird@^3.5.5: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.4.0: + version "4.11.9" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" + integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== + +bn.js@^5.1.1: + version "5.1.3" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b" + integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ== + +body-parser@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +bonjour@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" + integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= + dependencies: + array-flatten "^2.1.0" + deep-equal "^1.0.1" + dns-equal "^1.0.0" + dns-txt "^2.0.2" + multicast-dns "^6.0.1" + multicast-dns-service-types "^1.1.0" + +boolbase@^1.0.0, boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + +boxen@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" + integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^5.3.1" + chalk "^3.0.0" + cli-boxes "^2.2.0" + string-width "^4.1.0" + term-size "^2.1.0" + type-fest "^0.8.1" + widest-line "^3.1.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + +browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.8.5: + version "4.14.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.1.tgz#cb2b490ba881d45dc3039078c7ed04411eaf3fa3" + integrity sha512-zyBTIHydW37pnb63c7fHFXUG6EcqWOqoMdDx6cdyaDFriZ20EoVxcE95S54N+heRqY8m8IUgB5zYta/gCwSaaA== + dependencies: + caniuse-lite "^1.0.30001124" + electron-to-chromium "^1.3.562" + escalade "^3.0.2" + node-releases "^1.1.60" + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-indexof@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" + integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== + +buffer-json@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/buffer-json/-/buffer-json-2.0.0.tgz#f73e13b1e42f196fe2fd67d001c7d7107edd7c23" + integrity sha512-+jjPFVqyfF1esi9fvfUs3NqM0pH1ziZ36VP4hmA/y/Ssfo/5w5xHKfTw9BwQjoJ1w/oVtpLomqwUHKdefGyuHw== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@^4.3.0: + version "4.9.2" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" + integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cac@^6.5.6: + version "6.6.1" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.6.1.tgz#3dde3f6943f45d42a56729ea3573c08b3e7b6a6d" + integrity sha512-uhki4T3Ax68hw7Dufi0bATVAF8ayBSwOKUEJHjObPrUN4tlQ8Lf7oljpTje/mArLxYN0D743c2zJt4C1bVTCqg== + +cacache@^12.0.2, cacache@^12.0.3: + version "12.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" + integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== + dependencies: + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + infer-owner "^1.0.3" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cache-loader@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/cache-loader/-/cache-loader-3.0.1.tgz#cee6cf4b3cdc7c610905b26bad6c2fc439c821af" + integrity sha512-HzJIvGiGqYsFUrMjAJNDbVZoG7qQA+vy9AIoKs7s9DscNfki0I589mf2w6/tW+kkFH3zyiknoWV5Jdynu6b/zw== + dependencies: + buffer-json "^2.0.0" + find-cache-dir "^2.1.0" + loader-utils "^1.2.3" + mkdirp "^0.5.1" + neo-async "^2.6.1" + schema-utils "^1.0.0" + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= + +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + +camel-case@3.0.x: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk= + +camelcase@^5.0.0, camelcase@^5.2.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e" + integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001124: + version "1.0.30001124" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001124.tgz#5d9998190258e11630d674fc50ea8e579ae0ced2" + integrity sha512-zQW8V3CdND7GHRH6rxm6s59Ww4g/qGWTheoboW9nfeMg7sUoopIfKCcNZUjwYRCOrvereh3kwDpZj4VLQ7zGtA== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + integrity sha1-qg0yYptu6XIgBBHL1EYckHvCt60= + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +character-parser@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/character-parser/-/character-parser-2.2.0.tgz#c7ce28f36d4bcd9744e5ffc2c5fcde1c73261fc0" + integrity sha1-x84o821LzZdE5f/CxfzeHHMmH8A= + dependencies: + is-regex "^1.0.3" + +cheerio@^1.0.0-rc.3: + version "1.0.0-rc.3" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6" + integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA== + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.1" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash "^4.15.0" + parse5 "^3.0.1" + +chokidar@^2.0.3, chokidar@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chokidar@^3.4.1: + version "3.4.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" + integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +chrome-trace-event@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" + integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== + dependencies: + tslib "^1.9.0" + +ci-info@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" + integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +clean-css@4.2.x, clean-css@^4.1.11: + version "4.2.3" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78" + integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA== + dependencies: + source-map "~0.6.0" + +cli-boxes@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + +clipboard-copy@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/clipboard-copy/-/clipboard-copy-3.1.0.tgz#4c59030a43d4988990564a664baeafba99f78ca4" + integrity sha512-Xsu1NddBXB89IUauda5BIq3Zq73UWkjkaQlPQbLNvNsd5WBMnTWPNKYR6HGaySOxGYZ+BKxP2E9X4ElnI3yiPA== + +clipboard@^2.0.0: + version "2.0.6" + resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.6.tgz#52921296eec0fdf77ead1749421b21c968647376" + integrity sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg== + dependencies: + good-listener "^1.2.2" + select "^1.1.2" + tiny-emitter "^2.0.0" + +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + integrity sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE= + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +coa@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" + integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== + dependencies: + "@types/q" "^1.5.1" + chalk "^2.4.1" + q "^1.1.2" + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0, color-convert@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" + integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10" + integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== + dependencies: + color-convert "^1.9.1" + color-string "^1.5.2" + +colorette@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" + integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@2.17.x: + version "2.17.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" + integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@~2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" + integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.5.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +configstore@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" + integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== + dependencies: + dot-prop "^5.2.0" + graceful-fs "^4.1.2" + make-dir "^3.0.0" + unique-string "^2.0.0" + write-file-atomic "^3.0.0" + xdg-basedir "^4.0.0" + +connect-history-api-fallback@^1.5.0, connect-history-api-fallback@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" + integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== + +consola@^2.6.0: + version "2.15.0" + resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.0.tgz#40fc4eefa4d2f8ef2e2806147f056ea207fcc0e9" + integrity sha512-vlcSGgdYS26mPf7qNi+dCisbhiyDnrN1zaRbw3CSuc2wGOMEGGPsp46PdRG5gqXwgtJfjxDkxRNAgRPr1B77vQ== + +console-browserify@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== + +consolidate@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7" + integrity sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw== + dependencies: + bluebird "^3.1.1" + +constantinople@^3.0.1, constantinople@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/constantinople/-/constantinople-3.1.2.tgz#d45ed724f57d3d10500017a7d3a889c1381ae647" + integrity sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw== + dependencies: + "@types/babel-types" "^7.0.0" + "@types/babylon" "^6.16.2" + babel-types "^6.26.0" + babylon "^6.18.0" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== + dependencies: + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +copy-webpack-plugin@^5.0.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz#8a889e1dcafa6c91c6cd4be1ad158f1d3823bae2" + integrity sha512-Uh7crJAco3AjBvgAy9Z75CjK8IG+gxaErro71THQ+vv/bl4HaQcpkexAY8KVW/T6D2W2IRr+couF/knIRkZMIQ== + dependencies: + cacache "^12.0.3" + find-cache-dir "^2.1.0" + glob-parent "^3.1.0" + globby "^7.1.1" + is-glob "^4.0.1" + loader-utils "^1.2.3" + minimatch "^3.0.4" + normalize-path "^3.0.0" + p-limit "^2.2.1" + schema-utils "^1.0.0" + serialize-javascript "^4.0.0" + webpack-log "^2.0.0" + +core-js-compat@^3.6.2, core-js-compat@^3.6.5: + version "3.6.5" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.5.tgz#2a51d9a4e25dfd6e690251aa81f99e3c05481f1c" + integrity sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng== + dependencies: + browserslist "^4.8.5" + semver "7.0.0" + +core-js@^2.4.0: + version "2.6.11" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" + integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== + +core-js@^3.6.4, core-js@^3.6.5: + version "3.6.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" + integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cosmiconfig@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-spawn@^6.0.0, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + +css-color-names@0.0.4, css-color-names@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= + +css-declaration-sorter@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" + integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== + dependencies: + postcss "^7.0.1" + timsort "^0.3.0" + +css-loader@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.1.tgz#d8254f72e412bb2238bb44dd674ffbef497333ea" + integrity sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w== + dependencies: + camelcase "^5.2.0" + icss-utils "^4.1.0" + loader-utils "^1.2.3" + normalize-path "^3.0.0" + postcss "^7.0.14" + postcss-modules-extract-imports "^2.0.0" + postcss-modules-local-by-default "^2.0.6" + postcss-modules-scope "^2.1.0" + postcss-modules-values "^2.0.0" + postcss-value-parser "^3.3.0" + schema-utils "^1.0.0" + +css-parse@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-2.0.0.tgz#a468ee667c16d81ccf05c58c38d2a97c780dbfd4" + integrity sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q= + dependencies: + css "^2.0.0" + +css-select-base-adapter@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" + integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== + +css-select@^1.1.0, css-select@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-select@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" + integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== + dependencies: + boolbase "^1.0.0" + css-what "^3.2.1" + domutils "^1.7.0" + nth-check "^1.0.2" + +css-tree@1.0.0-alpha.37: + version "1.0.0-alpha.37" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" + integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== + dependencies: + mdn-data "2.0.4" + source-map "^0.6.1" + +css-tree@1.0.0-alpha.39: + version "1.0.0-alpha.39" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb" + integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA== + dependencies: + mdn-data "2.0.6" + source-map "^0.6.1" + +css-what@2.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" + integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== + +css-what@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.3.0.tgz#10fec696a9ece2e591ac772d759aacabac38cd39" + integrity sha512-pv9JPyatiPaQ6pf4OvD/dbfm0o5LviWmwxNWzblYf/1u9QZd0ihV+PMwy5jdQWQ3349kZmKEx9WXuSka2dM4cg== + +css@^2.0.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" + integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== + dependencies: + inherits "^2.0.3" + source-map "^0.6.1" + source-map-resolve "^0.5.2" + urix "^0.1.0" + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssnano-preset-default@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76" + integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA== + dependencies: + css-declaration-sorter "^4.0.1" + cssnano-util-raw-cache "^4.0.1" + postcss "^7.0.0" + postcss-calc "^7.0.1" + postcss-colormin "^4.0.3" + postcss-convert-values "^4.0.1" + postcss-discard-comments "^4.0.2" + postcss-discard-duplicates "^4.0.2" + postcss-discard-empty "^4.0.1" + postcss-discard-overridden "^4.0.1" + postcss-merge-longhand "^4.0.11" + postcss-merge-rules "^4.0.3" + postcss-minify-font-values "^4.0.2" + postcss-minify-gradients "^4.0.2" + postcss-minify-params "^4.0.2" + postcss-minify-selectors "^4.0.2" + postcss-normalize-charset "^4.0.1" + postcss-normalize-display-values "^4.0.2" + postcss-normalize-positions "^4.0.2" + postcss-normalize-repeat-style "^4.0.2" + postcss-normalize-string "^4.0.2" + postcss-normalize-timing-functions "^4.0.2" + postcss-normalize-unicode "^4.0.1" + postcss-normalize-url "^4.0.1" + postcss-normalize-whitespace "^4.0.2" + postcss-ordered-values "^4.1.2" + postcss-reduce-initial "^4.0.3" + postcss-reduce-transforms "^4.0.2" + postcss-svgo "^4.0.2" + postcss-unique-selectors "^4.0.1" + +cssnano-util-get-arguments@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" + integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= + +cssnano-util-get-match@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" + integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= + +cssnano-util-raw-cache@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" + integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== + dependencies: + postcss "^7.0.0" + +cssnano-util-same-parent@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" + integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== + +cssnano@^4.1.10: + version "4.1.10" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2" + integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== + dependencies: + cosmiconfig "^5.0.0" + cssnano-preset-default "^4.0.7" + is-resolvable "^1.0.0" + postcss "^7.0.0" + +csso@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.3.tgz#0d9985dc852c7cc2b2cacfbbe1079014d1a8e903" + integrity sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ== + dependencies: + css-tree "1.0.0-alpha.39" + +cyclist@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" + integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +de-indent@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" + integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0= + +debug@2.6.9, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@=3.1.0, debug@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@^3.1.1, debug@^3.2.5: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +decamelize@^1.0.0, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +deep-equal@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deepmerge@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753" + integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ== + +default-gateway@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" + integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== + dependencies: + execa "^1.0.0" + ip-regex "^2.1.0" + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +del@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" + integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== + dependencies: + "@types/glob" "^7.1.1" + globby "^6.1.0" + is-path-cwd "^2.0.0" + is-path-in-cwd "^2.0.0" + p-map "^2.0.0" + pify "^4.0.1" + rimraf "^2.6.3" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +delegate@^3.1.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" + integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-node@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" + integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-glob@^2.0.0, dir-glob@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" + integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== + dependencies: + path-type "^3.0.0" + +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= + +dns-packet@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" + integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== + dependencies: + ip "^1.1.0" + safe-buffer "^5.0.1" + +dns-txt@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" + integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= + dependencies: + buffer-indexof "^1.0.0" + +docsearch.js@^2.5.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/docsearch.js/-/docsearch.js-2.6.3.tgz#57cb4600d3b6553c677e7cbbe6a734593e38625d" + integrity sha512-GN+MBozuyz664ycpZY0ecdQE0ND/LSgJKhTLA0/v3arIS3S1Rpf2OJz6A35ReMsm91V5apcmzr5/kM84cvUg+A== + dependencies: + algoliasearch "^3.24.5" + autocomplete.js "0.36.0" + hogan.js "^3.0.2" + request "^2.87.0" + stack-utils "^1.0.1" + to-factory "^1.0.0" + zepto "^1.2.0" + +doctypes@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" + integrity sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk= + +dom-converter@^0.2: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + +dom-serializer@0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + +dom-serializer@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" + integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== + dependencies: + domelementtype "^1.3.0" + entities "^1.1.1" + +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + +domain-browser@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== + +domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== + +domelementtype@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" + integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== + dependencies: + domelementtype "1" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1, domutils@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + +dot-prop@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== + dependencies: + is-obj "^2.0.0" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +duplexify@^3.4.2, duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.562: + version "1.3.564" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.564.tgz#e9c319ae437b3eb8bbf3e3bae4bead5a21945961" + integrity sha512-fNaYN3EtKQWLQsrKXui8mzcryJXuA0LbCLoizeX6oayG2emBaS5MauKjCPAvc29NEY4FpLHIUWiP+Y0Bfrs5dg== + +elliptic@^6.5.3: + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" + integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.5.0" + tapable "^1.0.0" + +entities@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.2.tgz#ac74db0bba8d33808bbf36809c3a5c3683531436" + integrity sha512-dmD3AvJQBUjKpcNkoqr+x+IF0SdRtPz9Vk0uTy4yWqga9ibB6s4v++QFWNohjiUGoMlF552ZvNyXDxz5iW0qmw== + +entities@^1.1.1, entities@~1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + +entities@^2.0.0, entities@^2.0.3, entities@~2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f" + integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ== + +envify@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/envify/-/envify-4.1.0.tgz#f39ad3db9d6801b4e6b478b61028d3f0b6819f7e" + integrity sha512-IKRVVoAYr4pIx4yIWNsz9mOsboxlNXiu7TNBnem/K/uTHdkyzXWDzHCK7UTolqBbgaBz0tQHsD3YNls0uIIjiw== + dependencies: + esprima "^4.0.0" + through "~2.3.4" + +envinfo@^7.2.0: + version "7.7.3" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.7.3.tgz#4b2d8622e3e7366afb8091b23ed95569ea0208cc" + integrity sha512-46+j5QxbPWza0PB1i15nZx0xQ4I/EfQxg9J8Had3b408SV63nEtor2e+oiY63amTo9KTuh2a3XLObNwduxYwwA== + +errno@^0.1.3, errno@~0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== + dependencies: + prr "~1.0.1" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: + version "1.17.6" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" + integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.0" + is-regex "^1.1.0" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es6-promise@^4.1.0: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +escalade@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4" + integrity sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ== + +escape-goat@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" + integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== + +escape-html@^1.0.3, escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +esm@^3.2.25: + version "3.2.25" + resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" + integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esrecurse@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= + +events@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" + integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== + +eventsource@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" + integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== + dependencies: + original "^1.0.0" + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +express@^4.17.1: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +faye-websocket@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= + dependencies: + websocket-driver ">=0.5.1" + +faye-websocket@~0.11.1: + version "0.11.3" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" + integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== + dependencies: + websocket-driver ">=0.5.1" + +figgy-pudding@^3.5.1: + version "3.5.2" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" + integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +file-loader@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-3.0.1.tgz#f8e0ba0b599918b51adfe45d66d1e771ad560faa" + integrity sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw== + dependencies: + loader-utils "^1.0.2" + schema-utils "^1.0.0" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-cache-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +flush-write-stream@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== + dependencies: + inherits "^2.0.3" + readable-stream "^2.3.6" + +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + +follow-redirects@^1.0.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" + integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +from2@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-write-stream-atomic@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= + dependencies: + graceful-fs "^4.1.2" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^1.2.7: + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +fuse.js@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-6.0.0.tgz#6fc16cd7555648deda392892402d4188553552b2" + integrity sha512-e5Ap6mhF/WQ9bKqsMFTTR5/DS9qbYab4VXHtMdxCanH+VZkdUV2LqcgMO31etSQv53NXsguQF1bdqkrrPAM2HQ== + +fuse.js@^3.4.6: + version "3.6.1" + resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.6.1.tgz#7de85fdd6e1b3377c23ce010892656385fd9b10c" + integrity sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw== + +gensync@^1.0.0-beta.1: + version "1.0.0-beta.1" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" + integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-stream@^4.0.0, get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= + +glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-dirs@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201" + integrity sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A== + dependencies: + ini "^1.3.5" + +global@^4.3.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globby@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw= + dependencies: + array-union "^1.0.1" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +globby@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" + integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA= + dependencies: + array-union "^1.0.1" + dir-glob "^2.0.0" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + +globby@^9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" + integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== + dependencies: + "@types/glob" "^7.1.1" + array-union "^1.0.2" + dir-glob "^2.2.2" + fast-glob "^2.2.6" + glob "^7.1.3" + ignore "^4.0.3" + pify "^4.0.1" + slash "^2.0.0" + +good-listener@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" + integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA= + dependencies: + delegate "^3.1.2" + +got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + +gray-matter@^4.0.1, gray-matter@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.2.tgz#9aa379e3acaf421193fce7d2a28cebd4518ac454" + integrity sha512-7hB/+LxrOjq/dd8APlK0r24uL/67w7SkYnfwhNFwg/VDIGWGmduTDYf3WNstLW2fbbmRwrDGCVSJ2isuf2+4Hw== + dependencies: + js-yaml "^3.11.0" + kind-of "^6.0.2" + section-matter "^1.0.0" + strip-bom-string "^1.0.0" + +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.0, has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has-yarn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" + integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== + +has@^1.0.0, has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash-sum@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04" + integrity sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ= + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +he@1.2.x, he@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hex-color-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" + integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hogan.js@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/hogan.js/-/hogan.js-3.0.2.tgz#4cd9e1abd4294146e7679e41d7898732b02c7bfd" + integrity sha1-TNnhq9QpQUbnZ55B14mHMrAse/0= + dependencies: + mkdirp "0.3.0" + nopt "1.0.10" + +hotkeys-js@3.8.1, hotkeys-js@^3.7.3: + version "3.8.1" + resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.8.1.tgz#fa7051f73bf1dc92a8b8d580a40b247f91966376" + integrity sha512-YlhVQtyG9f1b7GhtzdhR0Pl+cImD1ZrKI6zYUa7QLd0zuThiL7RzZ+ANJyy7z+kmcCpNYBf5PjBa3CjiQ5PFpw== + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +hsl-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" + integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= + +hsla-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" + integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= + +html-comment-regex@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" + integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== + +html-entities@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44" + integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA== + +html-minifier@^3.2.3: + version "3.5.21" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c" + integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA== + dependencies: + camel-case "3.0.x" + clean-css "4.2.x" + commander "2.17.x" + he "1.2.x" + param-case "2.1.x" + relateurl "0.2.x" + uglify-js "3.4.x" + +html-tags@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" + integrity sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos= + +html-tags@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" + integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== + +htmlparser2@^3.3.0, htmlparser2@^3.9.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" + integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== + dependencies: + domelementtype "^1.3.1" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.1.1" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-parser-js@>=0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.2.tgz#da2e31d237b393aae72ace43882dd7e270a8ff77" + integrity sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ== + +http-proxy-middleware@0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" + integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== + dependencies: + http-proxy "^1.17.0" + is-glob "^4.0.0" + lodash "^4.17.11" + micromatch "^3.1.10" + +http-proxy@^1.17.0: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +icss-replace-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= + +icss-utils@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" + integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== + dependencies: + postcss "^7.0.14" + +ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + +iferr@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= + +ignore@^3.3.5: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== + +ignore@^4.0.3: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" + integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== + +import-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" + integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk= + dependencies: + import-from "^2.1.0" + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-from@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" + integrity sha1-M1238qev/VOqpHHUuAId7ja387E= + dependencies: + resolve-from "^3.0.0" + +import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= + +import-local@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" + integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== + dependencies: + pkg-dir "^3.0.0" + resolve-cwd "^2.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= + +infer-owner@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ini@^1.3.5, ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +internal-ip@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" + integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== + dependencies: + default-gateway "^4.2.0" + ipaddr.js "^1.9.0" + +invariant@^2.2.2, invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + +ip@^1.1.0, ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + +ipaddr.js@1.9.1, ipaddr.js@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-absolute-url@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= + +is-absolute-url@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arguments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" + integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.4, is-callable@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb" + integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-color-stop@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" + integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= + dependencies: + css-color-names "^0.0.4" + hex-color-regex "^1.1.0" + hsl-regex "^1.0.0" + hsla-regex "^1.0.0" + rgb-regex "^1.0.1" + rgba-regex "^1.0.0" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= + +is-expression@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-expression/-/is-expression-3.0.0.tgz#39acaa6be7fd1f3471dc42c7416e61c24317ac9f" + integrity sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8= + dependencies: + acorn "~4.0.2" + object-assign "^4.0.1" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-installed-globally@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" + integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== + dependencies: + global-dirs "^2.0.1" + is-path-inside "^3.0.1" + +is-npm@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" + integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-path-cwd@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-in-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" + integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== + dependencies: + is-path-inside "^2.1.0" + +is-path-inside@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" + integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== + dependencies: + path-is-inside "^1.0.2" + +is-path-inside@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" + integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== + +is-plain-obj@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-promise@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + +is-regex@^1.0.3, is-regex@^1.0.4, is-regex@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + dependencies: + has-symbols "^1.0.1" + +is-resolvable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-svg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" + integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ== + dependencies: + html-comment-regex "^1.1.0" + +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + +is-yarn-global@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" + integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isarray@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +javascript-stringify@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-1.6.0.tgz#142d111f3a6e3dae8f4a9afd77d45855b5a9cce3" + integrity sha1-FC0RHzpuPa6PSpr9d9RYVbWpzOM= + +javascript-stringify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.0.1.tgz#6ef358035310e35d667c675ed63d3eb7c1aa19e5" + integrity sha512-yV+gqbd5vaOYjqlbk16EG89xB5udgjqQF3C5FAORDg4f/IS1Yc5ERCv5e/57yBcfJYw05V5JyIXabhwb75Xxow== + +js-base64@^2.5.2: + version "2.6.4" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4" + integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ== + +js-stringify@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db" + integrity sha1-Fzb939lyTyijaCrcYjCufk6Weds= + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.11.0, js-yaml@^3.13.1: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json3@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" + integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== + +json5@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonp@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/jsonp/-/jsonp-0.2.1.tgz#a65b4fa0f10bda719a05441ea7b94c55f3e15bae" + integrity sha1-pltPoPEL2nGaBUQep7lMVfPhW64= + dependencies: + debug "^2.1.3" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +jstransformer@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/jstransformer/-/jstransformer-1.0.0.tgz#ed8bf0921e2f3f1ed4d5c1a44f68709ed24722c3" + integrity sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM= + dependencies: + is-promise "^2.0.0" + promise "^7.0.1" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +killable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" + integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +last-call-webpack-plugin@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555" + integrity sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w== + dependencies: + lodash "^4.17.5" + webpack-sources "^1.1.0" + +latest-version@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== + dependencies: + package-json "^6.3.0" + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levenary@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.1.tgz#842a9ee98d2075aa7faeedbe32679e9205f46f77" + integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ== + dependencies: + leven "^3.1.0" + +linkify-it@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf" + integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw== + dependencies: + uc.micro "^1.0.1" + +load-script@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/load-script/-/load-script-1.0.0.tgz#0491939e0bee5643ee494a7e3da3d2bac70c6ca4" + integrity sha1-BJGTngvuVkPuSUp+PaPSuscMbKQ= + +loader-runner@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" + integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== + +loader-utils@^0.2.16: + version "0.2.17" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" + integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g= + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + object-assign "^4.0.1" + +loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + +lodash.chunk@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.chunk/-/lodash.chunk-4.2.0.tgz#66e5ce1f76ed27b4303d8c6512e8d1216e8106bc" + integrity sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw= + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + +lodash.kebabcase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" + integrity sha1-hImxyw0p/4gZXM7KRI/21swpXDY= + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + +lodash.padstart@^4.6.1: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b" + integrity sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs= + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + +lodash.template@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== + dependencies: + lodash._reinterpolate "^3.0.0" + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= + +lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + +loglevel@^1.6.8: + version "1.7.0" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0" + integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ== + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= + +loose-envify@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lower-case@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^4.1.2: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +make-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +markdown-it-anchor@^5.0.2: + version "5.3.0" + resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz#d549acd64856a8ecd1bea58365ef385effbac744" + integrity sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA== + +markdown-it-attrs@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/markdown-it-attrs/-/markdown-it-attrs-3.0.3.tgz#92acdb16fe551cb056c5eb9848413443cafb5231" + integrity sha512-cLnICU2t61skNCr4Wih/sdza+UbQcqJGZwvqAypnbWA284nzDm+Gpc90iaRk/JjsIy4emag5v3s0rXFhFBWhCA== + +markdown-it-chain@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/markdown-it-chain/-/markdown-it-chain-1.3.0.tgz#ccf6fe86c10266bafb4e547380dfd7f277cc17bc" + integrity sha512-XClV8I1TKy8L2qsT9iX3qiV+50ZtcInGXI80CA+DP62sMs7hXlyV/RM3hfwy5O3Ad0sJm9xIwQELgANfESo8mQ== + dependencies: + webpack-chain "^4.9.0" + +markdown-it-container@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/markdown-it-container/-/markdown-it-container-2.0.0.tgz#0019b43fd02eefece2f1960a2895fba81a404695" + integrity sha1-ABm0P9Au7+zi8ZYKKJX7qBpARpU= + +markdown-it-emoji@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz#9bee0e9a990a963ba96df6980c4fddb05dfb4dcc" + integrity sha1-m+4OmpkKljupbfaYDE/dsF37Tcw= + +markdown-it-table-of-contents@^0.4.0: + version "0.4.4" + resolved "https://registry.yarnpkg.com/markdown-it-table-of-contents/-/markdown-it-table-of-contents-0.4.4.tgz#3dc7ce8b8fc17e5981c77cc398d1782319f37fbc" + integrity sha512-TAIHTHPwa9+ltKvKPWulm/beozQU41Ab+FIefRaQV1NRnpzwcV9QOe6wXQS5WLivm5Q/nlo0rl6laGkMDZE7Gw== + +markdown-it@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc" + integrity sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg== + dependencies: + argparse "^1.0.7" + entities "~2.0.0" + linkify-it "^2.0.0" + mdurl "^1.0.1" + uc.micro "^1.0.5" + +markdown-it@^8.4.1: + version "8.4.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54" + integrity sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ== + dependencies: + argparse "^1.0.7" + entities "~1.1.1" + linkify-it "^2.0.0" + mdurl "^1.0.1" + uc.micro "^1.0.5" + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +mdn-data@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" + integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== + +mdn-data@2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978" + integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA== + +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +memory-fs@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +memory-fs@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" + integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merge-source-map@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" + integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== + dependencies: + source-map "^0.6.1" + +merge2@^1.2.3: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.44.0, "mime-db@>= 1.43.0 < 2": + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + +mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.0.3, mime@^2.4.4: + version "2.4.6" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1" + integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + dependencies: + dom-walk "^0.1.0" + +mini-css-extract-plugin@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.6.0.tgz#a3f13372d6fcde912f3ee4cd039665704801e3b9" + integrity sha512-79q5P7YGI6rdnVyIAV4NXpBQJFWdkzJxCim3Kog4078fM0piAaFlwocqbejdWtLW1cEzCexPrh6EdyFsPgVdAw== + dependencies: + loader-utils "^1.1.0" + normalize-url "^2.0.1" + schema-utils "^1.0.0" + webpack-sources "^1.1.0" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mississippi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" + integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^3.0.0" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + integrity sha1-G79asbqCevI1dRQ0kEJkVfSB/h4= + +mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mkdirp@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +move-concurrently@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +multicast-dns-service-types@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" + integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= + +multicast-dns@^6.0.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" + integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== + dependencies: + dns-packet "^1.3.1" + thunky "^1.0.2" + +nan@^2.12.1: + version "2.14.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" + integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +neo-async@^2.5.0, neo-async@^2.6.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +no-case@^2.2.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== + dependencies: + lower-case "^1.1.1" + +node-forge@0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" + integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== + +node-libs-browser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^3.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.1" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.11.0" + vm-browserify "^1.0.1" + +node-releases@^1.1.60: + version "1.1.60" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084" + integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA== + +nopt@1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= + dependencies: + abbrev "1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= + +normalize-url@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" + integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== + dependencies: + prepend-http "^2.0.0" + query-string "^5.0.1" + sort-keys "^2.0.0" + +normalize-url@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" + integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== + +normalize-url@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +nprogress@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" + integrity sha1-y480xTIT2JVyP8urkH6UIq28r7E= + +nth-check@^1.0.2, nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" + integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== + +object-is@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6" + integrity sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.0, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" + integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +object.values@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +opencollective-postinstall@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== + +opn@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== + dependencies: + is-wsl "^1.1.0" + +optimize-css-assets-webpack-plugin@^5.0.1: + version "5.0.4" + resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.4.tgz#85883c6528aaa02e30bbad9908c92926bb52dc90" + integrity sha512-wqd6FdI2a5/FdoiCNNkEvLeA//lHHfG24Ln2Xm2qqdIk4aOlsR18jwpyOihqQ8849W3qu2DX8fOYxpvTMj+93A== + dependencies: + cssnano "^4.1.10" + last-call-webpack-plugin "^3.0.0" + +original@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" + integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== + dependencies: + url-parse "^1.4.3" + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-limit@^2.0.0, p-limit@^2.2.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + +p-retry@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" + integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== + dependencies: + retry "^0.12.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-json@^6.3.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== + dependencies: + got "^9.6.0" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^6.2.0" + +pako@~1.0.5: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parallel-transform@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" + integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== + dependencies: + cyclist "^1.0.1" + inherits "^2.0.3" + readable-stream "^2.1.5" + +param-case@2.1.x: + version "2.1.1" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" + integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= + dependencies: + no-case "^2.2.0" + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse5@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" + integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== + dependencies: + "@types/node" "*" + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" + integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + +pbkdf2@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94" + integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + +portfinder@^1.0.13, portfinder@^1.0.26: + version "1.0.28" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" + integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA== + dependencies: + async "^2.6.2" + debug "^3.1.1" + mkdirp "^0.5.5" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +postcss-calc@^7.0.1: + version "7.0.4" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.4.tgz#5e177ddb417341e6d4a193c5d9fd8ada79094f8b" + integrity sha512-0I79VRAd1UTkaHzY9w83P39YGO/M3bG7/tNLrHGEunBolfoGM0hSjrGvjoeaj0JE/zIw5GsI2KZ0UwDJqv5hjw== + dependencies: + postcss "^7.0.27" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.0.2" + +postcss-colormin@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" + integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== + dependencies: + browserslist "^4.0.0" + color "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-convert-values@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" + integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-discard-comments@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" + integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== + dependencies: + postcss "^7.0.0" + +postcss-discard-duplicates@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" + integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== + dependencies: + postcss "^7.0.0" + +postcss-discard-empty@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" + integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== + dependencies: + postcss "^7.0.0" + +postcss-discard-overridden@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" + integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== + dependencies: + postcss "^7.0.0" + +postcss-load-config@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.0.tgz#c84d692b7bb7b41ddced94ee62e8ab31b417b003" + integrity sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q== + dependencies: + cosmiconfig "^5.0.0" + import-cwd "^2.0.0" + +postcss-loader@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" + integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA== + dependencies: + loader-utils "^1.1.0" + postcss "^7.0.0" + postcss-load-config "^2.0.0" + schema-utils "^1.0.0" + +postcss-merge-longhand@^4.0.11: + version "4.0.11" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" + integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== + dependencies: + css-color-names "0.0.4" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + stylehacks "^4.0.0" + +postcss-merge-rules@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" + integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + cssnano-util-same-parent "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + vendors "^1.0.0" + +postcss-minify-font-values@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" + integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-gradients@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" + integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== + dependencies: + cssnano-util-get-arguments "^4.0.0" + is-color-stop "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-params@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" + integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== + dependencies: + alphanum-sort "^1.0.0" + browserslist "^4.0.0" + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + uniqs "^2.0.0" + +postcss-minify-selectors@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" + integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== + dependencies: + alphanum-sort "^1.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + +postcss-modules-extract-imports@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" + integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== + dependencies: + postcss "^7.0.5" + +postcss-modules-local-by-default@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz#dd9953f6dd476b5fd1ef2d8830c8929760b56e63" + integrity sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^6.0.0" + postcss-value-parser "^3.3.1" + +postcss-modules-scope@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" + integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^6.0.0" + +postcss-modules-values@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz#479b46dc0c5ca3dc7fa5270851836b9ec7152f64" + integrity sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w== + dependencies: + icss-replace-symbols "^1.1.0" + postcss "^7.0.6" + +postcss-normalize-charset@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" + integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== + dependencies: + postcss "^7.0.0" + +postcss-normalize-display-values@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" + integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-positions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" + integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== + dependencies: + cssnano-util-get-arguments "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-repeat-style@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" + integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== + dependencies: + cssnano-util-get-arguments "^4.0.0" + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-string@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" + integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== + dependencies: + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-timing-functions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" + integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-unicode@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" + integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-url@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" + integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-whitespace@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" + integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-ordered-values@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" + integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== + dependencies: + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-reduce-initial@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" + integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + +postcss-reduce-transforms@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" + integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== + dependencies: + cssnano-util-get-match "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-safe-parser@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz#a6d4e48f0f37d9f7c11b2a581bf00f8ba4870b96" + integrity sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g== + dependencies: + postcss "^7.0.26" + +postcss-selector-parser@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270" + integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA== + dependencies: + dot-prop "^5.2.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" + integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== + dependencies: + cssesc "^3.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-svgo@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" + integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw== + dependencies: + is-svg "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + svgo "^1.0.0" + +postcss-unique-selectors@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" + integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== + dependencies: + alphanum-sort "^1.0.0" + postcss "^7.0.0" + uniqs "^2.0.0" + +postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== + +postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" + integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== + +postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: + version "7.0.32" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" + integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +prettier@^1.18.2: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + +pretty-error@^2.0.2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" + integrity sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM= + dependencies: + renderkid "^2.0.1" + utila "~0.4" + +pretty-time@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e" + integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA== + +prismjs@^1.13.0, prismjs@^1.19.0, prismjs@^1.21.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.21.0.tgz#36c086ec36b45319ec4218ee164c110f9fc015a3" + integrity sha512-uGdSIu1nk3kej2iZsLyDoJ7e9bnPzIgY0naW/HdknGj61zScaprVEVGHrPoXqI+M9sP0NDnTK2jpkvmldpuqDw== + optionalDependencies: + clipboard "^2.0.0" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= + +promise@^7.0.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + +proxy-addr@~2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pug-attrs@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pug-attrs/-/pug-attrs-2.0.4.tgz#b2f44c439e4eb4ad5d4ef25cac20d18ad28cc336" + integrity sha512-TaZ4Z2TWUPDJcV3wjU3RtUXMrd3kM4Wzjbe3EWnSsZPsJ3LDI0F3yCnf2/W7PPFF+edUFQ0HgDL1IoxSz5K8EQ== + dependencies: + constantinople "^3.0.1" + js-stringify "^1.0.1" + pug-runtime "^2.0.5" + +pug-code-gen@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/pug-code-gen/-/pug-code-gen-2.0.2.tgz#ad0967162aea077dcf787838d94ed14acb0217c2" + integrity sha512-kROFWv/AHx/9CRgoGJeRSm+4mLWchbgpRzTEn8XCiwwOy6Vh0gAClS8Vh5TEJ9DBjaP8wCjS3J6HKsEsYdvaCw== + dependencies: + constantinople "^3.1.2" + doctypes "^1.1.0" + js-stringify "^1.0.1" + pug-attrs "^2.0.4" + pug-error "^1.3.3" + pug-runtime "^2.0.5" + void-elements "^2.0.1" + with "^5.0.0" + +pug-error@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/pug-error/-/pug-error-1.3.3.tgz#f342fb008752d58034c185de03602dd9ffe15fa6" + integrity sha512-qE3YhESP2mRAWMFJgKdtT5D7ckThRScXRwkfo+Erqga7dyJdY3ZquspprMCj/9sJ2ijm5hXFWQE/A3l4poMWiQ== + +pug-filters@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pug-filters/-/pug-filters-3.1.1.tgz#ab2cc82db9eeccf578bda89130e252a0db026aa7" + integrity sha512-lFfjNyGEyVWC4BwX0WyvkoWLapI5xHSM3xZJFUhx4JM4XyyRdO8Aucc6pCygnqV2uSgJFaJWW3Ft1wCWSoQkQg== + dependencies: + clean-css "^4.1.11" + constantinople "^3.0.1" + jstransformer "1.0.0" + pug-error "^1.3.3" + pug-walk "^1.1.8" + resolve "^1.1.6" + uglify-js "^2.6.1" + +pug-lexer@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/pug-lexer/-/pug-lexer-4.1.0.tgz#531cde48c7c0b1fcbbc2b85485c8665e31489cfd" + integrity sha512-i55yzEBtjm0mlplW4LoANq7k3S8gDdfC6+LThGEvsK4FuobcKfDAwt6V4jKPH9RtiE3a2Akfg5UpafZ1OksaPA== + dependencies: + character-parser "^2.1.1" + is-expression "^3.0.0" + pug-error "^1.3.3" + +pug-linker@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/pug-linker/-/pug-linker-3.0.6.tgz#f5bf218b0efd65ce6670f7afc51658d0f82989fb" + integrity sha512-bagfuHttfQOpANGy1Y6NJ+0mNb7dD2MswFG2ZKj22s8g0wVsojpRlqveEQHmgXXcfROB2RT6oqbPYr9EN2ZWzg== + dependencies: + pug-error "^1.3.3" + pug-walk "^1.1.8" + +pug-load@^2.0.12: + version "2.0.12" + resolved "https://registry.yarnpkg.com/pug-load/-/pug-load-2.0.12.tgz#d38c85eb85f6e2f704dea14dcca94144d35d3e7b" + integrity sha512-UqpgGpyyXRYgJs/X60sE6SIf8UBsmcHYKNaOccyVLEuT6OPBIMo6xMPhoJnqtB3Q3BbO4Z3Bjz5qDsUWh4rXsg== + dependencies: + object-assign "^4.1.0" + pug-walk "^1.1.8" + +pug-parser@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/pug-parser/-/pug-parser-5.0.1.tgz#03e7ada48b6840bd3822f867d7d90f842d0ffdc9" + integrity sha512-nGHqK+w07p5/PsPIyzkTQfzlYfuqoiGjaoqHv1LjOv2ZLXmGX1O+4Vcvps+P4LhxZ3drYSljjq4b+Naid126wA== + dependencies: + pug-error "^1.3.3" + token-stream "0.0.1" + +pug-plain-loader@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pug-plain-loader/-/pug-plain-loader-1.0.0.tgz#cef2a984c90251882109ec2d417a6b433aa6b42a" + integrity sha512-mDfq/qvJJ0xdug38mZ1ObW0BQTx9kAHnKqotXC+C00XQkKmsWaMe90JUg/kN4lS6MU7tpVsMZ+rmcnBSPfDtHA== + dependencies: + loader-utils "^1.1.0" + +pug-runtime@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/pug-runtime/-/pug-runtime-2.0.5.tgz#6da7976c36bf22f68e733c359240d8ae7a32953a" + integrity sha512-P+rXKn9un4fQY77wtpcuFyvFaBww7/91f3jHa154qU26qFAnOe6SW1CbIDcxiG5lLK9HazYrMCCuDvNgDQNptw== + +pug-strip-comments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/pug-strip-comments/-/pug-strip-comments-1.0.4.tgz#cc1b6de1f6e8f5931cf02ec66cdffd3f50eaf8a8" + integrity sha512-i5j/9CS4yFhSxHp5iKPHwigaig/VV9g+FgReLJWWHEHbvKsbqL0oP/K5ubuLco6Wu3Kan5p7u7qk8A4oLLh6vw== + dependencies: + pug-error "^1.3.3" + +pug-walk@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pug-walk/-/pug-walk-1.1.8.tgz#b408f67f27912f8c21da2f45b7230c4bd2a5ea7a" + integrity sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA== + +pug@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pug/-/pug-2.0.4.tgz#ee7682ec0a60494b38d48a88f05f3b0ac931377d" + integrity sha512-XhoaDlvi6NIzL49nu094R2NA6P37ijtgMDuWE+ofekDChvfKnzFal60bhSdiy8y2PBO6fmz3oMEIcfpBVRUdvw== + dependencies: + pug-code-gen "^2.0.2" + pug-filters "^3.1.1" + pug-lexer "^4.1.0" + pug-linker "^3.0.6" + pug-load "^2.0.12" + pug-parser "^5.0.1" + pug-runtime "^2.0.5" + pug-strip-comments "^1.0.4" + +pump@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +pupa@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.0.1.tgz#dbdc9ff48ffbea4a26a069b6f9f7abb051008726" + integrity sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA== + dependencies: + escape-goat "^2.0.0" + +q@^1.1.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring-es3@^0.2.0, querystring-es3@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= + +querystring@0.2.0, querystring@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +readdirp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" + integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== + dependencies: + picomatch "^2.2.1" + +reduce@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/reduce/-/reduce-1.0.2.tgz#0cd680ad3ffe0b060e57a5c68bdfce37168d361b" + integrity sha512-xX7Fxke/oHO5IfZSk77lvPa/7bjMh9BuCk4OOoX5XTXrM7s0Z+MkPfSDfz0q7r91BhhGSs8gii/VEN/7zhCPpQ== + dependencies: + object-keys "^1.1.0" + +regenerate-unicode-properties@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" + integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== + dependencies: + regenerate "^1.4.0" + +regenerate@^1.4.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.1.tgz#cad92ad8e6b591773485fbe05a485caf4f457e6f" + integrity sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A== + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + +regenerator-runtime@^0.13.4: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + +regenerator-transform@^0.14.2: + version "0.14.5" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" + integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== + dependencies: + "@babel/runtime" "^7.8.4" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexp.prototype.flags@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" + integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +regexpu-core@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938" + integrity sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.2.0" + regjsgen "^0.5.1" + regjsparser "^0.6.4" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.2.0" + +registry-auth-token@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.0.tgz#1d37dffda72bbecd0f581e4715540213a65eb7da" + integrity sha512-P+lWzPrsgfN+UEpDS3U8AQKg/UjZX6mQSJueZj3EK+vNESoqBSpBUD3gmu4sF9lOsjXWjF11dQKUqemf3veq1w== + dependencies: + rc "^1.2.8" + +registry-url@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== + dependencies: + rc "^1.2.8" + +regjsgen@^0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" + integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== + +regjsparser@^0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272" + integrity sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw== + dependencies: + jsesc "~0.5.0" + +relateurl@0.2.x: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +renderkid@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.3.tgz#380179c2ff5ae1365c522bf2fcfcff01c5b74149" + integrity sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA== + dependencies: + css-select "^1.1.0" + dom-converter "^0.2" + htmlparser2 "^3.3.0" + strip-ansi "^3.0.0" + utila "^0.4.0" + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.5.2, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +request@^2.87.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= + dependencies: + resolve-from "^3.0.0" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@^1.1.6, resolve@^1.2.0, resolve@^1.3.2, resolve@^1.8.1: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + +rgb-regex@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" + integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= + +rgba-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" + integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= + +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + integrity sha1-YTObci/mo1FWiSENJOFMlhSGE+8= + dependencies: + align-text "^0.1.1" + +rimraf@^2.5.4, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= + dependencies: + aproba "^1.1.1" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@^2.1.2, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@~1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +schema-utils@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" + integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== + dependencies: + ajv "^6.1.0" + ajv-errors "^1.0.0" + ajv-keywords "^3.1.0" + +schema-utils@^2.6.5: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== + dependencies: + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" + +section-matter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" + integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== + dependencies: + extend-shallow "^2.0.1" + kind-of "^6.0.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= + +select@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" + integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= + +selfsigned@^1.10.7: + version "1.10.7" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.7.tgz#da5819fd049d5574f28e88a9bcc6dbc6e6f3906b" + integrity sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA== + dependencies: + node-forge "0.9.0" + +semver-diff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" + integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== + dependencies: + semver "^6.3.0" + +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@^5.1.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.0.0, semver@^6.1.0, semver@^6.2.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serialize-javascript@^3.1.0, serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= + dependencies: + is-arrayish "^0.3.1" + +sitemap@^3.0.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/sitemap/-/sitemap-3.2.2.tgz#3f77c358fa97b555c879e457098e39910095c62b" + integrity sha512-TModL/WU4m2q/mQcrDgNANn0P4LwprM9MMvG4hu5zP4c6IIKs2YLTu6nXXnNr8ODW/WFtxKggiJ1EGn2W0GNmg== + dependencies: + lodash.chunk "^4.2.0" + lodash.padstart "^4.6.1" + whatwg-url "^7.0.0" + xmlbuilder "^13.0.0" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= + +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + +smoothscroll-polyfill@^0.4.3: + version "0.4.4" + resolved "https://registry.yarnpkg.com/smoothscroll-polyfill/-/smoothscroll-polyfill-0.4.4.tgz#3a259131dc6930e6ca80003e1cb03b603b69abf8" + integrity sha512-TK5ZA9U5RqCwMpfoMq/l1mrH0JAR7y7KRvOBx0n2869aLxch+gT9GhN3yUfjiw+d/DiF1mKo14+hd62JyMmoBg== + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +sockjs-client@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" + integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== + dependencies: + debug "^3.2.5" + eventsource "^1.0.7" + faye-websocket "~0.11.1" + inherits "^2.0.3" + json3 "^3.3.2" + url-parse "^1.4.3" + +sockjs@0.3.20: + version "0.3.20" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.20.tgz#b26a283ec562ef8b2687b44033a4eeceac75d855" + integrity sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA== + dependencies: + faye-websocket "^0.10.0" + uuid "^3.4.0" + websocket-driver "0.6.5" + +sort-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= + dependencies: + is-plain-obj "^1.0.0" + +source-list-map@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@~0.5.12: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + integrity sha1-dc449SvwczxafwwRjYEzSiu19BI= + +source-map@^0.5.0, source-map@^0.5.6, source-map@~0.5.1: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +ssri@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" + integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== + dependencies: + figgy-pudding "^3.5.1" + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +stack-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" + integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +std-env@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-2.2.1.tgz#2ffa0fdc9e2263e0004c1211966e960948a40f6b" + integrity sha512-IjYQUinA3lg5re/YMlwlfhqNRTzMZMqE+pezevdcTaHceqx8ngEi1alX9nNCk9Sc81fy1fLDeQoaCzeiW1yBOQ== + dependencies: + ci-info "^1.6.0" + +stream-browserify@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" + integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-each@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" + integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.0.0, string-width@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trimend@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" + integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.trimstart@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" + integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string_decoder@^1.0.0, string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-bom-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" + integrity sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI= + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +stylehacks@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" + integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + +stylus-loader@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-3.0.2.tgz#27a706420b05a38e038e7cacb153578d450513c6" + integrity sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA== + dependencies: + loader-utils "^1.0.2" + lodash.clonedeep "^4.5.0" + when "~3.6.x" + +stylus@^0.54.5, stylus@^0.54.8: + version "0.54.8" + resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.8.tgz#3da3e65966bc567a7b044bfe0eece653e099d147" + integrity sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg== + dependencies: + css-parse "~2.0.0" + debug "~3.1.0" + glob "^7.1.6" + mkdirp "~1.0.4" + safer-buffer "^2.1.2" + sax "~1.2.4" + semver "^6.3.0" + source-map "^0.7.3" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +svg-tags@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" + integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q= + +svgo@^1.0.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" + integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== + dependencies: + chalk "^2.4.1" + coa "^2.0.2" + css-select "^2.0.0" + css-select-base-adapter "^0.1.1" + css-tree "1.0.0-alpha.37" + csso "^4.0.2" + js-yaml "^3.13.1" + mkdirp "~0.5.1" + object.values "^1.1.0" + sax "~1.2.4" + stable "^0.1.8" + unquote "~1.1.1" + util.promisify "~1.0.0" + +tapable@^1.0.0, tapable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + +term-size@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.0.tgz#1f16adedfe9bdc18800e1776821734086fcc6753" + integrity sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw== + +terser-webpack-plugin@^1.4.3: + version "1.4.5" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" + integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== + dependencies: + cacache "^12.0.2" + find-cache-dir "^2.1.0" + is-wsl "^1.1.0" + schema-utils "^1.0.0" + serialize-javascript "^4.0.0" + source-map "^0.6.1" + terser "^4.1.2" + webpack-sources "^1.4.0" + worker-farm "^1.7.0" + +terser@^4.1.2: + version "4.8.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" + integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== + dependencies: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +through2@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through@~2.3.4: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +timers-browserify@^2.0.4: + version "2.0.11" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f" + integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ== + dependencies: + setimmediate "^1.0.4" + +timsort@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= + +tiny-cookie@^2.3.1, tiny-cookie@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/tiny-cookie/-/tiny-cookie-2.3.2.tgz#3b5fb4e0888cfa0b4728d5f6b7be3d3a88e6a5f0" + integrity sha512-qbymkVh+6+Gc/c9sqnvbG+dOHH6bschjphK3SHgIfT6h/t+63GBL37JXNoXEc6u/+BcwU6XmaWUuf19ouLVtPg== + +tiny-emitter@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" + integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= + +to-factory@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-factory/-/to-factory-1.0.0.tgz#8738af8bd97120ad1d4047972ada5563bf9479b1" + integrity sha1-hzivi9lxIK0dQEeXKtpVY7+UebE= + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +token-stream@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/token-stream/-/token-stream-0.0.1.tgz#ceeefc717a76c4316f126d0b9dbaa55d7e7df01a" + integrity sha1-zu78cXp2xDFvEm0LnbqlXX598Bo= + +toml@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" + integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== + +toposort@^1.0.0: + version "1.0.7" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029" + integrity sha1-LmhELZ9k7HILjMieZEOsbKqVACk= + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= + dependencies: + punycode "^2.1.0" + +tslib@^1.9.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +uc.micro@^1.0.1, uc.micro@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" + integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== + +uglify-js@3.4.x: + version "3.4.10" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" + integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw== + dependencies: + commander "~2.19.0" + source-map "~0.6.1" + +uglify-js@^2.6.1: + version "2.8.29" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" + integrity sha1-KcVzMUgFe7Th913zW3qcty5qWd0= + dependencies: + source-map "~0.5.1" + yargs "~3.10.0" + optionalDependencies: + uglify-to-browserify "~1.0.0" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + integrity sha1-bgkk1r2mta/jSeOabWMoUKD4grc= + +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== + dependencies: + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" + integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== + +unicode-property-aliases-ecmascript@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" + integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= + +uniqs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= + +unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== + dependencies: + imurmurhash "^0.1.4" + +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unquote@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" + integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.1.0, upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +update-notifier@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.1.tgz#895fc8562bbe666179500f9f2cebac4f26323746" + integrity sha512-9y+Kds0+LoLG6yN802wVXoIfxYEwh3FlZwzMwpCZp62S2i1/Jzeqb9Eeeju3NSHccGGasfGlK5/vEHbAifYRDg== + dependencies: + boxen "^4.2.0" + chalk "^3.0.0" + configstore "^5.0.1" + has-yarn "^2.1.0" + import-lazy "^2.1.0" + is-ci "^2.0.0" + is-installed-globally "^0.3.1" + is-npm "^4.0.0" + is-yarn-global "^0.3.0" + latest-version "^5.0.0" + pupa "^2.0.1" + semver-diff "^3.1.1" + xdg-basedir "^4.0.0" + +upper-case@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= + +uri-js@^4.2.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +url-loader@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.1.2.tgz#b971d191b83af693c5e3fea4064be9e1f2d7f8d8" + integrity sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg== + dependencies: + loader-utils "^1.1.0" + mime "^2.0.3" + schema-utils "^1.0.0" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url-parse@^1.4.3: + version "1.4.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util.promisify@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + +util.promisify@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" + integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.2" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.0" + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= + dependencies: + inherits "2.0.1" + +util@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== + dependencies: + inherits "2.0.3" + +utila@^0.4.0, utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@^3.3.2, uuid@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +v-runtime-template@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/v-runtime-template/-/v-runtime-template-1.10.0.tgz#8ea7066c37cf4be5c701a06ca247e1afda89c4be" + integrity sha512-WLlq9jUepSfUrMEenw3mn7FDXX6hhbl11JjC1OKhwLzifHzVrY5a696TUHDPyj9jke3GGnR7b+2T3od/RL5cww== + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +vendors@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" + integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vm-browserify@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== + +void-elements@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= + +vue-hot-reload-api@^2.3.0: + version "2.3.4" + resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2" + integrity sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog== + +vue-loader@^15.7.1: + version "15.9.3" + resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.9.3.tgz#0de35d9e555d3ed53969516cac5ce25531299dda" + integrity sha512-Y67VnGGgVLH5Voostx8JBZgPQTlDQeOVBLOEsjc2cXbCYBKexSKEpOA56x0YZofoDOTszrLnIShyOX1p9uCEHA== + dependencies: + "@vue/component-compiler-utils" "^3.1.0" + hash-sum "^1.0.2" + loader-utils "^1.1.0" + vue-hot-reload-api "^2.3.0" + vue-style-loader "^4.1.0" + +vue-router@^3.1.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.4.3.tgz#fa93768616ee338aa174f160ac965167fa572ffa" + integrity sha512-BADg1mjGWX18Dpmy6bOGzGNnk7B/ZA0RxuA6qedY/YJwirMfKXIDzcccmHbQI0A6k5PzMdMloc0ElHfyOoX35A== + +vue-server-renderer@^2.6.10: + version "2.6.12" + resolved "https://registry.yarnpkg.com/vue-server-renderer/-/vue-server-renderer-2.6.12.tgz#a8cb9c49439ef205293cb41c35d0d2b0541653a5" + integrity sha512-3LODaOsnQx7iMFTBLjki8xSyOxhCtbZ+nQie0wWY4iOVeEtTg1a3YQAjd82WvKxrWHHTshjvLb7OXMc2/dYuxw== + dependencies: + chalk "^1.1.3" + hash-sum "^1.0.2" + he "^1.1.0" + lodash.template "^4.5.0" + lodash.uniq "^4.5.0" + resolve "^1.2.0" + serialize-javascript "^3.1.0" + source-map "0.5.6" + +vue-style-loader@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.2.tgz#dedf349806f25ceb4e64f3ad7c0a44fba735fcf8" + integrity sha512-0ip8ge6Gzz/Bk0iHovU9XAUQaFt/G2B61bnWa2tCcqqdgfHs1lF9xXorFbE55Gmy92okFT+8bfmySuUOu13vxQ== + dependencies: + hash-sum "^1.0.2" + loader-utils "^1.0.2" + +vue-template-compiler@^2.6.10: + version "2.6.12" + resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz#947ed7196744c8a5285ebe1233fe960437fcc57e" + integrity sha512-OzzZ52zS41YUbkCBfdXShQTe69j1gQDZ9HIX8miuC9C3rBCk9wIRjLiZZLrmX9V+Ftq/YEyv1JaVr5Y/hNtByg== + dependencies: + de-indent "^1.0.2" + he "^1.1.0" + +vue-template-es2015-compiler@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" + integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw== + +vue@^2.6.10: + version "2.6.12" + resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.12.tgz#f5ebd4fa6bd2869403e29a896aed4904456c9123" + integrity sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg== + +vuepress-html-webpack-plugin@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/vuepress-html-webpack-plugin/-/vuepress-html-webpack-plugin-3.2.0.tgz#219be272ad510faa8750d2d4e70fd028bfd1c16e" + integrity sha512-BebAEl1BmWlro3+VyDhIOCY6Gef2MCBllEVAP3NUAtMguiyOwo/dClbwJ167WYmcxHJKLl7b0Chr9H7fpn1d0A== + dependencies: + html-minifier "^3.2.3" + loader-utils "^0.2.16" + lodash "^4.17.3" + pretty-error "^2.0.2" + tapable "^1.0.0" + toposort "^1.0.0" + util.promisify "1.0.0" + +vuepress-plugin-container@^2.0.2: + version "2.1.5" + resolved "https://registry.yarnpkg.com/vuepress-plugin-container/-/vuepress-plugin-container-2.1.5.tgz#37fff05662fedbd63ffd3a5463b2592c7a7f3133" + integrity sha512-TQrDX/v+WHOihj3jpilVnjXu9RcTm6m8tzljNJwYhxnJUW0WWQ0hFLcDTqTBwgKIFdEiSxVOmYE+bJX/sq46MA== + dependencies: + "@vuepress/shared-utils" "^1.2.0" + markdown-it-container "^2.0.0" + +vuepress-plugin-sitemap@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/vuepress-plugin-sitemap/-/vuepress-plugin-sitemap-2.3.1.tgz#51298aca77a5de96396fdbd1103e1637dd61ae6a" + integrity sha512-n+8lbukhrKrsI9H/EX0EBgkE1pn85LAQFvQ5dIvrZP4Kz6JxPOPPNTQmZMhahQV1tXbLZQCEN7A1WZH4x+arJQ== + dependencies: + sitemap "^3.0.0" + +vuepress-plugin-smooth-scroll@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/vuepress-plugin-smooth-scroll/-/vuepress-plugin-smooth-scroll-0.0.3.tgz#6eff2d4c186cca917cc9f7df2b0af7de7c8c6438" + integrity sha512-qsQkDftLVFLe8BiviIHaLV0Ea38YLZKKonDGsNQy1IE0wllFpFIEldWD8frWZtDFdx6b/O3KDMgVQ0qp5NjJCg== + dependencies: + smoothscroll-polyfill "^0.4.3" + +vuepress-theme-cosmos@^1.0.172: + version "1.0.172" + resolved "https://registry.yarnpkg.com/vuepress-theme-cosmos/-/vuepress-theme-cosmos-1.0.172.tgz#3a1da55c92cc06a81683044ea7fa0a762d2a8458" + integrity sha512-/iSIA8CVzfsHWBL/DnlJNClHXZQQhHrmTIL6toTXfcWOo1UlGRcs+gNKcMbW+tToZjC140NBwX9sdNO8evx2mg== + dependencies: + "@cosmos-ui/vue" "^0.33.0" + "@vuepress/plugin-google-analytics" "1.5.3" + algoliasearch "^4.2.0" + axios "^0.19.2" + cheerio "^1.0.0-rc.3" + clipboard-copy "^3.1.0" + entities "2.0.2" + esm "^3.2.25" + fuse.js "6.0.0" + gray-matter "^4.0.2" + hotkeys-js "3.8.1" + jsonp "^0.2.1" + markdown-it "^10.0.0" + markdown-it-attrs "^3.0.3" + prismjs "^1.21.0" + pug "^2.0.4" + pug-plain-loader "^1.0.0" + stylus "^0.54.8" + stylus-loader "^3.0.2" + tiny-cookie "^2.3.2" + v-runtime-template "^1.10.0" + vuepress "^1.5.4" + vuepress-plugin-sitemap "^2.3.1" + +vuepress@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/vuepress/-/vuepress-1.5.4.tgz#282d2412c1c7269d8bd93b83d421ef53b77b45f6" + integrity sha512-F25r65BzxDFAJmWIN9s9sQSndLIf1ldAKEwkeXCqE4p2lsx/eVvQJL3DzOeeR2WgCFOkhFMKWIV+CthTGdNTZg== + dependencies: + "@vuepress/core" "1.5.4" + "@vuepress/theme-default" "1.5.4" + cac "^6.5.6" + envinfo "^7.2.0" + opencollective-postinstall "^2.0.2" + update-notifier "^4.0.0" + +watchpack-chokidar2@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz#9948a1866cbbd6cb824dea13a7ed691f6c8ddff0" + integrity sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA== + dependencies: + chokidar "^2.1.8" + +watchpack@^1.7.2, watchpack@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.4.tgz#6e9da53b3c80bb2d6508188f5b200410866cd30b" + integrity sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg== + dependencies: + graceful-fs "^4.1.2" + neo-async "^2.5.0" + optionalDependencies: + chokidar "^3.4.1" + watchpack-chokidar2 "^2.0.0" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + +webpack-chain@^4.9.0: + version "4.12.1" + resolved "https://registry.yarnpkg.com/webpack-chain/-/webpack-chain-4.12.1.tgz#6c8439bbb2ab550952d60e1ea9319141906c02a6" + integrity sha512-BCfKo2YkDe2ByqkEWe1Rw+zko4LsyS75LVr29C6xIrxAg9JHJ4pl8kaIZ396SUSNp6b4815dRZPSTAS8LlURRQ== + dependencies: + deepmerge "^1.5.2" + javascript-stringify "^1.6.0" + +webpack-chain@^6.0.0: + version "6.5.1" + resolved "https://registry.yarnpkg.com/webpack-chain/-/webpack-chain-6.5.1.tgz#4f27284cbbb637e3c8fbdef43eef588d4d861206" + integrity sha512-7doO/SRtLu8q5WM0s7vPKPWX580qhi0/yBHkOxNkv50f6qB76Zy9o2wRTrrPULqYTvQlVHuvbA8v+G5ayuUDsA== + dependencies: + deepmerge "^1.5.2" + javascript-stringify "^2.0.1" + +webpack-dev-middleware@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3" + integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw== + dependencies: + memory-fs "^0.4.1" + mime "^2.4.4" + mkdirp "^0.5.1" + range-parser "^1.2.1" + webpack-log "^2.0.0" + +webpack-dev-server@^3.5.1: + version "3.11.0" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz#8f154a3bce1bcfd1cc618ef4e703278855e7ff8c" + integrity sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg== + dependencies: + ansi-html "0.0.7" + bonjour "^3.5.0" + chokidar "^2.1.8" + compression "^1.7.4" + connect-history-api-fallback "^1.6.0" + debug "^4.1.1" + del "^4.1.1" + express "^4.17.1" + html-entities "^1.3.1" + http-proxy-middleware "0.19.1" + import-local "^2.0.0" + internal-ip "^4.3.0" + ip "^1.1.5" + is-absolute-url "^3.0.3" + killable "^1.0.1" + loglevel "^1.6.8" + opn "^5.5.0" + p-retry "^3.0.1" + portfinder "^1.0.26" + schema-utils "^1.0.0" + selfsigned "^1.10.7" + semver "^6.3.0" + serve-index "^1.9.1" + sockjs "0.3.20" + sockjs-client "1.4.0" + spdy "^4.0.2" + strip-ansi "^3.0.1" + supports-color "^6.1.0" + url "^0.11.0" + webpack-dev-middleware "^3.7.2" + webpack-log "^2.0.0" + ws "^6.2.1" + yargs "^13.3.2" + +webpack-log@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" + integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== + dependencies: + ansi-colors "^3.0.0" + uuid "^3.3.2" + +webpack-merge@^4.1.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + +webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack@^4.8.1: + version "4.44.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.1.tgz#17e69fff9f321b8f117d1fda714edfc0b939cc21" + integrity sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/wasm-edit" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + acorn "^6.4.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" + enhanced-resolve "^4.3.0" + eslint-scope "^4.0.3" + json-parse-better-errors "^1.0.2" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.3" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" + schema-utils "^1.0.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.3" + watchpack "^1.7.4" + webpack-sources "^1.4.1" + +webpackbar@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-3.2.0.tgz#bdaad103fad11a4e612500e72aaae98b08ba493f" + integrity sha512-PC4o+1c8gWWileUfwabe0gqptlXUDJd5E0zbpr2xHP1VSOVlZVPBZ8j6NCR8zM5zbKdxPhctHXahgpNK1qFDPw== + dependencies: + ansi-escapes "^4.1.0" + chalk "^2.4.1" + consola "^2.6.0" + figures "^3.0.0" + pretty-time "^1.1.0" + std-env "^2.2.1" + text-table "^0.2.0" + wrap-ansi "^5.1.0" + +websocket-driver@0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" + integrity sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY= + dependencies: + websocket-extensions ">=0.1.1" + +websocket-driver@>=0.5.1: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +when@~3.6.x: + version "3.6.4" + resolved "https://registry.yarnpkg.com/when/-/when-3.6.4.tgz#473b517ec159e2b85005497a13983f095412e34e" + integrity sha1-RztRfsFZ4rhQBUl6E5g/CVQS404= + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + integrity sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0= + +with@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/with/-/with-5.1.1.tgz#fa4daa92daf32c4ea94ed453c81f04686b575dfe" + integrity sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4= + dependencies: + acorn "^3.1.0" + acorn-globals "^3.0.0" + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= + +worker-farm@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" + integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== + dependencies: + errno "~0.1.7" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +ws@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" + integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== + dependencies: + async-limiter "~1.0.0" + +xdg-basedir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== + +xmlbuilder@^13.0.0: + version "13.0.2" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-13.0.2.tgz#02ae33614b6a047d1c32b5389c1fdacb2bce47a7" + integrity sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ== + +xtend@^4.0.0, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + integrity sha1-9+572FfdfB0tOMDnTvvWgdFDH9E= + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0" + +zepto@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/zepto/-/zepto-1.2.0.tgz#e127bd9e66fd846be5eab48c1394882f7c0e4f98" + integrity sha1-4Se9nmb9hGvl6rSME5SIL3wOT5g= diff --git a/go.mod b/go.mod new file mode 100644 index 0000000000..aede0fe7ce --- /dev/null +++ b/go.mod @@ -0,0 +1,30 @@ +module github.com/cosmos/ethermint + +go 1.14 + +require ( + github.com/aristanetworks/goarista v0.0.0-20200331225509-2cc472e8fbd6 // indirect + github.com/btcsuite/btcd v0.21.0-beta + github.com/btcsuite/btcutil v1.0.2 + github.com/cespare/cp v1.1.1 // indirect + github.com/cosmos/cosmos-sdk v0.39.1 + github.com/deckarep/golang-set v1.7.1 // indirect + github.com/ethereum/go-ethereum v1.9.21 + github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect + github.com/gorilla/mux v1.8.0 + github.com/gorilla/websocket v1.4.2 + github.com/mattn/go-colorable v0.1.7 // indirect + github.com/miguelmota/go-ethereum-hdwallet v0.0.0-20200123000308-a60dcd172b4c + github.com/pkg/errors v0.9.1 + github.com/prometheus/tsdb v0.9.1 // indirect + github.com/spf13/afero v1.2.2 // indirect + github.com/spf13/cobra v1.0.0 + github.com/spf13/viper v1.7.1 + github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48 + github.com/stretchr/testify v1.6.1 + github.com/tendermint/tendermint v0.33.7 + github.com/tendermint/tm-db v0.5.1 + github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 + gopkg.in/yaml.v2 v2.3.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000000..18fb76d4a5 --- /dev/null +++ b/go.sum @@ -0,0 +1,943 @@ +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/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/99designs/keyring v1.1.3 h1:mEV3iyZWjkxQ7R8ia8GcG97vCX5zQQ7n4o8R2BylwQY= +github.com/99designs/keyring v1.1.3/go.mod h1:657DQuMrBZRtuL/voxVyiyb6zpMehlm5vLB9Qwrv904= +github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= +github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= +github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +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/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= +github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= +github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/sarama v1.23.1/go.mod h1:XLH1GYJnLVE0XCr6KdJGVJRTwY30moWNJ4sERjXX6fs= +github.com/Shopify/sarama v1.26.1/go.mod h1:NbSGBSSndYaIhRcBtY9V0U7AyH+x71bG668AuWys/yU= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw= +github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8= +github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI= +github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= +github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +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/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= +github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/aristanetworks/fsnotify v1.4.2/go.mod h1:D/rtu7LpjYM8tRJphJ0hUBYpjai8SfX+aSNsWDTq/Ks= +github.com/aristanetworks/glog v0.0.0-20180419172825-c15b03b3054f/go.mod h1:KASm+qXFKs/xjSoWn30NrWBBvdTTQq+UjkhjEJHfSFA= +github.com/aristanetworks/glog v0.0.0-20191112221043-67e8567f59f3/go.mod h1:KASm+qXFKs/xjSoWn30NrWBBvdTTQq+UjkhjEJHfSFA= +github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= +github.com/aristanetworks/goarista v0.0.0-20190912214011-b54698eaaca6/go.mod h1:Z4RTxGAuYhPzcq8+EdRM+R8M48Ssle2TsWtwRKa+vns= +github.com/aristanetworks/goarista v0.0.0-20200331225509-2cc472e8fbd6 h1:Pcu4aKyFfpH0aXLnYJrsTjdRvXNY4SbODsb0pMTZxhA= +github.com/aristanetworks/goarista v0.0.0-20200331225509-2cc472e8fbd6/go.mod h1:QZe5Yh80Hp1b6JxQdpfSEEe8X7hTyTEZSosSrFf/oJE= +github.com/aristanetworks/splunk-hec-go v0.3.3/go.mod h1:1VHO9r17b0K7WmOlLb9nTk/2YanvOEnLMUgsFrxBROc= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d h1:1aAija9gr0Hyv4KfQcRcwlmFIrhkDmIj2dz5bkg/s/8= +github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d/go.mod h1:icNx/6QdFblhsEjZehARqbNumymUT/ydwlLojFdv7Sk= +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/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= +github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= +github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M= +github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= +github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= +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 h1:Tvd0BfvqX9o823q1j2UZ/epQo09eJh6dTcRp79ilIN4= +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 h1:ZxaA6lo2EpxGddsA8JwWOcxlzRybb444sgmeJQMJGQE= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= +github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +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 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cosmos/cosmos-sdk v0.39.1 h1:vhjf9PZh9ph8btAj9aBpHoVITgVVjNBpM3x5Gl/Vwac= +github.com/cosmos/cosmos-sdk v0.39.1/go.mod h1:ry2ROl5n+f2/QXpKJo3rdWNJwll00z7KhIVcxNcl16M= +github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU= +github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= +github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4= +github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= +github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= +github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/danieljoos/wincred v1.0.2 h1:zf4bhty2iLuwgjgpraD2E9UbvO+fe54XXGJbOwe23fU= +github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U= +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 v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= +github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a h1:mq+R6XEM6lJX5VlLyZIrUSP8tSuJp82xTK89hvBwJbU= +github.com/dvsekhvalnov/jose2go v0.0.0-20180829124132-7f401d37b68a/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/elastic/gosigar v0.10.5/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ethereum/go-ethereum v1.9.5/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= +github.com/ethereum/go-ethereum v1.9.21 h1:8qRlhzrItnmUGdVlBzZLI2Tb46S0RdSNjFwICo781ws= +github.com/ethereum/go-ethereum v1.9.21/go.mod h1:RXAVzbGrSGmDkDnHymruTAIEjUR3E4TX0EOpaj702sI= +github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= +github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= +github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= +github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= +github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y= +github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= +github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +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-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/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +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 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +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/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/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-0.20190508161146-9fa652df1129/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +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.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 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26 h1:lMm2hD9Fy0ynom5+85/pbdkiYcBqM1JWmhpAXLmy0fw= +github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26/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 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= +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 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +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/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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= +github.com/google/uuid v1.0.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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg= +github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= +github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +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/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277 h1:E0whKxgp2ojts0FDgUA8dl62bmH0LxKanMoBr6MDTDM= +github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= +github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= +github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= +github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +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.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/holiman/uint256 v1.1.1 h1:4JywC80b+/hSfljFlEBLHrrh+CIONLDz9NuFl0af4Mw= +github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= +github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= +github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883 h1:FSeK4fZCo8u40n2JMnyAsd6x7+SbvoOMHvQOU/n10P4= +github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= +github.com/influxdata/influxdb1-client v0.0.0-20190809212627-fc22c7df067e/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 h1:6OvNmYgJyexcZ3pYbTI9jWx5tHo1Dee/tWbLMfPe2TA= +github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= +github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= +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.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/karalabe/hid v1.0.0/go.mod h1:Vr51f8rUOLYrfrWDFlV12GGQgM5AT8sVh+2fY4MPeu8= +github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 h1:I/yrLt2WilKxlQKCM52clh5rGzTKpVctGT1lH4Dc8Jw= +github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM= +github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +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 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.10.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= +github.com/klauspost/reedsolomon v1.9.3/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +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/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= +github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= +github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miguelmota/go-ethereum-hdwallet v0.0.0-20200123000308-a60dcd172b4c h1:cbhK2JT4nl7k8frmCN98ttRdSGP75x9mDxDhlQ1kHQQ= +github.com/miguelmota/go-ethereum-hdwallet v0.0.0-20200123000308-a60dcd172b4c/go.mod h1:Z4zI+CdJB1fyrZ1jfevFH6flNV9izrLZnQAeuD6Wkjk= +github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0= +github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= +github.com/minio/highwayhash v1.0.0 h1:iMSDhgUILCr0TNm8LWlSjF8N0ZIj2qbO8WHp6Q/J2BA= +github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= +github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c h1:1RHs3tNxjXGHeul8z2t6H2N2TlAqpKe5yryJztRx4Jk= +github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +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.10.1/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 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +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.0/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 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/openconfig/gnmi v0.0.0-20190823184014-89b2bf29312c/go.mod h1:t+O9It+LKzfOAhKTT5O0ehDix+MTqbtT0T9t+7zzOvc= +github.com/openconfig/reference v0.0.0-20190727015836-8dfd928c9696/go.mod h1:ym2A+zigScwkSEb/cVQB0/ZMpU3rqiH6X7WRRsxgOGw= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= +github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4= +github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= +github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= +github.com/pierrec/lz4 v0.0.0-20190327172049-315a67e90e41/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.4.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +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/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= +github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/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.1.0/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.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +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.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.0.10 h1:QJQN3jYQhkamO4mhfUWqdDH2asK7ONOI9MTWjyAxNKM= +github.com/prometheus/procfs v0.0.10/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/prometheus/tsdb v0.9.1 h1:IWaAmWkYlgG7/S4iw4IpAQt5Y35QaZM6/GsZ7GsjAuk= +github.com/prometheus/tsdb v0.9.1/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4= +github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs= +github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= +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 v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shirou/gopsutil v2.20.5+incompatible h1:tYH07UPoQt0OCQdgWWMgYHy3/a9bcxNpBIysykNIP7I= +github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +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/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +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/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw= +github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= +github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= +github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48 h1:ju5UTwk5Odtm4trrY+4Ca4RMj5OyXbmVeDAVad2T0Jw= +github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= +github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE= +github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= +github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 h1:njlZPzLwU639dk2kqnCPPv+wNjq7Xb6EfUxe/oX0/NM= +github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +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.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +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.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/goleveldb v0.0.0-20180621010148-0d5a0ceb10cf/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= +github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d h1:gZZadD8H+fF+n9CmNhYL1Y0dJB+kLOmKd7FbPJLeGHs= +github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= +github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca h1:Ld/zXl5t4+D69SiV4JoN7kkfvJdOWlPpfxrzxpLMoUk= +github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= +github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= +github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= +github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= +github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= +github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= +github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s= +github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U= +github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 h1:hqAk8riJvK4RMWx1aInLzndwxKalgi5rTqgfXxOxbEI= +github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= +github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= +github.com/tendermint/go-amino v0.15.1 h1:D2uk35eT4iTsvJd9jWIetzthE5C0/k2QmMFkCN+4JgQ= +github.com/tendermint/go-amino v0.15.1/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/tendermint/iavl v0.14.0 h1:Jkff+IFrXxRWtH9Jn/ga/2cxNnzMTv58xEKgCJsKUBg= +github.com/tendermint/iavl v0.14.0/go.mod h1:QmfViflFiXzxKLQE4tAUuWQHq+RSuQFxablW5oJZ6sE= +github.com/tendermint/tendermint v0.33.5/go.mod h1:0yUs9eIuuDq07nQql9BmI30FtYGcEC60Tu5JzB5IezM= +github.com/tendermint/tendermint v0.33.7 h1:b5CQD8ggDtl4u0EbXzabi0MaOw9NrcXker6ijEkAE74= +github.com/tendermint/tendermint v0.33.7/go.mod h1:0yUs9eIuuDq07nQql9BmI30FtYGcEC60Tu5JzB5IezM= +github.com/tendermint/tm-db v0.5.1 h1:H9HDq8UEA7Eeg13kdYckkgwwkQLBnJGgX4PgLJRhieY= +github.com/tendermint/tm-db v0.5.1/go.mod h1:g92zWjHpCYlEvQXvy9M168Su8V1IBEeawpXVVBaK4f4= +github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc= +github.com/tjfoc/gmsm v1.3.0/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tyler-smith/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= +github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4= +github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 h1:1cngl9mPEoITZG8s8cVcUy5CeIBYhEESkOB7m6Gmkrk= +github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xtaci/kcp-go v5.4.5+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= +github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= +github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= +github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8= +github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +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.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +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-20181029021203-45a5f77698d3/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-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +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-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +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/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/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 h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +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= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190125091013-d26f9f9a57f3/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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +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-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190912160710-24e19bdeb0f2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72/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-20200324143707-d3edc9973b7e/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-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +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/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 h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/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-20190403152447-81d4e9dc473e/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-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/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-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8 h1:AvbQYmiaaaza3cW3QXRyPo5kYgpFIzOAfeAAN7m3qQ4= +golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +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 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +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-20190328211700-ab21143f2384/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-20190912185636-87d9f09c5d89/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-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd h1:hHkvGJK23seRCflePJnVa9IMv8fsuavSCWKd11kDQFs= +golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +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/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/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-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +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-20200218151345-dad8c97a84f5 h1:jB9+PJSvu5tBfmJHy/OVapFdjDF3WvpkqRhxqrmzoEU= +google.golang.org/genproto v0.0.0-20200218151345-dad8c97a84f5/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/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.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +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 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/bsm/ratelimit.v1 v1.0.0-20160220154919-db14e161995a/go.mod h1:KF9sEfUPAXdG8Oev9e99iLGnl2uJMjc5B+4y3O7x610= +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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fatih/set.v0 v0.2.1/go.mod h1:5eLWEndGL4zGGemXWrKuts+wTJR0y+w+auqUJZbmyBg= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= +gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= +gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= +gopkg.in/jcmturner/gokrb5.v7 v7.2.3/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= +gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= +gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= +gopkg.in/karalabe/cookiejar.v2 v2.0.0-20150724131613-8dcd6a7f4951/go.mod h1:owOxCRGGeAx1uugABik6K9oeNu1cgxP/R9ItzLDxNWA= +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/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 h1:a6cXbcDDUkSBlpnkWV1bJ+vv3mOgQEltEJ2rPxroVu0= +gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= +gopkg.in/redis.v4 v4.2.4/go.mod h1:8KREHdypkCEojGKQcjMqAODMICIVwZAONWq8RowTITA= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +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/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +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= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/importer/importer_test.go b/importer/importer_test.go index f0e500df70..756a7e0505 100644 --- a/importer/importer_test.go +++ b/importer/importer_test.go @@ -13,29 +13,34 @@ import ( "testing" "time" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/stretchr/testify/require" + + sdkcodec "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" + sdkstore "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/ethermint/core" + emintcrypto "github.com/cosmos/ethermint/crypto" "github.com/cosmos/ethermint/types" + "github.com/cosmos/ethermint/x/evm" evmtypes "github.com/cosmos/ethermint/x/evm/types" ethcmn "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" - ethmisc "github.com/ethereum/go-ethereum/consensus/misc" ethcore "github.com/ethereum/go-ethereum/core" ethtypes "github.com/ethereum/go-ethereum/core/types" ethvm "github.com/ethereum/go-ethereum/core/vm" + ethcrypto "github.com/ethereum/go-ethereum/crypto" ethparams "github.com/ethereum/go-ethereum/params" ethrlp "github.com/ethereum/go-ethereum/rlp" - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - dbm "github.com/tendermint/tendermint/libs/db" tmlog "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" ) var ( @@ -43,15 +48,8 @@ var ( flagBlockchain string flagCPUProfile string - miner501 = ethcmn.HexToAddress("0x35e8e5dC5FBd97c5b421A80B596C030a2Be2A04D") genInvestor = ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0") - paramsKey = sdk.NewKVStoreKey("params") - tParamsKey = sdk.NewTransientStoreKey("transient_params") - accKey = sdk.NewKVStoreKey("acc") - storageKey = sdk.NewKVStoreKey("storage") - codeKey = sdk.NewKVStoreKey("code") - logger = tmlog.NewNopLogger() rewardBig8 = big.NewInt(8) @@ -62,17 +60,20 @@ func init() { flag.StringVar(&flagCPUProfile, "cpu-profile", "", "write CPU profile") flag.StringVar(&flagDataDir, "datadir", "", "test data directory for state storage") flag.StringVar(&flagBlockchain, "blockchain", "blockchain", "ethereum block export file (blocks to import)") + testing.Init() flag.Parse() } -func newTestCodec() *codec.Codec { - cdc := codec.New() +func newTestCodec() *sdkcodec.Codec { + cdc := sdkcodec.New() evmtypes.RegisterCodec(cdc) types.RegisterCodec(cdc) auth.RegisterCodec(cdc) + bank.RegisterCodec(cdc) sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) + emintcrypto.RegisterCodec(cdc) + sdkcodec.RegisterCrypto(cdc) return cdc } @@ -97,13 +98,14 @@ func trapSignals() { }() } -func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak auth.AccountKeeper) { +// nolint: interfacer +func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak auth.AccountKeeper, evmKeeper evm.Keeper) { genBlock := ethcore.DefaultGenesisBlock() ms := cms.CacheMultiStore() ctx := sdk.NewContext(ms, abci.Header{}, false, logger) - stateDB, err := evmtypes.NewCommitStateDB(ctx, ak, storageKey, codeKey) - require.NoError(t, err, "failed to create a StateDB instance") + // Set the default Ethermint parameters to the parameter keeper store + evmKeeper.SetParams(ctx, evmtypes.DefaultParams()) // sort the addresses and insertion of key/value pairs matters genAddrs := make([]string, len(genBlock.Alloc)) @@ -119,23 +121,23 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak auth.Accoun addr := ethcmn.HexToAddress(addrStr) acc := genBlock.Alloc[addr] - stateDB.AddBalance(addr, acc.Balance) - stateDB.SetCode(addr, acc.Code) - stateDB.SetNonce(addr, acc.Nonce) + evmKeeper.AddBalance(ctx, addr, acc.Balance) + evmKeeper.SetCode(ctx, addr, acc.Code) + evmKeeper.SetNonce(ctx, addr, acc.Nonce) for key, value := range acc.Storage { - stateDB.SetState(addr, key, value) + evmKeeper.SetState(ctx, addr, key, value) } } - // get balance of one of the genesis account having 200 ETH - b := stateDB.GetBalance(genInvestor) + // get balance of one of the genesis account having 400 ETH + b := evmKeeper.GetBalance(ctx, genInvestor) require.Equal(t, "200000000000000000000", b.String()) // commit the stateDB with 'false' to delete empty objects // // NOTE: Commit does not yet return the intra merkle root (version) - _, err = stateDB.Commit(false) + _, err := evmKeeper.Commit(ctx, false) require.NoError(t, err) // persist multi-store cache state @@ -147,7 +149,10 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak auth.Accoun // verify account mapper state genAcc := ak.GetAccount(ctx, sdk.AccAddress(genInvestor.Bytes())) require.NotNil(t, genAcc) - require.Equal(t, sdk.NewIntFromBigInt(b), genAcc.GetCoins().AmountOf(types.DenomDefault)) + + evmDenom := evmKeeper.GetParams(ctx).EvmDenom + balance := sdk.NewCoin(evmDenom, genAcc.GetCoins().AmountOf(evmDenom)) + require.Equal(t, sdk.NewIntFromBigInt(b), balance.Amount) } func TestImportBlocks(t *testing.T) { @@ -163,35 +168,52 @@ func TestImportBlocks(t *testing.T) { require.NoError(t, err, "failed to start CPU profile") } - db := dbm.NewDB("state", dbm.LevelDBBackend, flagDataDir) + db := dbm.NewDB("state", dbm.GoLevelDBBackend, flagDataDir) defer cleanup() trapSignals() - // create logger, codec and root multi-store cdc := newTestCodec() + cms := store.NewCommitMultiStore(db) - ak := auth.NewAccountKeeper(cdc, accKey, types.ProtoBaseAccount) + + authStoreKey := sdk.NewKVStoreKey(auth.StoreKey) + evmStoreKey := sdk.NewKVStoreKey(evmtypes.StoreKey) + paramsStoreKey := sdk.NewKVStoreKey(params.StoreKey) + paramsTransientStoreKey := sdk.NewTransientStoreKey(params.TStoreKey) // mount stores - keys := []*sdk.KVStoreKey{accKey, storageKey, codeKey} + keys := []*sdk.KVStoreKey{authStoreKey, evmStoreKey, paramsStoreKey} for _, key := range keys { cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, nil) } - cms.SetPruning(sdk.PruneNothing) + cms.MountStoreWithDB(paramsTransientStoreKey, sdk.StoreTypeTransient, nil) + + paramsKeeper := params.NewKeeper(cdc, paramsStoreKey, paramsTransientStoreKey) + + // Set specific subspaces + authSubspace := paramsKeeper.Subspace(auth.DefaultParamspace) + evmSubspace := paramsKeeper.Subspace(evmtypes.DefaultParamspace).WithKeyTable(evmtypes.ParamKeyTable()) + ak := auth.NewAccountKeeper(cdc, authStoreKey, authSubspace, types.ProtoAccount) + evmKeeper := evm.NewKeeper(cdc, evmStoreKey, evmSubspace, ak) + + cms.SetPruning(sdkstore.PruneNothing) // load latest version (root) err := cms.LoadLatestVersion() require.NoError(t, err) // set and test genesis block - createAndTestGenesis(t, cms, ak) + createAndTestGenesis(t, cms, ak, evmKeeper) // open blockchain export file blockchainInput, err := os.Open(flagBlockchain) require.Nil(t, err) - defer blockchainInput.Close() + defer func() { + err := blockchainInput.Close() + require.NoError(t, err) + }() // ethereum mainnet config chainContext := core.NewChainContext() @@ -227,26 +249,25 @@ func TestImportBlocks(t *testing.T) { ctx := sdk.NewContext(ms, abci.Header{}, false, logger) ctx = ctx.WithBlockHeight(int64(block.NumberU64())) - stateDB := createStateDB(t, ctx, ak) - if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0 { - ethmisc.ApplyDAOHardFork(stateDB) + applyDAOHardFork(evmKeeper) } for i, tx := range block.Transactions() { - stateDB.Prepare(tx.Hash(), block.Hash(), i) + evmKeeper.Prepare(ctx, tx.Hash(), block.Hash(), i) - _, _, err = ethcore.ApplyTransaction( - chainConfig, chainContext, nil, gp, stateDB, header, tx, usedGas, vmConfig, + receipt, gas, err := applyTransaction( + chainConfig, chainContext, nil, gp, evmKeeper, header, tx, usedGas, vmConfig, ) - require.NoError(t, err, "failed to apply tx at block %d; tx: %X", block.NumberU64(), tx.Hash()) + require.NoError(t, err, "failed to apply tx at block %d; tx: %X; gas %d; receipt:%v", block.NumberU64(), tx.Hash(), gas, receipt) + require.NotNil(t, receipt) } // apply mining rewards - accumulateRewards(chainConfig, stateDB, header, block.Uncles()) + accumulateRewards(chainConfig, evmKeeper, header, block.Uncles()) // commit stateDB - _, err := stateDB.Commit(chainConfig.IsEIP158(block.Number())) + _, err := evmKeeper.CommitStateDB.Commit(chainConfig.IsEIP158(block.Number())) require.NoError(t, err, "failed to commit StateDB") // simulate BaseApp EndBlocker commitment @@ -260,18 +281,11 @@ func TestImportBlocks(t *testing.T) { } } -func createStateDB(t *testing.T, ctx sdk.Context, ak auth.AccountKeeper) *evmtypes.CommitStateDB { - stateDB, err := evmtypes.NewCommitStateDB(ctx, ak, storageKey, codeKey) - require.NoError(t, err, "failed to create a StateDB instance") - - return stateDB -} - // accumulateRewards credits the coinbase of the given block with the mining // reward. The total reward consists of the static block reward and rewards for // included uncles. The coinbase of each uncle block is also rewarded. func accumulateRewards( - config *ethparams.ChainConfig, stateDB *evmtypes.CommitStateDB, + config *ethparams.ChainConfig, evmKeeper evm.Keeper, header *ethtypes.Header, uncles []*ethtypes.Header, ) { @@ -290,10 +304,95 @@ func accumulateRewards( r.Sub(r, header.Number) r.Mul(r, blockReward) r.Div(r, rewardBig8) - stateDB.AddBalance(uncle.Coinbase, r) + evmKeeper.CommitStateDB.AddBalance(uncle.Coinbase, r) r.Div(blockReward, rewardBig32) reward.Add(reward, r) } - stateDB.AddBalance(header.Coinbase, reward) + evmKeeper.CommitStateDB.AddBalance(header.Coinbase, reward) +} + +// ApplyDAOHardFork modifies the state database according to the DAO hard-fork +// rules, transferring all balances of a set of DAO accounts to a single refund +// contract. +// Code is pulled from go-ethereum 1.9 because the StateDB interface does not include the +// SetBalance function implementation +// Ref: https://github.com/ethereum/go-ethereum/blob/52f2461774bcb8cdd310f86b4bc501df5b783852/consensus/misc/dao.go#L74 +func applyDAOHardFork(evmKeeper evm.Keeper) { + // Retrieve the contract to refund balances into + if !evmKeeper.CommitStateDB.Exist(ethparams.DAORefundContract) { + evmKeeper.CommitStateDB.CreateAccount(ethparams.DAORefundContract) + } + + // Move every DAO account and extra-balance account funds into the refund contract + for _, addr := range ethparams.DAODrainList() { + evmKeeper.CommitStateDB.AddBalance(ethparams.DAORefundContract, evmKeeper.CommitStateDB.GetBalance(addr)) + evmKeeper.CommitStateDB.SetBalance(addr, new(big.Int)) + } +} + +// ApplyTransaction attempts to apply a transaction to the given state database +// and uses the input parameters for its environment. It returns the receipt +// for the transaction, gas used and an error if the transaction failed, +// indicating the block was invalid. +// Function is also pulled from go-ethereum 1.9 because of the incompatible usage +// Ref: https://github.com/ethereum/go-ethereum/blob/52f2461774bcb8cdd310f86b4bc501df5b783852/core/state_processor.go#L88 +func applyTransaction( + config *ethparams.ChainConfig, bc ethcore.ChainContext, author *ethcmn.Address, + gp *ethcore.GasPool, evmKeeper evm.Keeper, header *ethtypes.Header, + tx *ethtypes.Transaction, usedGas *uint64, cfg ethvm.Config, +) (*ethtypes.Receipt, uint64, error) { + msg, err := tx.AsMessage(ethtypes.MakeSigner(config, header.Number)) + if err != nil { + return nil, 0, err + } + + // Create a new context to be used in the EVM environment + context := ethcore.NewEVMContext(msg, header, bc, author) + + // Create a new environment which holds all relevant information + // about the transaction and calling mechanisms. + vmenv := ethvm.NewEVM(context, evmKeeper.CommitStateDB, config, cfg) + + // Apply the transaction to the current state (included in the env) + execResult, err := ethcore.ApplyMessage(vmenv, msg, gp) + if err != nil { + // NOTE: ignore vm execution error (eg: tx out of gas at block 51169) as we care only about state transition errors + return ðtypes.Receipt{}, 0, nil + } + + // Update the state with pending changes + var intRoot ethcmn.Hash + if config.IsByzantium(header.Number) { + err = evmKeeper.CommitStateDB.Finalise(true) + } else { + intRoot, err = evmKeeper.CommitStateDB.IntermediateRoot(config.IsEIP158(header.Number)) + } + + if err != nil { + return nil, execResult.UsedGas, err + } + + root := intRoot.Bytes() + *usedGas += execResult.UsedGas + + // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx + // based on the eip phase, we're passing whether the root touch-delete accounts. + receipt := ethtypes.NewReceipt(root, execResult.Failed(), *usedGas) + receipt.TxHash = tx.Hash() + receipt.GasUsed = execResult.UsedGas + + // if the transaction created a contract, store the creation address in the receipt. + if msg.To() == nil { + receipt.ContractAddress = ethcrypto.CreateAddress(vmenv.Context.Origin, tx.Nonce()) + } + + // Set the receipt logs and create a bloom for filtering + receipt.Logs, err = evmKeeper.CommitStateDB.GetLogs(tx.Hash()) + receipt.Bloom = ethtypes.CreateBloom(ethtypes.Receipts{receipt}) + receipt.BlockHash = evmKeeper.CommitStateDB.BlockHash() + receipt.BlockNumber = header.Number + receipt.TransactionIndex = uint(evmKeeper.CommitStateDB.TxIndex()) + + return receipt, execResult.UsedGas, err } diff --git a/init.sh b/init.sh new file mode 100755 index 0000000000..17223d4f70 --- /dev/null +++ b/init.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +KEY="mykey" +CHAINID="ethermint-1" +MONIKER="localtestnet" + +# remove existing daemon and client +rm -rf ~/.ethermint* + +make install + +ethermintcli config keyring-backend test + +# Set up config for CLI +ethermintcli config chain-id $CHAINID +ethermintcli config output json +ethermintcli config indent true +ethermintcli config trust-node true + +# if $KEY exists it should be deleted +ethermintcli keys add $KEY + +# Set moniker and chain-id for Ethermint (Moniker can be anything, chain-id must be an integer) +ethermintd init $MONIKER --chain-id $CHAINID + +# Change parameter token denominations to aphoton +cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["staking"]["params"]["bond_denom"]="aphoton"' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json +cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["crisis"]["constant_fee"]["denom"]="aphoton"' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json +cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["gov"]["deposit_params"]["min_deposit"][0]["denom"]="aphoton"' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json +cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["mint"]["params"]["mint_denom"]="aphoton"' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json + +# Enable faucet +cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["faucet"]["enable_faucet"]=true' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json + +# Allocate genesis accounts (cosmos formatted addresses) +ethermintd add-genesis-account $(ethermintcli keys show $KEY -a) 100000000000000000000aphoton + +# Sign genesis transaction +ethermintd gentx --name $KEY --amount=1000000000000000000aphoton --keyring-backend test + +# Collect genesis tx +ethermintd collect-gentxs + +echo -e '\n\ntestnet faucet enabled' +echo -e 'to transfer tokens to your account address use:' +echo -e "ethermintcli tx faucet request 100aphoton --from $KEY\n" + + +# Run this to ensure everything worked and that the genesis file is setup correctly +ethermintd validate-genesis + +# Command to run the rest server in a different terminal/window +echo -e '\nrun the following command in a different terminal/window to run the REST server and JSON-RPC:' +echo -e "ethermintcli rest-server --laddr \"tcp://localhost:8545\" --unlock-key $KEY --chain-id $CHAINID --trace\n" + +# Start the node (remove the --pruning=nothing flag if historical queries are not needed) +ethermintd start --pruning=nothing --rpc.unsafe --log_level "main:info,state:info,mempool:info" --trace diff --git a/networks/local/Makefile b/networks/local/Makefile new file mode 100644 index 0000000000..0b117de9e1 --- /dev/null +++ b/networks/local/Makefile @@ -0,0 +1,4 @@ +all: + docker build --tag ethermintd/node ethermintnode + +.PHONY: all diff --git a/networks/local/ethermintnode/Dockerfile b/networks/local/ethermintnode/Dockerfile new file mode 100644 index 0000000000..a52e793848 --- /dev/null +++ b/networks/local/ethermintnode/Dockerfile @@ -0,0 +1,32 @@ +FROM golang:stretch as build-env + +# Install minimum necessary dependencies +ENV PACKAGES curl make git libc-dev bash gcc +RUN apt-get update && apt-get upgrade -y && \ + apt-get install -y $PACKAGES + +# Set working directory for the build +WORKDIR /go/src/github.com/ChainSafe/ethermint + +# Add source files +COPY . . + +# build Ethermint +RUN make build-ethermint-linux + +# Final image +FROM golang:1.14 as final + +WORKDIR / + +RUN apt-get update + +# Copy over binaries from the build-env +COPY --from=build-env /go/src/github.com/ChainSafe/ethermint/build/ethermintd /usr/bin/ethermintd +COPY --from=build-env /go/src/github.com/ChainSafe/ethermint/build/ethermintcli /usr/bin/ethermintcli +COPY --from=build-env /go/src/github.com/ChainSafe/ethermint/scripts/start.sh / + +EXPOSE 26656 26657 1317 8545 8546 + +# Run ethermintd by default, omit entrypoint to ease using container with ethermintcli +ENTRYPOINT ["/bin/bash", "-c"] \ No newline at end of file diff --git a/rpc/addrlock.go b/rpc/addrlock.go new file mode 100644 index 0000000000..51f6b9c334 --- /dev/null +++ b/rpc/addrlock.go @@ -0,0 +1,38 @@ +package rpc + +import ( + "sync" + + "github.com/ethereum/go-ethereum/common" +) + +// AddrLocker is a mutex structure used to avoid querying outdated account data +type AddrLocker struct { + mu sync.Mutex + locks map[common.Address]*sync.Mutex +} + +// lock returns the lock of the given address. +func (l *AddrLocker) lock(address common.Address) *sync.Mutex { + l.mu.Lock() + defer l.mu.Unlock() + if l.locks == nil { + l.locks = make(map[common.Address]*sync.Mutex) + } + if _, ok := l.locks[address]; !ok { + l.locks[address] = new(sync.Mutex) + } + return l.locks[address] +} + +// LockAddr locks an account's mutex. This is used to prevent another tx getting the +// same nonce until the lock is released. The mutex prevents the (an identical nonce) from +// being read again during the time that the first transaction is being signed. +func (l *AddrLocker) LockAddr(address common.Address) { + l.lock(address).Lock() +} + +// UnlockAddr unlocks the mutex of the given account. +func (l *AddrLocker) UnlockAddr(address common.Address) { + l.lock(address).Unlock() +} diff --git a/rpc/apis.go b/rpc/apis.go new file mode 100644 index 0000000000..eeeaf0d45b --- /dev/null +++ b/rpc/apis.go @@ -0,0 +1,59 @@ +// Package rpc contains RPC handler methods and utilities to start +// Ethermint's Web3-compatibly JSON-RPC server. +package rpc + +import ( + "github.com/cosmos/cosmos-sdk/client/context" + emintcrypto "github.com/cosmos/ethermint/crypto" + "github.com/ethereum/go-ethereum/rpc" +) + +// RPC namespaces and API version +const ( + Web3Namespace = "web3" + EthNamespace = "eth" + PersonalNamespace = "personal" + NetNamespace = "net" + + apiVersion = "1.0" +) + +// GetRPCAPIs returns the list of all APIs +func GetRPCAPIs(cliCtx context.CLIContext, keys []emintcrypto.PrivKeySecp256k1) []rpc.API { + nonceLock := new(AddrLocker) + backend := NewEthermintBackend(cliCtx) + ethAPI := NewPublicEthAPI(cliCtx, backend, nonceLock, keys) + + return []rpc.API{ + { + Namespace: Web3Namespace, + Version: apiVersion, + Service: NewPublicWeb3API(), + Public: true, + }, + { + Namespace: EthNamespace, + Version: apiVersion, + Service: ethAPI, + Public: true, + }, + { + Namespace: PersonalNamespace, + Version: apiVersion, + Service: NewPersonalEthAPI(ethAPI), + Public: false, + }, + { + Namespace: EthNamespace, + Version: apiVersion, + Service: NewPublicFilterAPI(cliCtx, backend), + Public: true, + }, + { + Namespace: NetNamespace, + Version: apiVersion, + Service: NewPublicNetAPI(cliCtx), + Public: true, + }, + } +} diff --git a/rpc/args/send_tx.go b/rpc/args/send_tx.go new file mode 100644 index 0000000000..7515dbb565 --- /dev/null +++ b/rpc/args/send_tx.go @@ -0,0 +1,22 @@ +package args + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// SendTxArgs represents the arguments to submit a new transaction into the transaction pool. +// Duplicate struct definition since geth struct is in internal package +// Ref: https://github.com/ethereum/go-ethereum/blob/release/1.9/internal/ethapi/api.go#L1346 +type SendTxArgs struct { + From common.Address `json:"from"` + To *common.Address `json:"to"` + Gas *hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + Value *hexutil.Big `json:"value"` + Nonce *hexutil.Uint64 `json:"nonce"` + // We accept "data" and "input" for backwards-compatibility reasons. "input" is the + // newer name and should be preferred by clients. + Data *hexutil.Bytes `json:"data"` + Input *hexutil.Bytes `json:"input"` +} diff --git a/rpc/backend.go b/rpc/backend.go new file mode 100644 index 0000000000..1c5800a1e8 --- /dev/null +++ b/rpc/backend.go @@ -0,0 +1,328 @@ +package rpc + +import ( + "fmt" + "math/big" + "os" + "strconv" + + "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" + + evmtypes "github.com/cosmos/ethermint/x/evm/types" + + "github.com/cosmos/cosmos-sdk/client/context" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" +) + +// Backend implements the functionality needed to filter changes. +// Implemented by EthermintBackend. +type Backend interface { + // Used by block filter; also used for polling + BlockNumber() (hexutil.Uint64, error) + HeaderByNumber(blockNum BlockNumber) (*ethtypes.Header, error) + HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error) + GetBlockByNumber(blockNum BlockNumber, fullTx bool) (map[string]interface{}, error) + GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) + getEthBlockByNumber(height int64, fullTx bool) (map[string]interface{}, error) + getGasLimit() (int64, error) + // returns the logs of a given block + GetLogs(blockHash common.Hash) ([][]*ethtypes.Log, error) + + // Used by pending transaction filter + PendingTransactions() ([]*Transaction, error) + + // Used by log filter + GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, error) + BloomStatus() (uint64, uint64) +} + +var _ Backend = (*EthermintBackend)(nil) + +// EthermintBackend implements the Backend interface +type EthermintBackend struct { + cliCtx context.CLIContext + logger log.Logger + gasLimit int64 +} + +// NewEthermintBackend creates a new EthermintBackend instance +func NewEthermintBackend(cliCtx context.CLIContext) *EthermintBackend { + return &EthermintBackend{ + cliCtx: cliCtx, + logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "json-rpc"), + gasLimit: int64(^uint32(0)), + } +} + +// BlockNumber returns the current block number. +func (e *EthermintBackend) BlockNumber() (hexutil.Uint64, error) { + res, height, err := e.cliCtx.QueryWithData(fmt.Sprintf("custom/%s/blockNumber", evmtypes.ModuleName), nil) + if err != nil { + return hexutil.Uint64(0), err + } + + var out evmtypes.QueryResBlockNumber + e.cliCtx.Codec.MustUnmarshalJSON(res, &out) + + e.cliCtx.WithHeight(height) + return hexutil.Uint64(out.Number), nil +} + +// GetBlockByNumber returns the block identified by number. +func (e *EthermintBackend) GetBlockByNumber(blockNum BlockNumber, fullTx bool) (map[string]interface{}, error) { + value := blockNum.Int64() + return e.getEthBlockByNumber(value, fullTx) +} + +// GetBlockByHash returns the block identified by hash. +func (e *EthermintBackend) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) { + res, height, err := e.cliCtx.Query(fmt.Sprintf("custom/%s/%s/%s", evmtypes.ModuleName, evmtypes.QueryHashToHeight, hash.Hex())) + if err != nil { + return nil, err + } + + var out evmtypes.QueryResBlockNumber + if err := e.cliCtx.Codec.UnmarshalJSON(res, &out); err != nil { + return nil, err + } + + e.cliCtx = e.cliCtx.WithHeight(height) + return e.getEthBlockByNumber(out.Number, fullTx) +} + +// HeaderByNumber returns the block header identified by height. +func (e *EthermintBackend) HeaderByNumber(blockNum BlockNumber) (*ethtypes.Header, error) { + return e.getBlockHeader(blockNum.Int64()) +} + +// HeaderByHash returns the block header identified by hash. +func (e *EthermintBackend) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error) { + res, height, err := e.cliCtx.Query(fmt.Sprintf("custom/%s/%s/%s", evmtypes.ModuleName, evmtypes.QueryHashToHeight, blockHash.Hex())) + if err != nil { + return nil, err + } + var out evmtypes.QueryResBlockNumber + if err := e.cliCtx.Codec.UnmarshalJSON(res, &out); err != nil { + return nil, err + } + + e.cliCtx = e.cliCtx.WithHeight(height) + return e.getBlockHeader(out.Number) +} + +func (e *EthermintBackend) getBlockHeader(height int64) (*ethtypes.Header, error) { + if height <= 0 { + // get latest block height + num, err := e.BlockNumber() + if err != nil { + return nil, err + } + + height = int64(num) + } + + block, err := e.cliCtx.Client.Block(&height) + if err != nil { + return nil, err + } + + res, _, err := e.cliCtx.Query(fmt.Sprintf("custom/%s/%s/%s", evmtypes.ModuleName, evmtypes.QueryBloom, strconv.FormatInt(height, 10))) + if err != nil { + return nil, err + } + + var bloomRes evmtypes.QueryBloomFilter + e.cliCtx.Codec.MustUnmarshalJSON(res, &bloomRes) + + ethHeader := EthHeaderFromTendermint(block.Block.Header) + ethHeader.Bloom = bloomRes.Bloom + + return ethHeader, nil +} + +func (e *EthermintBackend) getEthBlockByNumber(height int64, fullTx bool) (map[string]interface{}, error) { + // Remove this check when 0 query is fixed ref: (https://github.com/tendermint/tendermint/issues/4014) + var blkNumPtr *int64 + if height != 0 { + blkNumPtr = &height + } + + block, err := e.cliCtx.Client.Block(blkNumPtr) + if err != nil { + return nil, err + } + header := block.Block.Header + + gasLimit, err := e.getGasLimit() + if err != nil { + return nil, err + } + + var ( + gasUsed *big.Int + transactions []common.Hash + ) + + if fullTx { + // Populate full transaction data + transactions, gasUsed, err = convertTransactionsToRPC( + e.cliCtx, block.Block.Txs, common.BytesToHash(header.Hash()), uint64(header.Height), + ) + if err != nil { + return nil, err + } + } else { + // TODO: Gas used not saved and cannot be calculated by hashes + // Return slice of transaction hashes + transactions = make([]common.Hash, len(block.Block.Txs)) + for i, tx := range block.Block.Txs { + transactions[i] = common.BytesToHash(tx.Hash()) + } + } + + res, _, err := e.cliCtx.Query(fmt.Sprintf("custom/%s/%s/%s", evmtypes.ModuleName, evmtypes.QueryBloom, strconv.FormatInt(block.Block.Height, 10))) + if err != nil { + return nil, err + } + + var out evmtypes.QueryBloomFilter + e.cliCtx.Codec.MustUnmarshalJSON(res, &out) + return formatBlock(header, block.Block.Size(), gasLimit, gasUsed, transactions, out.Bloom), nil +} + +// getGasLimit returns the gas limit per block set in genesis +func (e *EthermintBackend) getGasLimit() (int64, error) { + // Retrieve from gasLimit variable cache + if e.gasLimit != -1 { + return e.gasLimit, nil + } + + // Query genesis block if hasn't been retrieved yet + genesis, err := e.cliCtx.Client.Genesis() + if err != nil { + return 0, err + } + + // Save value to gasLimit cached value + gasLimit := genesis.Genesis.ConsensusParams.Block.MaxGas + if gasLimit == -1 { + // Sets gas limit to max uint32 to not error with javascript dev tooling + // This -1 value indicating no block gas limit is set to max uint64 with geth hexutils + // which errors certain javascript dev tooling which only supports up to 53 bits + gasLimit = int64(^uint32(0)) + } + e.gasLimit = gasLimit + return gasLimit, nil +} + +// GetTransactionLogs returns the logs given a transaction hash. +// It returns an error if there's an encoding error. +// If no logs are found for the tx hash, the error is nil. +func (e *EthermintBackend) GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, error) { + ctx := e.cliCtx + + res, height, err := ctx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", evmtypes.ModuleName, evmtypes.QueryTransactionLogs, txHash.Hex()), nil) + if err != nil { + return nil, err + } + + out := new(evmtypes.QueryETHLogs) + if err := e.cliCtx.Codec.UnmarshalJSON(res, &out); err != nil { + return nil, err + } + + e.cliCtx = e.cliCtx.WithHeight(height) + return out.Logs, nil +} + +// PendingTransactions returns the transactions that are in the transaction pool +// and have a from address that is one of the accounts this node manages. +func (e *EthermintBackend) PendingTransactions() ([]*Transaction, error) { + pendingTxs, err := e.cliCtx.Client.UnconfirmedTxs(100) + if err != nil { + return nil, err + } + + transactions := make([]*Transaction, pendingTxs.Count) + for _, tx := range pendingTxs.Txs { + ethTx, err := bytesToEthTx(e.cliCtx, tx) + if err != nil { + return nil, err + } + + // * Should check signer and reference against accounts the node manages in future + rpcTx, err := newRPCTransaction(*ethTx, common.BytesToHash(tx.Hash()), common.Hash{}, nil, 0) + if err != nil { + return nil, err + } + + transactions = append(transactions, rpcTx) + } + + return transactions, nil +} + +// GetLogs returns all the logs from all the ethreum transactions in a block. +func (e *EthermintBackend) GetLogs(blockHash common.Hash) ([][]*ethtypes.Log, error) { + res, _, err := e.cliCtx.Query(fmt.Sprintf("custom/%s/%s/%s", evmtypes.ModuleName, evmtypes.QueryHashToHeight, blockHash.Hex())) + if err != nil { + return nil, err + } + + var out evmtypes.QueryResBlockNumber + if err := e.cliCtx.Codec.UnmarshalJSON(res, &out); err != nil { + return nil, err + } + + block, err := e.cliCtx.Client.Block(&out.Number) + if err != nil { + return nil, err + } + + var blockLogs = [][]*ethtypes.Log{} + for _, tx := range block.Block.Txs { + // NOTE: we query the state in case the tx result logs are not persisted after an upgrade. + res, _, err := e.cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", evmtypes.ModuleName, evmtypes.QueryTransactionLogs, common.BytesToHash(tx.Hash()).Hex()), nil) + if err != nil { + continue + } + + out := new(evmtypes.QueryETHLogs) + if err := e.cliCtx.Codec.UnmarshalJSON(res, &out); err != nil { + return nil, err + } + + blockLogs = append(blockLogs, out.Logs) + } + + return blockLogs, nil +} + +// BloomStatus returns the BloomBitsBlocks and the number of processed sections maintained +// by the chain indexer. +func (e *EthermintBackend) BloomStatus() (uint64, uint64) { + return 4096, 0 +} + +// EthHeaderFromTendermint is an util function that returns an Ethereum Header +// from a tendermint Header. +func EthHeaderFromTendermint(header tmtypes.Header) *ethtypes.Header { + return ðtypes.Header{ + ParentHash: common.BytesToHash(header.LastBlockID.Hash.Bytes()), + UncleHash: common.Hash{}, + Coinbase: common.Address{}, + Root: common.BytesToHash(header.AppHash), + TxHash: common.BytesToHash(header.DataHash), + ReceiptHash: common.Hash{}, + Difficulty: nil, + Number: big.NewInt(header.Height), + Time: uint64(header.Time.Unix()), + Extra: nil, + MixDigest: common.Hash{}, + Nonce: ethtypes.BlockNonce{}, + } +} diff --git a/rpc/config.go b/rpc/config.go new file mode 100644 index 0000000000..6142a2abe5 --- /dev/null +++ b/rpc/config.go @@ -0,0 +1,136 @@ +package rpc + +import ( + "bufio" + "fmt" + "os" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/input" + "github.com/cosmos/cosmos-sdk/client/lcd" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys" + sdk "github.com/cosmos/cosmos-sdk/types" + authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + + "github.com/cosmos/ethermint/app" + "github.com/cosmos/ethermint/crypto" + "github.com/ethereum/go-ethereum/rpc" +) + +const ( + flagUnlockKey = "unlock-key" + flagWebsocket = "wsport" +) + +// EmintServeCmd creates a CLI command to start Cosmos REST server with web3 RPC API and +// Cosmos rest-server endpoints +func EmintServeCmd(cdc *codec.Codec) *cobra.Command { + cmd := lcd.ServeCommand(cdc, registerRoutes) + cmd.Flags().String(flagUnlockKey, "", "Select a key to unlock on the RPC server") + cmd.Flags().String(flagWebsocket, "8546", "websocket port to listen to") + cmd.Flags().StringP(flags.FlagBroadcastMode, "b", flags.BroadcastSync, "Transaction broadcasting mode (sync|async|block)") + return cmd +} + +// registerRoutes creates a new server and registers the `/rpc` endpoint. +// Rpc calls are enabled based on their associated module (eg. "eth"). +func registerRoutes(rs *lcd.RestServer) { + s := rpc.NewServer() + accountName := viper.GetString(flagUnlockKey) + accountNames := strings.Split(accountName, ",") + + var privkeys []crypto.PrivKeySecp256k1 + if len(accountName) > 0 { + var err error + inBuf := bufio.NewReader(os.Stdin) + + keyringBackend := viper.GetString(flags.FlagKeyringBackend) + passphrase := "" + switch keyringBackend { + case keys.BackendOS: + break + case keys.BackendFile: + passphrase, err = input.GetPassword( + "Enter password to unlock key for RPC API: ", + inBuf) + if err != nil { + panic(err) + } + } + + privkeys, err = unlockKeyFromNameAndPassphrase(accountNames, passphrase) + if err != nil { + panic(err) + } + } + + apis := GetRPCAPIs(rs.CliCtx, privkeys) + + // TODO: Allow cli to configure modules https://github.com/ChainSafe/ethermint/issues/74 + whitelist := make(map[string]bool) + + // Register all the APIs exposed by the services + for _, api := range apis { + if whitelist[api.Namespace] || (len(whitelist) == 0 && api.Public) { + if err := s.RegisterName(api.Namespace, api.Service); err != nil { + panic(err) + } + } else if !api.Public { // TODO: how to handle private apis? should only accept local calls + if err := s.RegisterName(api.Namespace, api.Service); err != nil { + panic(err) + } + } + } + + // Web3 RPC API route + rs.Mux.HandleFunc("/", s.ServeHTTP).Methods("POST", "OPTIONS") + + // Register all other Cosmos routes + client.RegisterRoutes(rs.CliCtx, rs.Mux) + authrest.RegisterTxRoutes(rs.CliCtx, rs.Mux) + app.ModuleBasics.RegisterRESTRoutes(rs.CliCtx, rs.Mux) + + // start websockets server + websocketAddr := viper.GetString(flagWebsocket) + ws := newWebsocketsServer(rs.CliCtx, websocketAddr) + ws.start() +} + +func unlockKeyFromNameAndPassphrase(accountNames []string, passphrase string) ([]crypto.PrivKeySecp256k1, error) { + keybase, err := keys.NewKeyring( + sdk.KeyringServiceName(), + viper.GetString(flags.FlagKeyringBackend), + viper.GetString(flags.FlagHome), + os.Stdin, + crypto.EthSecp256k1Options()..., + ) + if err != nil { + return []crypto.PrivKeySecp256k1{}, err + } + + // try the for loop with array []string accountNames + // run through the bottom code inside the for loop + + keys := make([]crypto.PrivKeySecp256k1, len(accountNames)) + for i, acc := range accountNames { + // With keyring keybase, password is not required as it is pulled from the OS prompt + privKey, err := keybase.ExportPrivateKeyObject(acc, passphrase) + if err != nil { + return []crypto.PrivKeySecp256k1{}, err + } + + var ok bool + keys[i], ok = privKey.(crypto.PrivKeySecp256k1) + if !ok { + panic(fmt.Sprintf("invalid private key type %T at index %d", privKey, i)) + } + } + + return keys, nil +} diff --git a/rpc/eth_api.go b/rpc/eth_api.go new file mode 100644 index 0000000000..059dfa8461 --- /dev/null +++ b/rpc/eth_api.go @@ -0,0 +1,1069 @@ +package rpc + +import ( + "bytes" + "errors" + "fmt" + "math/big" + "os" + "sync" + + "github.com/spf13/viper" + + "github.com/cosmos/ethermint/crypto" + params "github.com/cosmos/ethermint/rpc/args" + ethermint "github.com/cosmos/ethermint/types" + "github.com/cosmos/ethermint/utils" + "github.com/cosmos/ethermint/version" + evmtypes "github.com/cosmos/ethermint/x/evm/types" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/merkle" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/rpc/client" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keys" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// PublicEthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec. +type PublicEthAPI struct { + cliCtx context.CLIContext + chainIDEpoch *big.Int + logger log.Logger + backend Backend + keys []crypto.PrivKeySecp256k1 // unlocked keys + nonceLock *AddrLocker + keybaseLock sync.Mutex +} + +// NewPublicEthAPI creates an instance of the public ETH Web3 API. +func NewPublicEthAPI(cliCtx context.CLIContext, backend Backend, nonceLock *AddrLocker, + key []crypto.PrivKeySecp256k1) *PublicEthAPI { + + epoch, err := ethermint.ParseChainID(cliCtx.ChainID) + if err != nil { + panic(err) + } + + api := &PublicEthAPI{ + cliCtx: cliCtx, + chainIDEpoch: epoch, + logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "json-rpc"), + backend: backend, + keys: key, + nonceLock: nonceLock, + } + + if err := api.getKeybaseInfo(); err != nil { + api.logger.Error("failed to get keybase info", "error", err) + } + + return api +} + +func (e *PublicEthAPI) getKeybaseInfo() error { + e.keybaseLock.Lock() + defer e.keybaseLock.Unlock() + + if e.cliCtx.Keybase == nil { + keybase, err := keys.NewKeyring( + sdk.KeyringServiceName(), + viper.GetString(flags.FlagKeyringBackend), + viper.GetString(flags.FlagHome), + e.cliCtx.Input, + crypto.EthSecp256k1Options()..., + ) + if err != nil { + return err + } + + e.cliCtx.Keybase = keybase + } + + return nil +} + +// ProtocolVersion returns the supported Ethereum protocol version. +func (e *PublicEthAPI) ProtocolVersion() hexutil.Uint { + e.logger.Debug("eth_protocolVersion") + return hexutil.Uint(version.ProtocolVersion) +} + +// ChainId returns the chain's identifier in hex format +func (e *PublicEthAPI) ChainId() (hexutil.Uint, error) { // nolint + e.logger.Debug("eth_chainId") + return hexutil.Uint(uint(e.chainIDEpoch.Uint64())), nil +} + +// Syncing returns whether or not the current node is syncing with other peers. Returns false if not, or a struct +// outlining the state of the sync if it is. +func (e *PublicEthAPI) Syncing() (interface{}, error) { + e.logger.Debug("eth_syncing") + + status, err := e.cliCtx.Client.Status() + if err != nil { + return false, err + } + + if !status.SyncInfo.CatchingUp { + return false, nil + } + + return map[string]interface{}{ + // "startingBlock": nil, // NA + "currentBlock": hexutil.Uint64(status.SyncInfo.LatestBlockHeight), + // "highestBlock": nil, // NA + // "pulledStates": nil, // NA + // "knownStates": nil, // NA + }, nil +} + +// Coinbase is the address that staking rewards will be send to (alias for Etherbase). +func (e *PublicEthAPI) Coinbase() (common.Address, error) { + e.logger.Debug("eth_coinbase") + + node, err := e.cliCtx.GetNode() + if err != nil { + return common.Address{}, err + } + + status, err := node.Status() + if err != nil { + return common.Address{}, err + } + + return common.BytesToAddress(status.ValidatorInfo.Address.Bytes()), nil +} + +// Mining returns whether or not this node is currently mining. Always false. +func (e *PublicEthAPI) Mining() bool { + e.logger.Debug("eth_mining") + return false +} + +// Hashrate returns the current node's hashrate. Always 0. +func (e *PublicEthAPI) Hashrate() hexutil.Uint64 { + e.logger.Debug("eth_hashrate") + return 0 +} + +// GasPrice returns the current gas price based on Ethermint's gas price oracle. +func (e *PublicEthAPI) GasPrice() *hexutil.Big { + e.logger.Debug("eth_gasPrice") + out := big.NewInt(0) + return (*hexutil.Big)(out) +} + +// Accounts returns the list of accounts available to this node. +func (e *PublicEthAPI) Accounts() ([]common.Address, error) { + e.logger.Debug("eth_accounts") + e.keybaseLock.Lock() + + addresses := make([]common.Address, 0) // return [] instead of nil if empty + + keybase, err := keys.NewKeyring( + sdk.KeyringServiceName(), + viper.GetString(flags.FlagKeyringBackend), + viper.GetString(flags.FlagHome), + e.cliCtx.Input, + crypto.EthSecp256k1Options()..., + ) + if err != nil { + return addresses, err + } + + infos, err := keybase.List() + if err != nil { + return addresses, err + } + + e.keybaseLock.Unlock() + + for _, info := range infos { + addressBytes := info.GetPubKey().Address().Bytes() + addresses = append(addresses, common.BytesToAddress(addressBytes)) + } + + return addresses, nil +} + +// BlockNumber returns the current block number. +func (e *PublicEthAPI) BlockNumber() (hexutil.Uint64, error) { + e.logger.Debug("eth_blockNumber") + return e.backend.BlockNumber() +} + +// GetBalance returns the provided account's balance up to the provided block number. +func (e *PublicEthAPI) GetBalance(address common.Address, blockNum BlockNumber) (*hexutil.Big, error) { + e.logger.Debug("eth_getBalance", "address", address, "block number", blockNum) + ctx := e.cliCtx.WithHeight(blockNum.Int64()) + res, _, err := ctx.QueryWithData(fmt.Sprintf("custom/%s/balance/%s", evmtypes.ModuleName, address.Hex()), nil) + if err != nil { + return nil, err + } + + var out evmtypes.QueryResBalance + e.cliCtx.Codec.MustUnmarshalJSON(res, &out) + val, err := utils.UnmarshalBigInt(out.Balance) + if err != nil { + return nil, err + } + + return (*hexutil.Big)(val), nil +} + +// GetStorageAt returns the contract storage at the given address, block number, and key. +func (e *PublicEthAPI) GetStorageAt(address common.Address, key string, blockNum BlockNumber) (hexutil.Bytes, error) { + e.logger.Debug("eth_getStorageAt", "address", address, "key", key, "block number", blockNum) + ctx := e.cliCtx.WithHeight(blockNum.Int64()) + res, _, err := ctx.QueryWithData(fmt.Sprintf("custom/%s/storage/%s/%s", evmtypes.ModuleName, address.Hex(), key), nil) + if err != nil { + return nil, err + } + + var out evmtypes.QueryResStorage + e.cliCtx.Codec.MustUnmarshalJSON(res, &out) + return out.Value, nil +} + +// GetTransactionCount returns the number of transactions at the given address up to the given block number. +func (e *PublicEthAPI) GetTransactionCount(address common.Address, blockNum BlockNumber) (*hexutil.Uint64, error) { + e.logger.Debug("eth_getTransactionCount", "address", address, "block number", blockNum) + ctx := e.cliCtx.WithHeight(blockNum.Int64()) + + // Get nonce (sequence) from account + from := sdk.AccAddress(address.Bytes()) + accRet := authtypes.NewAccountRetriever(ctx) + + err := accRet.EnsureExists(from) + if err != nil { + // account doesn't exist yet, return 0 + n := hexutil.Uint64(0) + return &n, nil + } + + _, nonce, err := accRet.GetAccountNumberSequence(from) + if err != nil { + return nil, err + } + + n := hexutil.Uint64(nonce) + return &n, nil +} + +// GetBlockTransactionCountByHash returns the number of transactions in the block identified by hash. +func (e *PublicEthAPI) GetBlockTransactionCountByHash(hash common.Hash) *hexutil.Uint { + e.logger.Debug("eth_getBlockTransactionCountByHash", "hash", hash) + res, _, err := e.cliCtx.Query(fmt.Sprintf("custom/%s/%s/%s", evmtypes.ModuleName, evmtypes.QueryHashToHeight, hash.Hex())) + if err != nil { + // Return nil if block does not exist + return nil + } + + var out evmtypes.QueryResBlockNumber + e.cliCtx.Codec.MustUnmarshalJSON(res, &out) + return e.getBlockTransactionCountByNumber(out.Number) +} + +// GetBlockTransactionCountByNumber returns the number of transactions in the block identified by number. +func (e *PublicEthAPI) GetBlockTransactionCountByNumber(blockNum BlockNumber) *hexutil.Uint { + e.logger.Debug("eth_getBlockTransactionCountByNumber", "block number", blockNum) + height := blockNum.Int64() + return e.getBlockTransactionCountByNumber(height) +} + +func (e *PublicEthAPI) getBlockTransactionCountByNumber(number int64) *hexutil.Uint { + block, err := e.cliCtx.Client.Block(&number) + if err != nil { + // Return nil if block doesn't exist + return nil + } + + n := hexutil.Uint(len(block.Block.Txs)) + return &n +} + +// GetUncleCountByBlockHash returns the number of uncles in the block idenfied by hash. Always zero. +func (e *PublicEthAPI) GetUncleCountByBlockHash(hash common.Hash) hexutil.Uint { + return 0 +} + +// GetUncleCountByBlockNumber returns the number of uncles in the block idenfied by number. Always zero. +func (e *PublicEthAPI) GetUncleCountByBlockNumber(blockNum BlockNumber) hexutil.Uint { + return 0 +} + +// GetCode returns the contract code at the given address and block number. +func (e *PublicEthAPI) GetCode(address common.Address, blockNumber BlockNumber) (hexutil.Bytes, error) { + e.logger.Debug("eth_getCode", "address", address, "block number", blockNumber) + ctx := e.cliCtx.WithHeight(blockNumber.Int64()) + res, _, err := ctx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", evmtypes.ModuleName, evmtypes.QueryCode, address.Hex()), nil) + if err != nil { + return nil, err + } + + var out evmtypes.QueryResCode + e.cliCtx.Codec.MustUnmarshalJSON(res, &out) + return out.Code, nil +} + +// GetTransactionLogs returns the logs given a transaction hash. +func (e *PublicEthAPI) GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, error) { + e.logger.Debug("eth_getTransactionLogs", "hash", txHash) + return e.backend.GetTransactionLogs(txHash) +} + +// ExportAccount exports an account's balance, code, and storage at the given block number +// TODO: deprecate this once the export genesis command works +func (e *PublicEthAPI) ExportAccount(address common.Address, blockNumber BlockNumber) (string, error) { + e.logger.Debug("eth_exportAccount", "address", address, "block number", blockNumber) + ctx := e.cliCtx.WithHeight(blockNumber.Int64()) + + res, _, err := ctx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", evmtypes.ModuleName, evmtypes.QueryExportAccount, address.Hex()), nil) + if err != nil { + return "", err + } + + return string(res), nil +} + +func checkKeyInKeyring(keys []crypto.PrivKeySecp256k1, address common.Address) (key crypto.PrivKeySecp256k1, exist bool) { + if len(keys) > 0 { + for _, key := range keys { + if bytes.Equal(key.PubKey().Address().Bytes(), address.Bytes()) { + return key, true + } + } + } + return nil, false +} + +// Sign signs the provided data using the private key of address via Geth's signature standard. +func (e *PublicEthAPI) Sign(address common.Address, data hexutil.Bytes) (hexutil.Bytes, error) { + e.logger.Debug("eth_sign", "address", address, "data", data) + // TODO: Change this functionality to find an unlocked account by address + + key, exist := checkKeyInKeyring(e.keys, address) + if !exist { + return nil, keystore.ErrLocked + } + + // Sign the requested hash with the wallet + signature, err := key.Sign(data) + if err == nil { + signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper + } + + return signature, err +} + +// SendTransaction sends an Ethereum transaction. +func (e *PublicEthAPI) SendTransaction(args params.SendTxArgs) (common.Hash, error) { + e.logger.Debug("eth_sendTransaction", "args", args) + // TODO: Change this functionality to find an unlocked account by address + + for _, key := range e.keys { + e.logger.Debug("eth_sendTransaction", "key", fmt.Sprintf("0x%x", key.PubKey().Address().Bytes())) + } + + key, exist := checkKeyInKeyring(e.keys, args.From) + if !exist { + e.logger.Debug("failed to find key in keyring", "key", args.From) + return common.Hash{}, keystore.ErrLocked + } + + // Mutex lock the address' nonce to avoid assigning it to multiple requests + if args.Nonce == nil { + e.nonceLock.LockAddr(args.From) + defer e.nonceLock.UnlockAddr(args.From) + } + + // Assemble transaction from fields + tx, err := e.generateFromArgs(args) + if err != nil { + e.logger.Debug("failed to generate tx", "error", err) + return common.Hash{}, err + } + + // ChainID must be set as flag to send transaction + chainID := viper.GetString(flags.FlagChainID) + // parse the chainID from a string to a base-10 integer + chainIDEpoch, err := ethermint.ParseChainID(chainID) + if err != nil { + return common.Hash{}, err + } + + // Sign transaction + if err := tx.Sign(chainIDEpoch, key.ToECDSA()); err != nil { + e.logger.Debug("failed to sign tx", "error", err) + return common.Hash{}, err + } + + // Encode transaction by default Tx encoder + txEncoder := authclient.GetTxEncoder(e.cliCtx.Codec) + txBytes, err := txEncoder(tx) + if err != nil { + return common.Hash{}, err + } + + // Broadcast transaction in sync mode (default) + res, err := e.cliCtx.BroadcastTx(txBytes) + // If error is encountered on the node, the broadcast will not return an error + if err != nil { + return common.Hash{}, err + } + + // Return transaction hash + return common.HexToHash(res.TxHash), nil +} + +// SendRawTransaction send a raw Ethereum transaction. +func (e *PublicEthAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error) { + e.logger.Debug("eth_sendRawTransaction", "data", data) + tx := new(evmtypes.MsgEthereumTx) + + // RLP decode raw transaction bytes + if err := rlp.DecodeBytes(data, tx); err != nil { + // Return nil is for when gasLimit overflows uint64 + return common.Hash{}, nil + } + + // Encode transaction by default Tx encoder + txEncoder := authclient.GetTxEncoder(e.cliCtx.Codec) + txBytes, err := txEncoder(tx) + if err != nil { + return common.Hash{}, err + } + + // TODO: Possibly log the contract creation address (if recipient address is nil) or tx data + // If error is encountered on the node, the broadcast will not return an error + res, err := e.cliCtx.BroadcastTx(txBytes) + if err != nil { + return common.Hash{}, err + } + + // Return transaction hash + return common.HexToHash(res.TxHash), nil +} + +// CallArgs represents the arguments for a call. +type CallArgs struct { + From *common.Address `json:"from"` + To *common.Address `json:"to"` + Gas *hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + Value *hexutil.Big `json:"value"` + Data *hexutil.Bytes `json:"data"` +} + +// Call performs a raw contract call. +func (e *PublicEthAPI) Call(args CallArgs, blockNr BlockNumber, _ *map[common.Address]account) (hexutil.Bytes, error) { + e.logger.Debug("eth_call", "args", args, "block number", blockNr) + simRes, err := e.doCall(args, blockNr, big.NewInt(ethermint.DefaultRPCGasLimit)) + if err != nil { + return []byte{}, err + } + + data, err := evmtypes.DecodeResultData(simRes.Result.Data) + if err != nil { + return []byte{}, err + } + + return (hexutil.Bytes)(data.Ret), nil +} + +// account indicates the overriding fields of account during the execution of +// a message call. +// Note, state and stateDiff can't be specified at the same time. If state is +// set, message execution will only use the data in the given state. Otherwise +// if statDiff is set, all diff will be applied first and then execute the call +// message. +type account struct { + Nonce *hexutil.Uint64 `json:"nonce"` + Code *hexutil.Bytes `json:"code"` + Balance **hexutil.Big `json:"balance"` + State *map[common.Hash]common.Hash `json:"state"` + StateDiff *map[common.Hash]common.Hash `json:"stateDiff"` +} + +// DoCall performs a simulated call operation through the evmtypes. It returns the +// estimated gas used on the operation or an error if fails. +func (e *PublicEthAPI) doCall( + args CallArgs, blockNr BlockNumber, globalGasCap *big.Int, +) (*sdk.SimulationResponse, error) { + // Set height for historical queries + ctx := e.cliCtx + + if blockNr.Int64() != 0 { + ctx = e.cliCtx.WithHeight(blockNr.Int64()) + } + + // Set sender address or use a default if none specified + var addr common.Address + + if args.From == nil { + addrs, err := e.Accounts() + if err == nil && len(addrs) > 0 { + addr = addrs[0] + } + } else { + addr = *args.From + } + + // Set default gas & gas price if none were set + // Change this to uint64(math.MaxUint64 / 2) if gas cap can be configured + gas := uint64(ethermint.DefaultRPCGasLimit) + if args.Gas != nil { + gas = uint64(*args.Gas) + } + if globalGasCap != nil && globalGasCap.Uint64() < gas { + e.logger.Debug("Caller gas above allowance, capping", "requested", gas, "cap", globalGasCap) + gas = globalGasCap.Uint64() + } + + // Set gas price using default or parameter if passed in + gasPrice := new(big.Int).SetUint64(ethermint.DefaultGasPrice) + if args.GasPrice != nil { + gasPrice = args.GasPrice.ToInt() + } + + // Set value for transaction + value := new(big.Int) + if args.Value != nil { + value = args.Value.ToInt() + } + + // Set Data if provided + var data []byte + if args.Data != nil { + data = []byte(*args.Data) + } + + // Set destination address for call + var toAddr sdk.AccAddress + if args.To != nil { + toAddr = sdk.AccAddress(args.To.Bytes()) + } + + // Create new call message + msg := evmtypes.NewMsgEthermint(0, &toAddr, sdk.NewIntFromBigInt(value), gas, + sdk.NewIntFromBigInt(gasPrice), data, sdk.AccAddress(addr.Bytes())) + + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + // Generate tx to be used to simulate (signature isn't needed) + var stdSig authtypes.StdSignature + tx := authtypes.NewStdTx([]sdk.Msg{msg}, authtypes.StdFee{}, []authtypes.StdSignature{stdSig}, "") + + txEncoder := authclient.GetTxEncoder(ctx.Codec) + txBytes, err := txEncoder(tx) + if err != nil { + return nil, err + } + + // Transaction simulation through query + res, _, err := ctx.QueryWithData("app/simulate", txBytes) + if err != nil { + return nil, err + } + + var simResponse sdk.SimulationResponse + if err := ctx.Codec.UnmarshalBinaryBare(res, &simResponse); err != nil { + return nil, err + } + + return &simResponse, nil +} + +// EstimateGas returns an estimate of gas usage for the given smart contract call. +// It adds 1,000 gas to the returned value instead of using the gas adjustment +// param from the SDK. +func (e *PublicEthAPI) EstimateGas(args CallArgs) (hexutil.Uint64, error) { + e.logger.Debug("eth_estimateGas", "args", args) + simResponse, err := e.doCall(args, 0, big.NewInt(ethermint.DefaultRPCGasLimit)) + if err != nil { + return 0, err + } + + // TODO: change 1000 buffer for more accurate buffer (eg: SDK's gasAdjusted) + estimatedGas := simResponse.GasInfo.GasUsed + gas := estimatedGas + 1000 + + return hexutil.Uint64(gas), nil +} + +// GetBlockByHash returns the block identified by hash. +func (e *PublicEthAPI) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) { + e.logger.Debug("eth_getBlockByHash", "hash", hash, "full", fullTx) + return e.backend.GetBlockByHash(hash, fullTx) +} + +// GetBlockByNumber returns the block identified by number. +func (e *PublicEthAPI) GetBlockByNumber(blockNum BlockNumber, fullTx bool) (map[string]interface{}, error) { + e.logger.Debug("eth_getBlockByNumber", "number", blockNum, "full", fullTx) + return e.backend.GetBlockByNumber(blockNum, fullTx) +} + +func formatBlock( + header tmtypes.Header, size int, gasLimit int64, + gasUsed *big.Int, transactions interface{}, bloom ethtypes.Bloom, +) map[string]interface{} { + if bytes.Equal(header.DataHash, []byte{}) { + header.DataHash = tmbytes.HexBytes(common.Hash{}.Bytes()) + } + + return map[string]interface{}{ + "number": hexutil.Uint64(header.Height), + "hash": hexutil.Bytes(header.Hash()), + "parentHash": hexutil.Bytes(header.LastBlockID.Hash), + "nonce": hexutil.Uint64(0), // PoW specific + "sha3Uncles": common.Hash{}, // No uncles in Tendermint + "logsBloom": bloom, + "transactionsRoot": hexutil.Bytes(header.DataHash), + "stateRoot": hexutil.Bytes(header.AppHash), + "miner": common.Address{}, + "mixHash": common.Hash{}, + "difficulty": 0, + "totalDifficulty": 0, + "extraData": hexutil.Uint64(0), + "size": hexutil.Uint64(size), + "gasLimit": hexutil.Uint64(gasLimit), // Static gas limit + "gasUsed": (*hexutil.Big)(gasUsed), + "timestamp": hexutil.Uint64(header.Time.Unix()), + "transactions": transactions.([]common.Hash), + "uncles": []string{}, + "receiptsRoot": common.Hash{}, + } +} + +func convertTransactionsToRPC(cliCtx context.CLIContext, txs []tmtypes.Tx, blockHash common.Hash, height uint64) ([]common.Hash, *big.Int, error) { + transactions := make([]common.Hash, len(txs)) + gasUsed := big.NewInt(0) + + for i, tx := range txs { + ethTx, err := bytesToEthTx(cliCtx, tx) + if err != nil { + // continue to next transaction in case it's not a MsgEthereumTx + continue + } + // TODO: Remove gas usage calculation if saving gasUsed per block + gasUsed.Add(gasUsed, ethTx.Fee()) + tx, err := newRPCTransaction(*ethTx, common.BytesToHash(tx.Hash()), blockHash, &height, uint64(i)) + if err != nil { + return nil, nil, err + } + transactions[i] = tx.Hash + } + + return transactions, gasUsed, nil +} + +// Transaction represents a transaction returned to RPC clients. +type Transaction struct { + BlockHash *common.Hash `json:"blockHash"` + BlockNumber *hexutil.Big `json:"blockNumber"` + From common.Address `json:"from"` + Gas hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + Hash common.Hash `json:"hash"` + Input hexutil.Bytes `json:"input"` + Nonce hexutil.Uint64 `json:"nonce"` + To *common.Address `json:"to"` + TransactionIndex *hexutil.Uint64 `json:"transactionIndex"` + Value *hexutil.Big `json:"value"` + V *hexutil.Big `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` +} + +func bytesToEthTx(cliCtx context.CLIContext, bz []byte) (*evmtypes.MsgEthereumTx, error) { + var stdTx sdk.Tx + // TODO: switch to UnmarshalBinaryBare on SDK v0.40.0 + err := cliCtx.Codec.UnmarshalBinaryLengthPrefixed(bz, &stdTx) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) + } + + ethTx, ok := stdTx.(evmtypes.MsgEthereumTx) + if !ok { + return nil, fmt.Errorf("invalid transaction type %T, expected MsgEthereumTx", stdTx) + } + return ðTx, nil +} + +// newRPCTransaction returns a transaction that will serialize to the RPC +// representation, with the given location metadata set (if available). +func newRPCTransaction(tx evmtypes.MsgEthereumTx, txHash, blockHash common.Hash, blockNumber *uint64, index uint64) (*Transaction, error) { + // Verify signature and retrieve sender address + from, err := tx.VerifySig(tx.ChainID()) + if err != nil { + return nil, err + } + + result := Transaction{ + From: from, + Gas: hexutil.Uint64(tx.Data.GasLimit), + GasPrice: (*hexutil.Big)(tx.Data.Price), + Hash: txHash, + Input: hexutil.Bytes(tx.Data.Payload), + Nonce: hexutil.Uint64(tx.Data.AccountNonce), + To: tx.To(), + Value: (*hexutil.Big)(tx.Data.Amount), + V: (*hexutil.Big)(tx.Data.V), + R: (*hexutil.Big)(tx.Data.R), + S: (*hexutil.Big)(tx.Data.S), + } + + if blockHash != (common.Hash{}) { + result.BlockHash = &blockHash + result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(*blockNumber)) + result.TransactionIndex = (*hexutil.Uint64)(&index) + } + + return &result, nil +} + +// GetTransactionByHash returns the transaction identified by hash. +func (e *PublicEthAPI) GetTransactionByHash(hash common.Hash) (*Transaction, error) { + e.logger.Debug("eth_getTransactionByHash", "hash", hash) + tx, err := e.cliCtx.Client.Tx(hash.Bytes(), false) + if err != nil { + // Return nil for transaction when not found + return nil, nil + } + + // Can either cache or just leave this out if not necessary + block, err := e.cliCtx.Client.Block(&tx.Height) + if err != nil { + return nil, err + } + blockHash := common.BytesToHash(block.Block.Header.Hash()) + + ethTx, err := bytesToEthTx(e.cliCtx, tx.Tx) + if err != nil { + return nil, err + } + + height := uint64(tx.Height) + return newRPCTransaction(*ethTx, common.BytesToHash(tx.Tx.Hash()), blockHash, &height, uint64(tx.Index)) +} + +// GetTransactionByBlockHashAndIndex returns the transaction identified by hash and index. +func (e *PublicEthAPI) GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) (*Transaction, error) { + e.logger.Debug("eth_getTransactionByHashAndIndex", "hash", hash, "index", idx) + res, _, err := e.cliCtx.Query(fmt.Sprintf("custom/%s/%s/%s", evmtypes.ModuleName, evmtypes.QueryHashToHeight, hash.Hex())) + if err != nil { + return nil, err + } + + var out evmtypes.QueryResBlockNumber + e.cliCtx.Codec.MustUnmarshalJSON(res, &out) + return e.getTransactionByBlockNumberAndIndex(out.Number, idx) +} + +// GetTransactionByBlockNumberAndIndex returns the transaction identified by number and index. +func (e *PublicEthAPI) GetTransactionByBlockNumberAndIndex(blockNum BlockNumber, idx hexutil.Uint) (*Transaction, error) { + e.logger.Debug("eth_getTransactionByBlockNumberAndIndex", "number", blockNum, "index", idx) + value := blockNum.Int64() + return e.getTransactionByBlockNumberAndIndex(value, idx) +} + +func (e *PublicEthAPI) getTransactionByBlockNumberAndIndex(number int64, idx hexutil.Uint) (*Transaction, error) { + block, err := e.cliCtx.Client.Block(&number) + if err != nil { + return nil, err + } + header := block.Block.Header + + txs := block.Block.Txs + if uint64(idx) >= uint64(len(txs)) { + return nil, nil + } + ethTx, err := bytesToEthTx(e.cliCtx, txs[idx]) + if err != nil { + return nil, err + } + + height := uint64(header.Height) + return newRPCTransaction(*ethTx, common.BytesToHash(txs[idx].Hash()), common.BytesToHash(header.Hash()), &height, uint64(idx)) +} + +// GetTransactionReceipt returns the transaction receipt identified by hash. +func (e *PublicEthAPI) GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) { + e.logger.Debug("eth_getTransactionReceipt", "hash", hash) + tx, err := e.cliCtx.Client.Tx(hash.Bytes(), false) + if err != nil { + // Return nil for transaction when not found + return nil, nil + } + + // Query block for consensus hash + block, err := e.cliCtx.Client.Block(&tx.Height) + if err != nil { + return nil, err + } + blockHash := common.BytesToHash(block.Block.Header.Hash()) + + // Convert tx bytes to eth transaction + ethTx, err := bytesToEthTx(e.cliCtx, tx.Tx) + if err != nil { + return nil, err + } + + from, err := ethTx.VerifySig(ethTx.ChainID()) + if err != nil { + return nil, err + } + + // Set status codes based on tx result + var status hexutil.Uint + if tx.TxResult.IsOK() { + status = hexutil.Uint(1) + } else { + status = hexutil.Uint(0) + } + + txData := tx.TxResult.GetData() + + data, err := evmtypes.DecodeResultData(txData) + if err != nil { + status = 0 // transaction failed + } + + if data.Logs == nil { + data.Logs = []*ethtypes.Log{} + } + + receipt := map[string]interface{}{ + // Consensus fields: These fields are defined by the Yellow Paper + "status": status, + "cumulativeGasUsed": nil, // ignore until needed + "logsBloom": data.Bloom, + "logs": data.Logs, + + // Implementation fields: These fields are added by geth when processing a transaction. + // They are stored in the chain database. + "transactionHash": hash, + "contractAddress": data.ContractAddress, + "gasUsed": hexutil.Uint64(tx.TxResult.GasUsed), + + // Inclusion information: These fields provide information about the inclusion of the + // transaction corresponding to this receipt. + "blockHash": blockHash, + "blockNumber": hexutil.Uint64(tx.Height), + "transactionIndex": hexutil.Uint64(tx.Index), + + // sender and receiver (contract or EOA) addreses + "from": from, + "to": ethTx.To(), + } + + return receipt, nil +} + +// PendingTransactions returns the transactions that are in the transaction pool +// and have a from address that is one of the accounts this node manages. +func (e *PublicEthAPI) PendingTransactions() ([]*Transaction, error) { + e.logger.Debug("eth_getPendingTransactions") + return e.backend.PendingTransactions() +} + +// GetUncleByBlockHashAndIndex returns the uncle identified by hash and index. Always returns nil. +func (e *PublicEthAPI) GetUncleByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) map[string]interface{} { + return nil +} + +// GetUncleByBlockNumberAndIndex returns the uncle identified by number and index. Always returns nil. +func (e *PublicEthAPI) GetUncleByBlockNumberAndIndex(number hexutil.Uint, idx hexutil.Uint) map[string]interface{} { + return nil +} + +// Copied the Account and StorageResult types since they are registered under an +// internal pkg on geth. + +// AccountResult struct for account proof +type AccountResult struct { + Address common.Address `json:"address"` + AccountProof []string `json:"accountProof"` + Balance *hexutil.Big `json:"balance"` + CodeHash common.Hash `json:"codeHash"` + Nonce hexutil.Uint64 `json:"nonce"` + StorageHash common.Hash `json:"storageHash"` + StorageProof []StorageResult `json:"storageProof"` +} + +// StorageResult defines the format for storage proof return +type StorageResult struct { + Key string `json:"key"` + Value *hexutil.Big `json:"value"` + Proof []string `json:"proof"` +} + +// GetProof returns an account object with proof and any storage proofs +func (e *PublicEthAPI) GetProof(address common.Address, storageKeys []string, block BlockNumber) (*AccountResult, error) { + e.logger.Debug("eth_getProof", "address", address, "keys", storageKeys, "number", block) + e.cliCtx = e.cliCtx.WithHeight(int64(block)) + path := fmt.Sprintf("custom/%s/%s/%s", evmtypes.ModuleName, evmtypes.QueryAccount, address.Hex()) + + // query eth account at block height + resBz, _, err := e.cliCtx.Query(path) + if err != nil { + return nil, err + } + + var account evmtypes.QueryResAccount + e.cliCtx.Codec.MustUnmarshalJSON(resBz, &account) + + storageProofs := make([]StorageResult, len(storageKeys)) + opts := client.ABCIQueryOptions{Height: int64(block), Prove: true} + for i, k := range storageKeys { + // Get value for key + vPath := fmt.Sprintf("custom/%s/%s/%s/%s", evmtypes.ModuleName, evmtypes.QueryStorage, address, k) + vRes, err := e.cliCtx.Client.ABCIQueryWithOptions(vPath, nil, opts) + if err != nil { + return nil, err + } + + var value evmtypes.QueryResStorage + e.cliCtx.Codec.MustUnmarshalJSON(vRes.Response.GetValue(), &value) + + // check for proof + proof := vRes.Response.GetProof() + proofStr := new(merkle.Proof).String() + if proof != nil { + proofStr = proof.String() + } + + storageProofs[i] = StorageResult{ + Key: k, + Value: (*hexutil.Big)(common.BytesToHash(value.Value).Big()), + Proof: []string{proofStr}, + } + } + + req := abci.RequestQuery{ + Path: fmt.Sprintf("store/%s/key", auth.StoreKey), + Data: auth.AddressStoreKey(sdk.AccAddress(address.Bytes())), + Height: int64(block), + Prove: true, + } + + res, err := e.cliCtx.QueryABCI(req) + if err != nil { + return nil, err + } + + // check for proof + accountProof := res.GetProof() + accProofStr := new(merkle.Proof).String() + if accountProof != nil { + accProofStr = accountProof.String() + } + + return &AccountResult{ + Address: address, + AccountProof: []string{accProofStr}, + Balance: (*hexutil.Big)(utils.MustUnmarshalBigInt(account.Balance)), + CodeHash: common.BytesToHash(account.CodeHash), + Nonce: hexutil.Uint64(account.Nonce), + StorageHash: common.Hash{}, // Ethermint doesn't have a storage hash + StorageProof: storageProofs, + }, nil +} + +// generateFromArgs populates tx message with args (used in RPC API) +func (e *PublicEthAPI) generateFromArgs(args params.SendTxArgs) (*evmtypes.MsgEthereumTx, error) { + var ( + nonce uint64 + gasLimit uint64 + err error + ) + + amount := (*big.Int)(args.Value) + gasPrice := (*big.Int)(args.GasPrice) + + if args.GasPrice == nil { + + // Set default gas price + // TODO: Change to min gas price from context once available through server/daemon + gasPrice = big.NewInt(ethermint.DefaultGasPrice) + } + + if args.Nonce == nil { + // Get nonce (sequence) from account + from := sdk.AccAddress(args.From.Bytes()) + accRet := authtypes.NewAccountRetriever(e.cliCtx) + + if e.cliCtx.Keybase == nil { + return nil, fmt.Errorf("cliCtx.Keybase is nil") + } + + err = accRet.EnsureExists(from) + if err != nil { + // account doesn't exist + return nil, fmt.Errorf("nonexistent account %s: %s", args.From.Hex(), err) + } + + _, nonce, err = accRet.GetAccountNumberSequence(from) + if err != nil { + return nil, err + } + } else { + nonce = (uint64)(*args.Nonce) + } + + if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) { + return nil, errors.New(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`) + } + + // Sets input to either Input or Data, if both are set and not equal error above returns + var input []byte + if args.Input != nil { + input = *args.Input + } else if args.Data != nil { + input = *args.Data + } + + if args.To == nil && len(input) == 0 { + // Contract creation + return nil, fmt.Errorf("contract creation without any data provided") + } + + if args.Gas == nil { + callArgs := CallArgs{ + From: &args.From, + To: args.To, + Gas: args.Gas, + GasPrice: args.GasPrice, + Value: args.Value, + Data: args.Data, + } + gl, err := e.EstimateGas(callArgs) + if err != nil { + return nil, err + } + gasLimit = uint64(gl) + } else { + gasLimit = (uint64)(*args.Gas) + } + msg := evmtypes.NewMsgEthereumTx(nonce, args.To, amount, gasLimit, gasPrice, input) + + return &msg, nil +} diff --git a/rpc/filter_api.go b/rpc/filter_api.go new file mode 100644 index 0000000000..6d2fdc845c --- /dev/null +++ b/rpc/filter_api.go @@ -0,0 +1,553 @@ +package rpc + +import ( + "context" + "fmt" + "sync" + "time" + + coretypes "github.com/tendermint/tendermint/rpc/core/types" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/filters" + "github.com/ethereum/go-ethereum/rpc" + + clientcontext "github.com/cosmos/cosmos-sdk/client/context" + + evmtypes "github.com/cosmos/ethermint/x/evm/types" +) + +// FiltersBackend defines the methods requided by the PublicFilterAPI backend +type FiltersBackend interface { + GetBlockByNumber(blockNum BlockNumber, fullTx bool) (map[string]interface{}, error) + HeaderByNumber(blockNr BlockNumber) (*ethtypes.Header, error) + HeaderByHash(blockHash common.Hash) (*ethtypes.Header, error) + GetLogs(blockHash common.Hash) ([][]*ethtypes.Log, error) + + GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log, error) + BloomStatus() (uint64, uint64) +} + +// consider a filter inactive if it has not been polled for within deadline +var deadline = 5 * time.Minute + +// filter is a helper struct that holds meta information over the filter type +// and associated subscription in the event system. +type filter struct { + typ filters.Type + deadline *time.Timer // filter is inactive when deadline triggers + hashes []common.Hash + crit filters.FilterCriteria + logs []*ethtypes.Log + s *Subscription // associated subscription in event system +} + +// PublicFilterAPI offers support to create and manage filters. This will allow external clients to retrieve various +// information related to the Ethereum protocol such as blocks, transactions and logs. +type PublicFilterAPI struct { + cliCtx clientcontext.CLIContext + backend FiltersBackend + events *EventSystem + filtersMu sync.Mutex + filters map[rpc.ID]*filter +} + +// NewPublicFilterAPI returns a new PublicFilterAPI instance. +func NewPublicFilterAPI(cliCtx clientcontext.CLIContext, backend FiltersBackend) *PublicFilterAPI { + // start the client to subscribe to Tendermint events + err := cliCtx.Client.Start() + if err != nil { + panic(err) + } + + api := &PublicFilterAPI{ + cliCtx: cliCtx, + backend: backend, + filters: make(map[rpc.ID]*filter), + events: NewEventSystem(cliCtx.Client), + } + + go api.timeoutLoop() + + return api +} + +// timeoutLoop runs every 5 minutes and deletes filters that have not been recently used. +// Tt is started when the api is created. +func (api *PublicFilterAPI) timeoutLoop() { + ticker := time.NewTicker(deadline) + defer ticker.Stop() + + for { + <-ticker.C + api.filtersMu.Lock() + for id, f := range api.filters { + select { + case <-f.deadline.C: + f.s.Unsubscribe(api.events) + delete(api.filters, id) + default: + continue + } + } + api.filtersMu.Unlock() + } +} + +// NewPendingTransactionFilter creates a filter that fetches pending transaction hashes +// as transactions enter the pending state. +// +// It is part of the filter package because this filter can be used through the +// `eth_getFilterChanges` polling method that is also used for log filters. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newPendingTransactionFilter +func (api *PublicFilterAPI) NewPendingTransactionFilter() rpc.ID { + pendingTxSub, cancelSubs, err := api.events.SubscribePendingTxs() + if err != nil { + // wrap error on the ID + return rpc.ID(fmt.Sprintf("error creating pending tx filter: %s", err.Error())) + } + + api.filtersMu.Lock() + api.filters[pendingTxSub.ID()] = &filter{typ: filters.PendingTransactionsSubscription, deadline: time.NewTimer(deadline), hashes: make([]common.Hash, 0), s: pendingTxSub} + api.filtersMu.Unlock() + + go func(txsCh <-chan coretypes.ResultEvent, errCh <-chan error) { + defer cancelSubs() + + for { + select { + case ev := <-txsCh: + data, _ := ev.Data.(tmtypes.EventDataTx) + txHash := common.BytesToHash(data.Tx.Hash()) + + api.filtersMu.Lock() + if f, found := api.filters[pendingTxSub.ID()]; found { + f.hashes = append(f.hashes, txHash) + } + api.filtersMu.Unlock() + case <-errCh: + api.filtersMu.Lock() + delete(api.filters, pendingTxSub.ID()) + api.filtersMu.Unlock() + } + } + }(pendingTxSub.eventCh, pendingTxSub.Err()) + + return pendingTxSub.ID() +} + +// NewPendingTransactions creates a subscription that is triggered each time a transaction +// enters the transaction pool and was signed from one of the transactions this nodes manages. +func (api *PublicFilterAPI) NewPendingTransactions(ctx context.Context) (*rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported + } + + rpcSub := notifier.CreateSubscription() + + ctx, cancelFn := context.WithTimeout(context.Background(), deadline) + defer cancelFn() + + api.events.WithContext(ctx) + + pendingTxSub, cancelSubs, err := api.events.SubscribePendingTxs() + if err != nil { + return nil, err + } + + go func(txsCh <-chan coretypes.ResultEvent) { + defer cancelSubs() + + for { + select { + case ev := <-txsCh: + data, _ := ev.Data.(tmtypes.EventDataTx) + txHash := common.BytesToHash(data.Tx.Hash()) + + // To keep the original behaviour, send a single tx hash in one notification. + // TODO(rjl493456442) Send a batch of tx hashes in one notification + err = notifier.Notify(rpcSub.ID, txHash) + if err != nil { + return + } + case <-rpcSub.Err(): + pendingTxSub.Unsubscribe(api.events) + return + case <-notifier.Closed(): + pendingTxSub.Unsubscribe(api.events) + return + } + } + }(pendingTxSub.eventCh) + + return rpcSub, err +} + +// NewBlockFilter creates a filter that fetches blocks that are imported into the chain. +// It is part of the filter package since polling goes with eth_getFilterChanges. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newblockfilter +func (api *PublicFilterAPI) NewBlockFilter() rpc.ID { + headerSub, cancelSubs, err := api.events.SubscribeNewHeads() + if err != nil { + // wrap error on the ID + return rpc.ID(fmt.Sprintf("error creating block filter: %s", err.Error())) + } + + api.filtersMu.Lock() + api.filters[headerSub.ID()] = &filter{typ: filters.BlocksSubscription, deadline: time.NewTimer(deadline), hashes: []common.Hash{}, s: headerSub} + api.filtersMu.Unlock() + + go func(headersCh <-chan coretypes.ResultEvent, errCh <-chan error) { + defer cancelSubs() + + for { + select { + case ev := <-headersCh: + data, _ := ev.Data.(tmtypes.EventDataNewBlockHeader) + header := EthHeaderFromTendermint(data.Header) + api.filtersMu.Lock() + if f, found := api.filters[headerSub.ID()]; found { + f.hashes = append(f.hashes, header.Hash()) + } + api.filtersMu.Unlock() + case <-errCh: + api.filtersMu.Lock() + delete(api.filters, headerSub.ID()) + api.filtersMu.Unlock() + return + } + } + }(headerSub.eventCh, headerSub.Err()) + + return headerSub.ID() +} + +// NewHeads send a notification each time a new (header) block is appended to the chain. +func (api *PublicFilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported + } + + api.events.WithContext(ctx) + rpcSub := notifier.CreateSubscription() + + headersSub, cancelSubs, err := api.events.SubscribeNewHeads() + if err != nil { + return &rpc.Subscription{}, err + } + + go func(headersCh <-chan coretypes.ResultEvent) { + defer cancelSubs() + + for { + select { + case ev := <-headersCh: + data, ok := ev.Data.(tmtypes.EventDataNewBlockHeader) + if !ok { + err = fmt.Errorf("invalid event data %T, expected %s", ev.Data, tmtypes.EventNewBlockHeader) + headersSub.err <- err + return + } + + header := EthHeaderFromTendermint(data.Header) + err = notifier.Notify(rpcSub.ID, header) + if err != nil { + headersSub.err <- err + return + } + case <-rpcSub.Err(): + headersSub.Unsubscribe(api.events) + return + case <-notifier.Closed(): + headersSub.Unsubscribe(api.events) + return + } + } + }(headersSub.eventCh) + + return rpcSub, err +} + +// Logs creates a subscription that fires for all new log that match the given filter criteria. +func (api *PublicFilterAPI) Logs(ctx context.Context, crit filters.FilterCriteria) (*rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported + } + + api.events.WithContext(ctx) + rpcSub := notifier.CreateSubscription() + + logsSub, cancelSubs, err := api.events.SubscribeLogs(crit) + if err != nil { + return &rpc.Subscription{}, err + } + + go func(logsCh <-chan coretypes.ResultEvent) { + defer cancelSubs() + + for { + select { + case event := <-logsCh: + // filter only events from EVM module txs + _, isMsgEthermint := event.Events[evmtypes.TypeMsgEthermint] + _, isMsgEthereumTx := event.Events[evmtypes.TypeMsgEthereumTx] + + if !(isMsgEthermint || isMsgEthereumTx) { + // ignore transaction as it's not from the evm module + return + } + + // get transaction result data + dataTx, ok := event.Data.(tmtypes.EventDataTx) + if !ok { + err = fmt.Errorf("invalid event data %T, expected %s", event.Data, tmtypes.EventTx) + logsSub.err <- err + return + } + + resultData, err := evmtypes.DecodeResultData(dataTx.TxResult.Result.Data) + if err != nil { + return + } + + logs := filterLogs(resultData.Logs, crit.FromBlock, crit.ToBlock, crit.Addresses, crit.Topics) + + for _, log := range logs { + err = notifier.Notify(rpcSub.ID, log) + if err != nil { + return + } + } + case <-rpcSub.Err(): // client send an unsubscribe request + logsSub.Unsubscribe(api.events) + return + case <-notifier.Closed(): // connection dropped + logsSub.Unsubscribe(api.events) + return + } + } + }(logsSub.eventCh) + + return rpcSub, err +} + +// NewFilter creates a new filter and returns the filter id. It can be +// used to retrieve logs when the state changes. This method cannot be +// used to fetch logs that are already stored in the state. +// +// Default criteria for the from and to block are "latest". +// Using "latest" as block number will return logs for mined blocks. +// Using "pending" as block number returns logs for not yet mined (pending) blocks. +// In case logs are removed (chain reorg) previously returned logs are returned +// again but with the removed property set to true. +// +// In case "fromBlock" > "toBlock" an error is returned. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter +func (api *PublicFilterAPI) NewFilter(criteria filters.FilterCriteria) (rpc.ID, error) { + var ( + filterID = rpc.ID("") + err error + ) + + logsSub, cancelSubs, err := api.events.SubscribeLogs(criteria) + if err != nil { + return rpc.ID(""), err + } + + filterID = logsSub.ID() + + api.filtersMu.Lock() + api.filters[filterID] = &filter{typ: filters.LogsSubscription, deadline: time.NewTimer(deadline), hashes: []common.Hash{}, s: logsSub} + api.filtersMu.Unlock() + + go func(eventCh <-chan coretypes.ResultEvent) { + defer cancelSubs() + + for { + select { + case event := <-eventCh: + dataTx, ok := event.Data.(tmtypes.EventDataTx) + if !ok { + err = fmt.Errorf("invalid event data %T, expected EventDataTx", event.Data) + return + } + + var resultData evmtypes.ResultData + resultData, err = evmtypes.DecodeResultData(dataTx.TxResult.Result.Data) + if err != nil { + return + } + + logs := filterLogs(resultData.Logs, criteria.FromBlock, criteria.ToBlock, criteria.Addresses, criteria.Topics) + + api.filtersMu.Lock() + if f, found := api.filters[filterID]; found { + f.logs = append(f.logs, logs...) + } + api.filtersMu.Unlock() + case <-logsSub.Err(): + api.filtersMu.Lock() + delete(api.filters, filterID) + api.filtersMu.Unlock() + return + } + } + }(logsSub.eventCh) + + return filterID, err +} + +// GetLogs returns logs matching the given argument that are stored within the state. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs +func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit filters.FilterCriteria) ([]*ethtypes.Log, error) { + var filter *Filter + if crit.BlockHash != nil { + // Block filter requested, construct a single-shot filter + filter = NewBlockFilter(api.backend, crit) + } else { + // Convert the RPC block numbers into internal representations + begin := rpc.LatestBlockNumber.Int64() + if crit.FromBlock != nil { + begin = crit.FromBlock.Int64() + } + end := rpc.LatestBlockNumber.Int64() + if crit.ToBlock != nil { + end = crit.ToBlock.Int64() + } + // Construct the range filter + filter = NewRangeFilter(api.backend, begin, end, crit.Addresses, crit.Topics) + } + + // Run the filter and return all the logs + logs, err := filter.Logs(ctx) + if err != nil { + return nil, err + } + + return returnLogs(logs), err +} + +// UninstallFilter removes the filter with the given filter id. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_uninstallfilter +func (api *PublicFilterAPI) UninstallFilter(id rpc.ID) bool { + api.filtersMu.Lock() + f, found := api.filters[id] + if found { + delete(api.filters, id) + } + api.filtersMu.Unlock() + + if !found { + return false + } + f.s.Unsubscribe(api.events) + return true +} + +// GetFilterLogs returns the logs for the filter with the given id. +// If the filter could not be found an empty array of logs is returned. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterlogs +func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*ethtypes.Log, error) { + api.filtersMu.Lock() + f, found := api.filters[id] + api.filtersMu.Unlock() + + if !found { + return returnLogs(nil), fmt.Errorf("filter %s not found", id) + } + + if f.typ != filters.LogsSubscription { + return returnLogs(nil), fmt.Errorf("filter %s doesn't have a LogsSubscription type: got %d", id, f.typ) + } + + var filter *Filter + if f.crit.BlockHash != nil { + // Block filter requested, construct a single-shot filter + filter = NewBlockFilter(api.backend, f.crit) + } else { + // Convert the RPC block numbers into internal representations + begin := rpc.LatestBlockNumber.Int64() + if f.crit.FromBlock != nil { + begin = f.crit.FromBlock.Int64() + } + end := rpc.LatestBlockNumber.Int64() + if f.crit.ToBlock != nil { + end = f.crit.ToBlock.Int64() + } + // Construct the range filter + filter = NewRangeFilter(api.backend, begin, end, f.crit.Addresses, f.crit.Topics) + } + // Run the filter and return all the logs + logs, err := filter.Logs(ctx) + if err != nil { + return nil, err + } + return returnLogs(logs), nil +} + +// GetFilterChanges returns the logs for the filter with the given id since +// last time it was called. This can be used for polling. +// +// For pending transaction and block filters the result is []common.Hash. +// (pending)Log filters return []Log. +// +// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges +func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) { + api.filtersMu.Lock() + defer api.filtersMu.Unlock() + + f, found := api.filters[id] + if !found { + return nil, fmt.Errorf("filter %s not found", id) + } + + if !f.deadline.Stop() { + // timer expired but filter is not yet removed in timeout loop + // receive timer value and reset timer + <-f.deadline.C + } + f.deadline.Reset(deadline) + + switch f.typ { + case filters.PendingTransactionsSubscription, filters.BlocksSubscription: + hashes := f.hashes + f.hashes = nil + return returnHashes(hashes), nil + case filters.LogsSubscription, filters.MinedAndPendingLogsSubscription: + logs := make([]*ethtypes.Log, len(f.logs)) + copy(logs, f.logs) + f.logs = []*ethtypes.Log{} + return returnLogs(logs), nil + default: + return nil, fmt.Errorf("invalid filter %s type %d", id, f.typ) + } +} + +// returnHashes is a helper that will return an empty hash array case the given hash array is nil, +// otherwise the given hashes array is returned. +func returnHashes(hashes []common.Hash) []common.Hash { + if hashes == nil { + return []common.Hash{} + } + return hashes +} + +// returnLogs is a helper that will return an empty log array in case the given logs array is nil, +// otherwise the given logs array is returned. +func returnLogs(logs []*ethtypes.Log) []*ethtypes.Log { + if logs == nil { + return []*ethtypes.Log{} + } + return logs +} diff --git a/rpc/filter_system.go b/rpc/filter_system.go new file mode 100644 index 0000000000..00e7aa96b5 --- /dev/null +++ b/rpc/filter_system.go @@ -0,0 +1,433 @@ +package rpc + +import ( + "context" + "fmt" + "log" + "time" + + tmquery "github.com/tendermint/tendermint/libs/pubsub/query" + rpcclient "github.com/tendermint/tendermint/rpc/client" + coretypes "github.com/tendermint/tendermint/rpc/core/types" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/filters" + "github.com/ethereum/go-ethereum/rpc" + + sdk "github.com/cosmos/cosmos-sdk/types" + + evmtypes "github.com/cosmos/ethermint/x/evm/types" +) + +var ( + txEvents = tmtypes.QueryForEvent(tmtypes.EventTx).String() + evmEvents = tmquery.MustParse(fmt.Sprintf("%s='%s' AND %s.%s='%s'", tmtypes.EventTypeKey, tmtypes.EventTx, sdk.EventTypeMessage, sdk.AttributeKeyModule, evmtypes.ModuleName)).String() + headerEvents = tmtypes.QueryForEvent(tmtypes.EventNewBlockHeader).String() +) + +// EventSystem creates subscriptions, processes events and broadcasts them to the +// subscription which match the subscription criteria using the Tendermint's RPC client. +type EventSystem struct { + ctx context.Context + client rpcclient.Client + + // light client mode + lightMode bool + + index filterIndex + + // Subscriptions + txsSub *Subscription // Subscription for new transaction event + logsSub *Subscription // Subscription for new log event + // rmLogsSub *Subscription // Subscription for removed log event + + pendingLogsSub *Subscription // Subscription for pending log event + chainSub *Subscription // Subscription for new chain event + + // Channels + install chan *Subscription // install filter for event notification + uninstall chan *Subscription // remove filter for event notification + + // Unidirectional channels to receive Tendermint ResultEvents + txsCh <-chan coretypes.ResultEvent // Channel to receive new pending transactions event + logsCh <-chan coretypes.ResultEvent // Channel to receive new log event + pendingLogsCh <-chan coretypes.ResultEvent // Channel to receive new pending log event + // rmLogsCh <-chan coretypes.ResultEvent // Channel to receive removed log event + + chainCh <-chan coretypes.ResultEvent // Channel to receive new chain event +} + +// NewEventSystem creates a new manager that listens for event on the given mux, +// parses and filters them. It uses the all map to retrieve filter changes. The +// work loop holds its own index that is used to forward events to filters. +// +// The returned manager has a loop that needs to be stopped with the Stop function +// or by stopping the given mux. +func NewEventSystem(client rpcclient.Client) *EventSystem { + index := make(filterIndex) + for i := filters.UnknownSubscription; i < filters.LastIndexSubscription; i++ { + index[i] = make(map[rpc.ID]*Subscription) + } + + es := &EventSystem{ + ctx: context.Background(), + client: client, + lightMode: false, + index: index, + install: make(chan *Subscription), + uninstall: make(chan *Subscription), + txsCh: make(<-chan coretypes.ResultEvent), + logsCh: make(<-chan coretypes.ResultEvent), + pendingLogsCh: make(<-chan coretypes.ResultEvent), + // rmLogsCh: make(<-chan coretypes.ResultEvent), + chainCh: make(<-chan coretypes.ResultEvent), + } + + go es.eventLoop() + return es +} + +// WithContext sets a new context to the EventSystem. This is required to set a timeout context when +// a new filter is intantiated. +func (es *EventSystem) WithContext(ctx context.Context) { + es.ctx = ctx +} + +// subscribe performs a new event subscription to a given Tendermint event. +// The subscription creates a unidirectional receive event channel to receive the ResultEvent. By +// default, the subscription timeouts (i.e is canceled) after 5 minutes. This function returns an +// error if the subscription fails (eg: if the identifier is already subscribed) or if the filter +// type is invalid. +func (es *EventSystem) subscribe(sub *Subscription) (*Subscription, context.CancelFunc, error) { + var ( + err error + cancelFn context.CancelFunc + eventCh <-chan coretypes.ResultEvent + ) + + es.ctx, cancelFn = context.WithTimeout(context.Background(), deadline) + + switch sub.typ { + case filters.PendingTransactionsSubscription: + eventCh, err = es.client.Subscribe(es.ctx, string(sub.id), sub.event) + case filters.PendingLogsSubscription, filters.MinedAndPendingLogsSubscription: + eventCh, err = es.client.Subscribe(es.ctx, string(sub.id), sub.event) + case filters.LogsSubscription: + eventCh, err = es.client.Subscribe(es.ctx, string(sub.id), sub.event) + case filters.BlocksSubscription: + eventCh, err = es.client.Subscribe(es.ctx, string(sub.id), sub.event) + default: + err = fmt.Errorf("invalid filter subscription type %d", sub.typ) + } + + if err != nil { + sub.err <- err + return nil, cancelFn, err + } + + // wrap events in a go routine to prevent blocking + go func() { + es.install <- sub + <-sub.installed + }() + + sub.eventCh = eventCh + return sub, cancelFn, nil +} + +// SubscribeLogs creates a subscription that will write all logs matching the +// given criteria to the given logs channel. Default value for the from and to +// block is "latest". If the fromBlock > toBlock an error is returned. +func (es *EventSystem) SubscribeLogs(crit filters.FilterCriteria) (*Subscription, context.CancelFunc, error) { + var from, to rpc.BlockNumber + if crit.FromBlock == nil { + from = rpc.LatestBlockNumber + } else { + from = rpc.BlockNumber(crit.FromBlock.Int64()) + } + if crit.ToBlock == nil { + to = rpc.LatestBlockNumber + } else { + to = rpc.BlockNumber(crit.ToBlock.Int64()) + } + + switch { + // only interested in pending logs + case from == rpc.PendingBlockNumber && to == rpc.PendingBlockNumber: + return es.subscribePendingLogs(crit) + + // only interested in new mined logs, mined logs within a specific block range, or + // logs from a specific block number to new mined blocks + case (from == rpc.LatestBlockNumber && to == rpc.LatestBlockNumber), + (from >= 0 && to >= 0 && to >= from): + return es.subscribeLogs(crit) + + // interested in mined logs from a specific block number, new logs and pending logs + case from >= rpc.LatestBlockNumber && (to == rpc.PendingBlockNumber || to == rpc.LatestBlockNumber): + return es.subscribeMinedPendingLogs(crit) + + default: + return nil, nil, fmt.Errorf("invalid from and to block combination: from > to (%d > %d)", from, to) + } +} + +// subscribeMinedPendingLogs creates a subscription that returned mined and +// pending logs that match the given criteria. +func (es *EventSystem) subscribeMinedPendingLogs(crit filters.FilterCriteria) (*Subscription, context.CancelFunc, error) { + sub := &Subscription{ + id: rpc.NewID(), + typ: filters.MinedAndPendingLogsSubscription, + event: evmEvents, + logsCrit: crit, + created: time.Now().UTC(), + logs: make(chan []*ethtypes.Log), + installed: make(chan struct{}, 1), + err: make(chan error, 1), + } + return es.subscribe(sub) +} + +// subscribeLogs creates a subscription that will write all logs matching the +// given criteria to the given logs channel. +func (es *EventSystem) subscribeLogs(crit filters.FilterCriteria) (*Subscription, context.CancelFunc, error) { + sub := &Subscription{ + id: rpc.NewID(), + typ: filters.LogsSubscription, + event: evmEvents, + logsCrit: crit, + created: time.Now().UTC(), + logs: make(chan []*ethtypes.Log), + installed: make(chan struct{}, 1), + err: make(chan error, 1), + } + return es.subscribe(sub) +} + +// subscribePendingLogs creates a subscription that writes transaction hashes for +// transactions that enter the transaction pool. +func (es *EventSystem) subscribePendingLogs(crit filters.FilterCriteria) (*Subscription, context.CancelFunc, error) { + sub := &Subscription{ + id: rpc.NewID(), + typ: filters.PendingLogsSubscription, + event: evmEvents, + logsCrit: crit, + created: time.Now().UTC(), + logs: make(chan []*ethtypes.Log), + installed: make(chan struct{}, 1), + err: make(chan error, 1), + } + return es.subscribe(sub) +} + +// SubscribeNewHeads subscribes to new block headers events. +func (es EventSystem) SubscribeNewHeads() (*Subscription, context.CancelFunc, error) { + sub := &Subscription{ + id: rpc.NewID(), + typ: filters.BlocksSubscription, + event: headerEvents, + created: time.Now().UTC(), + headers: make(chan *ethtypes.Header), + installed: make(chan struct{}, 1), + err: make(chan error, 1), + } + return es.subscribe(sub) +} + +// SubscribePendingTxs subscribes to new pending transactions events from the mempool. +func (es EventSystem) SubscribePendingTxs() (*Subscription, context.CancelFunc, error) { + sub := &Subscription{ + id: rpc.NewID(), + typ: filters.PendingTransactionsSubscription, + event: txEvents, + created: time.Now().UTC(), + hashes: make(chan []common.Hash), + installed: make(chan struct{}, 1), + err: make(chan error, 1), + } + return es.subscribe(sub) +} + +type filterIndex map[filters.Type]map[rpc.ID]*Subscription + +func (es *EventSystem) handleLogs(ev coretypes.ResultEvent) { + data, _ := ev.Data.(tmtypes.EventDataTx) + resultData, err := evmtypes.DecodeResultData(data.TxResult.Result.Data) + if err != nil { + return + } + + if len(resultData.Logs) == 0 { + return + } + for _, f := range es.index[filters.LogsSubscription] { + matchedLogs := filterLogs(resultData.Logs, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics) + if len(matchedLogs) > 0 { + f.logs <- matchedLogs + } + } +} + +func (es *EventSystem) handleTxsEvent(ev coretypes.ResultEvent) { + data, _ := ev.Data.(tmtypes.EventDataTx) + for _, f := range es.index[filters.PendingTransactionsSubscription] { + f.hashes <- []common.Hash{common.BytesToHash(data.Tx.Hash())} + } +} + +func (es *EventSystem) handleChainEvent(ev coretypes.ResultEvent) { + data, _ := ev.Data.(tmtypes.EventDataNewBlockHeader) + for _, f := range es.index[filters.BlocksSubscription] { + f.headers <- EthHeaderFromTendermint(data.Header) + } + // TODO: light client +} + +// eventLoop (un)installs filters and processes mux events. +func (es *EventSystem) eventLoop() { + var ( + err error + cancelPendingTxsSubs, cancelLogsSubs, cancelPendingLogsSubs, cancelHeaderSubs context.CancelFunc + ) + + // Subscribe events + es.txsSub, cancelPendingTxsSubs, err = es.SubscribePendingTxs() + if err != nil { + panic(fmt.Errorf("failed to subscribe pending txs: %w", err)) + } + + defer cancelPendingTxsSubs() + + es.logsSub, cancelLogsSubs, err = es.SubscribeLogs(filters.FilterCriteria{}) + if err != nil { + panic(fmt.Errorf("failed to subscribe logs: %w", err)) + } + + defer cancelLogsSubs() + + es.pendingLogsSub, cancelPendingLogsSubs, err = es.subscribePendingLogs(filters.FilterCriteria{}) + if err != nil { + panic(fmt.Errorf("failed to subscribe pending logs: %w", err)) + } + + defer cancelPendingLogsSubs() + + es.chainSub, cancelHeaderSubs, err = es.SubscribeNewHeads() + if err != nil { + panic(fmt.Errorf("failed to subscribe headers: %w", err)) + } + + defer cancelHeaderSubs() + + // Ensure all subscriptions get cleaned up + defer func() { + es.txsSub.Unsubscribe(es) + es.logsSub.Unsubscribe(es) + // es.rmLogsSub.Unsubscribe(es) + es.pendingLogsSub.Unsubscribe(es) + es.chainSub.Unsubscribe(es) + }() + + for { + select { + case txEvent := <-es.txsSub.eventCh: + es.handleTxsEvent(txEvent) + case headerEv := <-es.chainSub.eventCh: + es.handleChainEvent(headerEv) + case logsEv := <-es.logsSub.eventCh: + es.handleLogs(logsEv) + // TODO: figure out how to handle removed logs + // case logsEv := <-es.rmLogsSub.eventCh: + // es.handleLogs(logsEv) + case logsEv := <-es.pendingLogsSub.eventCh: + es.handleLogs(logsEv) + + case f := <-es.install: + if f.typ == filters.MinedAndPendingLogsSubscription { + // the type are logs and pending logs subscriptions + es.index[filters.LogsSubscription][f.id] = f + es.index[filters.PendingLogsSubscription][f.id] = f + } else { + es.index[f.typ][f.id] = f + } + close(f.installed) + + case f := <-es.uninstall: + if f.typ == filters.MinedAndPendingLogsSubscription { + // the type are logs and pending logs subscriptions + delete(es.index[filters.LogsSubscription], f.id) + delete(es.index[filters.PendingLogsSubscription], f.id) + } else { + delete(es.index[f.typ], f.id) + } + close(f.err) + // System stopped + case <-es.txsSub.Err(): + return + case <-es.logsSub.Err(): + return + // case <-es.rmLogsSub.Err(): + // return + case <-es.pendingLogsSub.Err(): + return + case <-es.chainSub.Err(): + return + } + } + // }() +} + +// Subscription defines a wrapper for the private subscription +type Subscription struct { + id rpc.ID + typ filters.Type + event string + created time.Time + logsCrit filters.FilterCriteria + logs chan []*ethtypes.Log + hashes chan []common.Hash + headers chan *ethtypes.Header + installed chan struct{} // closed when the filter is installed + eventCh <-chan coretypes.ResultEvent + err chan error +} + +// ID returns the underlying subscription RPC identifier. +func (s Subscription) ID() rpc.ID { + return s.id +} + +// Unsubscribe to the current subscription from Tendermint Websocket. It sends an error to the +// subscription error channel if unsubscription fails. +func (s *Subscription) Unsubscribe(es *EventSystem) { + if err := es.client.Unsubscribe(es.ctx, string(s.ID()), s.event); err != nil { + s.err <- err + } + + go func() { + defer func() { + log.Println("successfully unsubscribed to event", s.event) + }() + + uninstallLoop: + for { + // write uninstall request and consume logs/hashes. This prevents + // the eventLoop broadcast method to deadlock when writing to the + // filter event channel while the subscription loop is waiting for + // this method to return (and thus not reading these events). + select { + case es.uninstall <- s: + break uninstallLoop + case <-s.logs: + case <-s.hashes: + case <-s.headers: + } + } + }() +} + +// Err returns the error channel +func (s *Subscription) Err() <-chan error { + return s.err +} diff --git a/rpc/filters.go b/rpc/filters.go new file mode 100644 index 0000000000..2a94040dd4 --- /dev/null +++ b/rpc/filters.go @@ -0,0 +1,242 @@ +package rpc + +import ( + "context" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/bloombits" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/eth/filters" +) + +// Filter can be used to retrieve and filter logs. +type Filter struct { + backend FiltersBackend + criteria filters.FilterCriteria + matcher *bloombits.Matcher +} + +// NewBlockFilter creates a new filter which directly inspects the contents of +// a block to figure out whether it is interesting or not. +func NewBlockFilter(backend FiltersBackend, criteria filters.FilterCriteria) *Filter { + // Create a generic filter and convert it into a block filter + return newFilter(backend, criteria, nil) +} + +// NewRangeFilter creates a new filter which uses a bloom filter on blocks to +// figure out whether a particular block is interesting or not. +func NewRangeFilter(backend FiltersBackend, begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter { + // Flatten the address and topic filter clauses into a single bloombits filter + // system. Since the bloombits are not positional, nil topics are permitted, + // which get flattened into a nil byte slice. + var filtersBz [][][]byte // nolint: prealloc + if len(addresses) > 0 { + filter := make([][]byte, len(addresses)) + for i, address := range addresses { + filter[i] = address.Bytes() + } + filtersBz = append(filtersBz, filter) + } + + for _, topicList := range topics { + filter := make([][]byte, len(topicList)) + for i, topic := range topicList { + filter[i] = topic.Bytes() + } + filtersBz = append(filtersBz, filter) + } + + size, _ := backend.BloomStatus() + + // Create a generic filter and convert it into a range filter + criteria := filters.FilterCriteria{ + FromBlock: big.NewInt(begin), + ToBlock: big.NewInt(end), + Addresses: addresses, + Topics: topics, + } + + return newFilter(backend, criteria, bloombits.NewMatcher(size, filtersBz)) +} + +// newFilter returns a new Filter +func newFilter(backend FiltersBackend, criteria filters.FilterCriteria, matcher *bloombits.Matcher) *Filter { + return &Filter{ + backend: backend, + criteria: criteria, + matcher: matcher, + } +} + +// Logs searches the blockchain for matching log entries, returning all from the +// first block that contains matches, updating the start of the filter accordingly. +func (f *Filter) Logs(_ context.Context) ([]*ethtypes.Log, error) { + logs := []*ethtypes.Log{} + var err error + + // If we're doing singleton block filtering, execute and return + if f.criteria.BlockHash != nil && f.criteria.BlockHash != (&common.Hash{}) { + header, err := f.backend.HeaderByHash(*f.criteria.BlockHash) + if err != nil { + return nil, err + } + if header == nil { + return nil, fmt.Errorf("unknown block header %s", f.criteria.BlockHash.String()) + } + return f.blockLogs(header) + } + + // Figure out the limits of the filter range + header, err := f.backend.HeaderByNumber(LatestBlockNumber) + if err != nil { + return nil, err + } + + if header == nil || header.Number == nil { + return nil, nil + } + + head := header.Number.Int64() + if f.criteria.FromBlock.Int64() == -1 { + f.criteria.FromBlock = big.NewInt(head) + } + if f.criteria.ToBlock.Int64() == -1 { + f.criteria.ToBlock = big.NewInt(head) + } + + for i := f.criteria.FromBlock.Int64(); i <= f.criteria.ToBlock.Int64(); i++ { + block, err := f.backend.GetBlockByNumber(BlockNumber(i), true) + if err != nil { + return logs, err + } + + txs, ok := block["transactions"].([]common.Hash) + if !ok || len(txs) == 0 { + continue + } + + logsMatched := f.checkMatches(txs) + logs = append(logs, logsMatched...) + } + + return logs, nil +} + +// blockLogs returns the logs matching the filter criteria within a single block. +func (f *Filter) blockLogs(header *ethtypes.Header) ([]*ethtypes.Log, error) { + if !bloomFilter(header.Bloom, f.criteria.Addresses, f.criteria.Topics) { + return []*ethtypes.Log{}, nil + } + + logsList, err := f.backend.GetLogs(header.Hash()) + if err != nil { + return []*ethtypes.Log{}, err + } + + var unfiltered []*ethtypes.Log // nolint: prealloc + for _, logs := range logsList { + unfiltered = append(unfiltered, logs...) + } + logs := filterLogs(unfiltered, nil, nil, f.criteria.Addresses, f.criteria.Topics) + if len(logs) == 0 { + return []*ethtypes.Log{}, nil + } + return logs, nil +} + +// checkMatches checks if the logs from the a list of transactions transaction +// contain any log events that match the filter criteria. This function is +// called when the bloom filter signals a potential match. +func (f *Filter) checkMatches(transactions []common.Hash) []*ethtypes.Log { + unfiltered := []*ethtypes.Log{} + for _, tx := range transactions { + logs, err := f.backend.GetTransactionLogs(tx) + if err != nil { + // ignore error if transaction didn't set any logs (eg: when tx type is not + // MsgEthereumTx or MsgEthermint) + continue + } + + unfiltered = append(unfiltered, logs...) + } + + return filterLogs(unfiltered, f.criteria.FromBlock, f.criteria.ToBlock, f.criteria.Addresses, f.criteria.Topics) +} + +// filterLogs creates a slice of logs matching the given criteria. +// [] -> anything +// [A] -> A in first position of log topics, anything after +// [null, B] -> anything in first position, B in second position +// [A, B] -> A in first position and B in second position +// [[A, B], [A, B]] -> A or B in first position, A or B in second position +func filterLogs(logs []*ethtypes.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*ethtypes.Log { + var ret []*ethtypes.Log +Logs: + for _, log := range logs { + if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber { + continue + } + if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber { + continue + } + if len(addresses) > 0 && !includes(addresses, log.Address) { + continue + } + // If the to filtered topics is greater than the amount of topics in logs, skip. + if len(topics) > len(log.Topics) { + continue + } + for i, sub := range topics { + match := len(sub) == 0 // empty rule set == wildcard + for _, topic := range sub { + if log.Topics[i] == topic { + match = true + break + } + } + if !match { + continue Logs + } + } + ret = append(ret, log) + } + return ret +} + +func includes(addresses []common.Address, a common.Address) bool { + for _, addr := range addresses { + if addr == a { + return true + } + } + + return false +} + +func bloomFilter(bloom ethtypes.Bloom, addresses []common.Address, topics [][]common.Hash) bool { + var included bool + if len(addresses) > 0 { + for _, addr := range addresses { + if ethtypes.BloomLookup(bloom, addr) { + included = true + break + } + } + if !included { + return false + } + } + + for _, sub := range topics { + included = len(sub) == 0 // empty rule set == wildcard + for _, topic := range sub { + if ethtypes.BloomLookup(bloom, topic) { + included = true + break + } + } + } + return included +} diff --git a/rpc/net_api.go b/rpc/net_api.go new file mode 100644 index 0000000000..2d0659305e --- /dev/null +++ b/rpc/net_api.go @@ -0,0 +1,35 @@ +package rpc + +import ( + "fmt" + + "github.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" + ethermint "github.com/cosmos/ethermint/types" +) + +// PublicNetAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec. +type PublicNetAPI struct { + networkVersion uint64 +} + +// NewPublicNetAPI creates an instance of the public Net Web3 API. +func NewPublicNetAPI(_ context.CLIContext) *PublicNetAPI { + chainID := viper.GetString(flags.FlagChainID) + // parse the chainID from a integer string + chainIDEpoch, err := ethermint.ParseChainID(chainID) + if err != nil { + panic(err) + } + + return &PublicNetAPI{ + networkVersion: chainIDEpoch.Uint64(), + } +} + +// Version returns the current ethereum protocol version. +func (s *PublicNetAPI) Version() string { + return fmt.Sprintf("%d", s.networkVersion) +} diff --git a/rpc/personal_api.go b/rpc/personal_api.go new file mode 100644 index 0000000000..439d170396 --- /dev/null +++ b/rpc/personal_api.go @@ -0,0 +1,265 @@ +package rpc + +import ( + "bytes" + "context" + "fmt" + "os" + "time" + + "github.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + + emintcrypto "github.com/cosmos/ethermint/crypto" + params "github.com/cosmos/ethermint/rpc/args" +) + +// PersonalEthAPI is the personal_ prefixed set of APIs in the Web3 JSON-RPC spec. +type PersonalEthAPI struct { + ethAPI *PublicEthAPI + keyInfos []keys.Info // all keys, both locked and unlocked. unlocked keys are stored in ethAPI.keys +} + +// NewPersonalEthAPI creates an instance of the public Personal Eth API. +func NewPersonalEthAPI(ethAPI *PublicEthAPI) *PersonalEthAPI { + api := &PersonalEthAPI{ + ethAPI: ethAPI, + } + + infos, err := api.getKeybaseInfo() + if err != nil { + return api + } + + api.keyInfos = infos + return api +} + +func (e *PersonalEthAPI) getKeybaseInfo() ([]keys.Info, error) { + e.ethAPI.keybaseLock.Lock() + defer e.ethAPI.keybaseLock.Unlock() + + if e.ethAPI.cliCtx.Keybase == nil { + keybase, err := keys.NewKeyring( + sdk.KeyringServiceName(), + viper.GetString(flags.FlagKeyringBackend), + viper.GetString(flags.FlagHome), + e.ethAPI.cliCtx.Input, + emintcrypto.EthSecp256k1Options()..., + ) + if err != nil { + return nil, err + } + + e.ethAPI.cliCtx.Keybase = keybase + } + + return e.ethAPI.cliCtx.Keybase.List() +} + +// ImportRawKey armors and encrypts a given raw hex encoded ECDSA key and stores it into the key directory. +// The name of the key will have the format "personal_", where is the total number of +// keys stored on the keyring. +// NOTE: The key will be both armored and encrypted using the same passphrase. +func (e *PersonalEthAPI) ImportRawKey(privkey, password string) (common.Address, error) { + e.ethAPI.logger.Debug("personal_importRawKey") + priv, err := crypto.HexToECDSA(privkey) + if err != nil { + return common.Address{}, err + } + + privKey := emintcrypto.PrivKeySecp256k1(crypto.FromECDSA(priv)) + + armor := mintkey.EncryptArmorPrivKey(privKey, password, emintcrypto.EthSecp256k1Type) + + // ignore error as we only care about the length of the list + list, _ := e.ethAPI.cliCtx.Keybase.List() + privKeyName := fmt.Sprintf("personal_%d", len(list)) + + if err := e.ethAPI.cliCtx.Keybase.ImportPrivKey(privKeyName, armor, password); err != nil { + return common.Address{}, err + } + + addr := common.BytesToAddress(privKey.PubKey().Address().Bytes()) + + info, err := e.ethAPI.cliCtx.Keybase.Get(privKeyName) + if err != nil { + return common.Address{}, err + } + + // append key and info to be able to lock and list the account + //e.ethAPI.keys = append(e.ethAPI.keys, privKey) + e.keyInfos = append(e.keyInfos, info) + e.ethAPI.logger.Info("key successfully imported", "name", privKeyName, "address", addr.String()) + + return addr, nil +} + +// ListAccounts will return a list of addresses for accounts this node manages. +func (e *PersonalEthAPI) ListAccounts() ([]common.Address, error) { + e.ethAPI.logger.Debug("personal_listAccounts") + addrs := []common.Address{} + for _, info := range e.keyInfos { + addressBytes := info.GetPubKey().Address().Bytes() + addrs = append(addrs, common.BytesToAddress(addressBytes)) + } + + return addrs, nil +} + +// LockAccount will lock the account associated with the given address when it's unlocked. +// It removes the key corresponding to the given address from the API's local keys. +func (e *PersonalEthAPI) LockAccount(address common.Address) bool { + e.ethAPI.logger.Debug("personal_lockAccount", "address", address.String()) + + for i, key := range e.ethAPI.keys { + if !bytes.Equal(key.PubKey().Address().Bytes(), address.Bytes()) { + continue + } + + tmp := make([]emintcrypto.PrivKeySecp256k1, len(e.ethAPI.keys)-1) + copy(tmp[:i], e.ethAPI.keys[:i]) + copy(tmp[i:], e.ethAPI.keys[i+1:]) + e.ethAPI.keys = tmp + + e.ethAPI.logger.Debug("account unlocked", "address", address.String()) + return true + } + + return false +} + +// NewAccount will create a new account and returns the address for the new account. +func (e *PersonalEthAPI) NewAccount(password string) (common.Address, error) { + e.ethAPI.logger.Debug("personal_newAccount") + _, err := e.getKeybaseInfo() + if err != nil { + return common.Address{}, err + } + + name := "key_" + time.Now().UTC().Format(time.RFC3339) + info, _, err := e.ethAPI.cliCtx.Keybase.CreateMnemonic(name, keys.English, password, emintcrypto.EthSecp256k1) + if err != nil { + return common.Address{}, err + } + + e.keyInfos = append(e.keyInfos, info) + + addr := common.BytesToAddress(info.GetPubKey().Address().Bytes()) + e.ethAPI.logger.Info("Your new key was generated", "address", addr.String()) + e.ethAPI.logger.Info("Please backup your key file!", "path", os.Getenv("HOME")+"/.ethermintcli/"+name) + e.ethAPI.logger.Info("Please remember your password!") + return addr, nil +} + +// UnlockAccount will unlock the account associated with the given address with +// the given password for duration seconds. If duration is nil it will use a +// default of 300 seconds. It returns an indication if the account was unlocked. +// It exports the private key corresponding to the given address from the keyring and stores it in the API's local keys. +func (e *PersonalEthAPI) UnlockAccount(_ context.Context, addr common.Address, password string, _ *uint64) (bool, error) { // nolint: interfacer + e.ethAPI.logger.Debug("personal_unlockAccount", "address", addr.String()) + // TODO: use duration + + var keyInfo keys.Info + + for _, info := range e.keyInfos { + addressBytes := info.GetPubKey().Address().Bytes() + if bytes.Equal(addressBytes, addr[:]) { + keyInfo = info + break + } + } + + if keyInfo == nil { + return false, fmt.Errorf("cannot find key with given address %s", addr.String()) + } + + // exporting private key only works on local keys + if keyInfo.GetType() != keys.TypeLocal { + return false, fmt.Errorf("key type must be %s, got %s", keys.TypeLedger.String(), keyInfo.GetType().String()) + } + + privKey, err := e.ethAPI.cliCtx.Keybase.ExportPrivateKeyObject(keyInfo.GetName(), password) + if err != nil { + return false, err + } + + emintKey, ok := privKey.(emintcrypto.PrivKeySecp256k1) + if !ok { + return false, fmt.Errorf("invalid private key type: %T", privKey) + } + + e.ethAPI.keys = append(e.ethAPI.keys, emintKey) + e.ethAPI.logger.Debug("account unlocked", "address", addr.String()) + return true, nil +} + +// SendTransaction will create a transaction from the given arguments and +// tries to sign it with the key associated with args.To. If the given password isn't +// able to decrypt the key it fails. +func (e *PersonalEthAPI) SendTransaction(_ context.Context, args params.SendTxArgs, _ string) (common.Hash, error) { + return e.ethAPI.SendTransaction(args) +} + +// Sign calculates an Ethereum ECDSA signature for: +// keccak256("\x19Ethereum Signed Message:\n" + len(message) + message)) +// +// Note, the produced signature conforms to the secp256k1 curve R, S and V values, +// where the V value will be 27 or 28 for legacy reasons. +// +// The key used to calculate the signature is decrypted with the given password. +// +// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign +func (e *PersonalEthAPI) Sign(_ context.Context, data hexutil.Bytes, addr common.Address, _ string) (hexutil.Bytes, error) { + e.ethAPI.logger.Debug("personal_sign", "data", data, "address", addr.String()) + + key, ok := checkKeyInKeyring(e.ethAPI.keys, addr) + if !ok { + return nil, fmt.Errorf("cannot find key with address %s", addr.String()) + } + + sig, err := crypto.Sign(accounts.TextHash(data), key.ToECDSA()) + if err != nil { + return nil, err + } + + sig[crypto.RecoveryIDOffset] += 27 // transform V from 0/1 to 27/28 + return sig, nil +} + +// EcRecover returns the address for the account that was used to create the signature. +// Note, this function is compatible with eth_sign and personal_sign. As such it recovers +// the address of: +// hash = keccak256("\x19Ethereum Signed Message:\n"${message length}${message}) +// addr = ecrecover(hash, signature) +// +// Note, the signature must conform to the secp256k1 curve R, S and V values, where +// the V value must be 27 or 28 for legacy reasons. +// +// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecove +func (e *PersonalEthAPI) EcRecover(_ context.Context, data, sig hexutil.Bytes) (common.Address, error) { + e.ethAPI.logger.Debug("personal_ecRecover", "data", data, "sig", sig) + + if len(sig) != crypto.SignatureLength { + return common.Address{}, fmt.Errorf("signature must be %d bytes long", crypto.SignatureLength) + } + if sig[crypto.RecoveryIDOffset] != 27 && sig[crypto.RecoveryIDOffset] != 28 { + return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)") + } + sig[crypto.RecoveryIDOffset] -= 27 // Transform yellow paper V from 27/28 to 0/1 + + pubkey, err := crypto.SigToPub(accounts.TextHash(data), sig) + if err != nil { + return common.Address{}, err + } + return crypto.PubkeyToAddress(*pubkey), nil +} diff --git a/rpc/types.go b/rpc/types.go new file mode 100644 index 0000000000..935947a291 --- /dev/null +++ b/rpc/types.go @@ -0,0 +1,67 @@ +package rpc + +import ( + "fmt" + "math" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// BlockNumber represents decoding hex string to block values +type BlockNumber int64 + +const ( + // LatestBlockNumber mapping from "latest" to 0 for tm query + LatestBlockNumber = BlockNumber(0) + + // EarliestBlockNumber mapping from "earliest" to 1 for tm query (earliest query not supported) + EarliestBlockNumber = BlockNumber(1) +) + +// NewBlockNumber creates a new BlockNumber instance. +func NewBlockNumber(n *big.Int) BlockNumber { + return BlockNumber(n.Int64()) +} + +// UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports: +// - "latest", "earliest" or "pending" as string arguments +// - the block number +// Returned errors: +// - an invalid block number error when the given argument isn't a known strings +// - an out of range error when the given block number is either too little or too large +func (bn *BlockNumber) UnmarshalJSON(data []byte) error { + input := strings.TrimSpace(string(data)) + if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' { + input = input[1 : len(input)-1] + } + + switch input { + case "earliest": + *bn = EarliestBlockNumber + return nil + case "latest": + *bn = LatestBlockNumber + return nil + case "pending": + *bn = LatestBlockNumber + return nil + } + + blckNum, err := hexutil.DecodeUint64(input) + if err != nil { + return err + } + if blckNum > math.MaxInt64 { + return fmt.Errorf("blocknumber too high") + } + + *bn = BlockNumber(blckNum) + return nil +} + +// Int64 converts block number to primitive type +func (bn BlockNumber) Int64() int64 { + return (int64)(bn) +} diff --git a/server/rpc/apis.go b/rpc/web3_api.go similarity index 57% rename from server/rpc/apis.go rename to rpc/web3_api.go index 3286b5afca..edff98fdfa 100644 --- a/server/rpc/apis.go +++ b/rpc/web3_api.go @@ -1,34 +1,14 @@ -// Package rpc contains RPC handler methods and utilities to start -// Ethermint's Web3-compatibly JSON-RPC server. package rpc import ( "github.com/cosmos/ethermint/version" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/rpc" ) -// GetRPCAPIs returns the master list of public APIs for use with -// StartHTTPEndpoint. -func GetRPCAPIs() []rpc.API { - return []rpc.API{ - { - Namespace: "web3", - Version: "1.0", - Service: NewPublicWeb3API(), - }, - { - Namespace: "eth", - Version: "1.0", - Service: NewPublicEthAPI(), - }, - } -} - // PublicWeb3API is the web3_ prefixed set of APIs in the Web3 JSON-RPC spec. -type PublicWeb3API struct { -} +type PublicWeb3API struct{} // NewPublicWeb3API creates an instance of the Web3 API. func NewPublicWeb3API() *PublicWeb3API { diff --git a/rpc/websockets.go b/rpc/websockets.go new file mode 100644 index 0000000000..26117824a8 --- /dev/null +++ b/rpc/websockets.go @@ -0,0 +1,542 @@ +package rpc + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "math/big" + "net" + "net/http" + "os" + "strings" + "sync" + + "github.com/gorilla/mux" + "github.com/gorilla/websocket" + "github.com/spf13/viper" + + "github.com/tendermint/tendermint/libs/log" + coretypes "github.com/tendermint/tendermint/rpc/core/types" + tmtypes "github.com/tendermint/tendermint/types" + + evmtypes "github.com/cosmos/ethermint/x/evm/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/eth/filters" + "github.com/ethereum/go-ethereum/rpc" + + context "github.com/cosmos/cosmos-sdk/client/context" +) + +type SubscriptionResponseJSON struct { + Jsonrpc string `json:"jsonrpc"` + Result interface{} `json:"result"` + ID float64 `json:"id"` +} + +type SubscriptionNotification struct { + Jsonrpc string `json:"jsonrpc"` + Method string `json:"method"` + Params *SubscriptionResult `json:"params"` +} + +type SubscriptionResult struct { + Subscription rpc.ID `json:"subscription"` + Result interface{} `json:"result"` +} + +type ErrorResponseJSON struct { + Jsonrpc string `json:"jsonrpc"` + Error *ErrorMessageJSON `json:"error"` + ID *big.Int `json:"id"` +} + +type ErrorMessageJSON struct { + Code *big.Int `json:"code"` + Message string `json:"message"` +} + +type websocketsServer struct { + rpcAddr string // listen address of rest-server + wsAddr string // listen address of ws server + api *pubSubAPI + logger log.Logger +} + +func newWebsocketsServer(cliCtx context.CLIContext, wsAddr string) *websocketsServer { + return &websocketsServer{ + rpcAddr: viper.GetString("laddr"), + wsAddr: wsAddr, + api: newPubSubAPI(cliCtx), + logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "websocket-server"), + } +} + +func (s *websocketsServer) start() { + ws := mux.NewRouter() + ws.Handle("/", s) + + go func() { + err := http.ListenAndServe(fmt.Sprintf(":%s", s.wsAddr), ws) + if err != nil { + s.logger.Error("http error:", err) + } + }() +} + +func (s *websocketsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + var upgrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + return true + }, + } + + wsConn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + s.logger.Error("websocket upgrade failed; error:", err) + return + } + + s.readLoop(wsConn) +} + +func (s *websocketsServer) sendErrResponse(conn *websocket.Conn, msg string) { + res := &ErrorResponseJSON{ + Jsonrpc: "2.0", + Error: &ErrorMessageJSON{ + Code: big.NewInt(-32600), + Message: msg, + }, + ID: nil, + } + err := conn.WriteJSON(res) + if err != nil { + s.logger.Error("websocket failed write message", "error", err) + } +} + +func (s *websocketsServer) readLoop(wsConn *websocket.Conn) { + for { + _, mb, err := wsConn.ReadMessage() + if err != nil { + _ = wsConn.Close() + s.logger.Error("failed to read message; error", err) + return + } + + var msg map[string]interface{} + err = json.Unmarshal(mb, &msg) + if err != nil { + s.sendErrResponse(wsConn, "invalid request") + continue + } + + // check if method == eth_subscribe or eth_unsubscribe + method := msg["method"] + if method.(string) == "eth_subscribe" { + params := msg["params"].([]interface{}) + if len(params) == 0 { + s.sendErrResponse(wsConn, "invalid parameters") + continue + } + + id, err := s.api.subscribe(wsConn, params) + if err != nil { + s.sendErrResponse(wsConn, err.Error()) + continue + } + + res := &SubscriptionResponseJSON{ + Jsonrpc: "2.0", + ID: 1, + Result: id, + } + + err = wsConn.WriteJSON(res) + if err != nil { + s.logger.Error("failed to write json response", err) + continue + } + + continue + } else if method.(string) == "eth_unsubscribe" { + ids, ok := msg["params"].([]interface{}) + if _, idok := ids[0].(string); !ok || !idok { + s.sendErrResponse(wsConn, "invalid parameters") + continue + } + + ok = s.api.unsubscribe(rpc.ID(ids[0].(string))) + res := &SubscriptionResponseJSON{ + Jsonrpc: "2.0", + ID: 1, + Result: ok, + } + + err = wsConn.WriteJSON(res) + if err != nil { + s.logger.Error("failed to write json response", err) + continue + } + + continue + } + + // otherwise, call the usual rpc server to respond + err = s.tcpGetAndSendResponse(wsConn, mb) + if err != nil { + s.sendErrResponse(wsConn, err.Error()) + } + } +} + +// tcpGetAndSendResponse connects to the rest-server over tcp, posts a JSON-RPC request, and sends the response +// to the client over websockets +func (s *websocketsServer) tcpGetAndSendResponse(conn *websocket.Conn, mb []byte) error { + addr := strings.Split(s.rpcAddr, "tcp://") + if len(addr) != 2 { + return fmt.Errorf("invalid laddr %s", s.rpcAddr) + } + + tcpConn, err := net.Dial("tcp", addr[1]) + if err != nil { + return fmt.Errorf("cannot connect to %s; %s", s.rpcAddr, err) + } + + buf := &bytes.Buffer{} + _, err = buf.Write(mb) + if err != nil { + return fmt.Errorf("failed to write message; %s", err) + } + + req, err := http.NewRequest("POST", s.rpcAddr, buf) + if err != nil { + return fmt.Errorf("failed to request; %s", err) + } + + req.Header.Set("Content-Type", "application/json;") + err = req.Write(tcpConn) + if err != nil { + return fmt.Errorf("failed to write to rest-server; %s", err) + } + + respBytes, err := ioutil.ReadAll(tcpConn) + if err != nil { + return fmt.Errorf("error reading response from rest-server; %s", err) + } + + respbuf := &bytes.Buffer{} + respbuf.Write(respBytes) + resp, err := http.ReadResponse(bufio.NewReader(respbuf), req) + if err != nil { + return fmt.Errorf("could not read response; %s", err) + } + + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("could not read body from response; %s", err) + } + + var wsSend interface{} + err = json.Unmarshal(body, &wsSend) + if err != nil { + return fmt.Errorf("failed to unmarshal rest-server response; %s", err) + } + + return conn.WriteJSON(wsSend) +} + +type wsSubscription struct { + sub *Subscription + unsubscribed chan struct{} // closed when unsubscribing + conn *websocket.Conn +} + +// pubSubAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec +type pubSubAPI struct { + cliCtx context.CLIContext + events *EventSystem + filtersMu sync.Mutex + filters map[rpc.ID]*wsSubscription + logger log.Logger +} + +// newPubSubAPI creates an instance of the ethereum PubSub API. +func newPubSubAPI(cliCtx context.CLIContext) *pubSubAPI { + return &pubSubAPI{ + cliCtx: cliCtx, + events: NewEventSystem(cliCtx.Client), + filters: make(map[rpc.ID]*wsSubscription), + logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "websocket-client"), + } +} + +func (api *pubSubAPI) subscribe(conn *websocket.Conn, params []interface{}) (rpc.ID, error) { + method, ok := params[0].(string) + if !ok { + return "0", fmt.Errorf("invalid parameters") + } + + switch method { + case "newHeads": + // TODO: handle extra params + return api.subscribeNewHeads(conn) + case "logs": + if len(params) > 1 { + return api.subscribeLogs(conn, params[1]) + } + + return api.subscribeLogs(conn, nil) + case "newPendingTransactions": + return api.subscribePendingTransactions(conn) + case "syncing": + return api.subscribeSyncing(conn) + default: + return "0", fmt.Errorf("unsupported method %s", method) + } +} + +func (api *pubSubAPI) unsubscribe(id rpc.ID) bool { + api.filtersMu.Lock() + defer api.filtersMu.Unlock() + + if api.filters[id] == nil { + return false + } + + close(api.filters[id].unsubscribed) + delete(api.filters, id) + return true +} + +func (api *pubSubAPI) subscribeNewHeads(conn *websocket.Conn) (rpc.ID, error) { + sub, _, err := api.events.SubscribeNewHeads() + if err != nil { + return "", fmt.Errorf("error creating block filter: %s", err.Error()) + } + + unsubscribed := make(chan struct{}) + api.filtersMu.Lock() + api.filters[sub.ID()] = &wsSubscription{ + sub: sub, + conn: conn, + unsubscribed: unsubscribed, + } + api.filtersMu.Unlock() + + go func(headersCh <-chan coretypes.ResultEvent, errCh <-chan error) { + for { + select { + case event := <-headersCh: + data, _ := event.Data.(tmtypes.EventDataNewBlockHeader) + header := EthHeaderFromTendermint(data.Header) + + api.filtersMu.Lock() + if f, found := api.filters[sub.ID()]; found { + // write to ws conn + res := &SubscriptionNotification{ + Jsonrpc: "2.0", + Method: "eth_subscription", + Params: &SubscriptionResult{ + Subscription: sub.ID(), + Result: header, + }, + } + + err = f.conn.WriteJSON(res) + if err != nil { + api.logger.Error("error writing header") + } + } + api.filtersMu.Unlock() + case <-errCh: + api.filtersMu.Lock() + delete(api.filters, sub.ID()) + api.filtersMu.Unlock() + return + case <-unsubscribed: + return + } + } + }(sub.eventCh, sub.Err()) + + return sub.ID(), nil +} + +func (api *pubSubAPI) subscribeLogs(conn *websocket.Conn, extra interface{}) (rpc.ID, error) { + crit := filters.FilterCriteria{} + + if extra != nil { + params, ok := extra.(map[string]interface{}) + if !ok { + return "", fmt.Errorf("invalid criteria") + } + + if params["address"] != nil { + address, ok := params["address"].(string) + addresses, sok := params["address"].([]interface{}) + if !ok && !sok { + return "", fmt.Errorf("invalid address; must be address or array of addresses") + } + + if ok { + crit.Addresses = []common.Address{common.HexToAddress(address)} + } + + if sok { + crit.Addresses = []common.Address{} + for _, addr := range addresses { + address, ok := addr.(string) + if !ok { + return "", fmt.Errorf("invalid address") + } + + crit.Addresses = append(crit.Addresses, common.HexToAddress(address)) + } + } + } + + if params["topics"] != nil { + topics, ok := params["topics"].([]interface{}) + if !ok { + return "", fmt.Errorf("invalid topics") + } + + crit.Topics = [][]common.Hash{} + for _, topic := range topics { + tstr, ok := topic.(string) + if !ok { + return "", fmt.Errorf("invalid topics") + } + + h := common.HexToHash(tstr) + crit.Topics = append(crit.Topics, []common.Hash{h}) + } + } + } + + sub, _, err := api.events.SubscribeLogs(crit) + if err != nil { + return rpc.ID(""), err + } + + unsubscribed := make(chan struct{}) + api.filtersMu.Lock() + api.filters[sub.ID()] = &wsSubscription{ + sub: sub, + conn: conn, + unsubscribed: unsubscribed, + } + api.filtersMu.Unlock() + + go func(ch <-chan coretypes.ResultEvent, errCh <-chan error) { + for { + select { + case event := <-ch: + dataTx, ok := event.Data.(tmtypes.EventDataTx) + if !ok { + err = fmt.Errorf("invalid event data %T, expected EventDataTx", event.Data) + return + } + + var resultData evmtypes.ResultData + resultData, err = evmtypes.DecodeResultData(dataTx.TxResult.Result.Data) + if err != nil { + return + } + + logs := filterLogs(resultData.Logs, crit.FromBlock, crit.ToBlock, crit.Addresses, crit.Topics) + + api.filtersMu.Lock() + if f, found := api.filters[sub.ID()]; found { + // write to ws conn + res := &SubscriptionNotification{ + Jsonrpc: "2.0", + Method: "eth_subscription", + Params: &SubscriptionResult{ + Subscription: sub.ID(), + Result: logs, + }, + } + + err = f.conn.WriteJSON(res) + } + api.filtersMu.Unlock() + + if err != nil { + err = fmt.Errorf("failed to write header: %w", err) + return + } + case <-errCh: + api.filtersMu.Lock() + delete(api.filters, sub.ID()) + api.filtersMu.Unlock() + return + case <-unsubscribed: + return + } + } + }(sub.eventCh, sub.Err()) + + return sub.ID(), nil +} + +func (api *pubSubAPI) subscribePendingTransactions(conn *websocket.Conn) (rpc.ID, error) { + sub, _, err := api.events.SubscribePendingTxs() + if err != nil { + return "", fmt.Errorf("error creating block filter: %s", err.Error()) + } + + unsubscribed := make(chan struct{}) + api.filtersMu.Lock() + api.filters[sub.ID()] = &wsSubscription{ + sub: sub, + conn: conn, + unsubscribed: unsubscribed, + } + api.filtersMu.Unlock() + + go func(txsCh <-chan coretypes.ResultEvent, errCh <-chan error) { + for { + select { + case ev := <-txsCh: + data, _ := ev.Data.(tmtypes.EventDataTx) + txHash := common.BytesToHash(data.Tx.Hash()) + + api.filtersMu.Lock() + if f, found := api.filters[sub.ID()]; found { + // write to ws conn + res := &SubscriptionNotification{ + Jsonrpc: "2.0", + Method: "eth_subscription", + Params: &SubscriptionResult{ + Subscription: sub.ID(), + Result: txHash, + }, + } + + err = f.conn.WriteJSON(res) + } + api.filtersMu.Unlock() + + if err != nil { + err = fmt.Errorf("failed to write header: %w", err) + return + } + case <-errCh: + api.filtersMu.Lock() + delete(api.filters, sub.ID()) + api.filtersMu.Unlock() + } + } + }(sub.eventCh, sub.Err()) + + return sub.ID(), nil +} + +func (api *pubSubAPI) subscribeSyncing(conn *websocket.Conn) (rpc.ID, error) { + return "", nil +} diff --git a/scripts/contract-test.sh b/scripts/contract-test.sh new file mode 100644 index 0000000000..087676fabf --- /dev/null +++ b/scripts/contract-test.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +KEY="mykey" +TESTKEY="test" +CHAINID="ethermint-100" +MONIKER="localtestnet" + +# stop and remove existing daemon and client data and process(es) +rm -rf $PWD/.ethermint* +pkill -f "ethermint*" + +type "ethermintd" 2> /dev/null || make build-ethermint +type "ethermintcli" 2> /dev/null || make build-ethermint + +$PWD/build/ethermintcli config keyring-backend test + +# Set up config for CLI +$PWD/build/ethermintcli config chain-id $CHAINID +$PWD/build/ethermintcli config output json +$PWD/build/ethermintcli config indent true +$PWD/build/ethermintcli config trust-node true + +# if $KEY exists it should be deleted +$PWD/build/ethermintcli keys add $KEY + +# Set moniker and chain-id for Ethermint (Moniker can be anything, chain-id must be an integer) +$PWD/build/ethermintd init $MONIKER --chain-id $CHAINID + +# Change parameter token denominations to aphoton +cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["staking"]["params"]["bond_denom"]="aphoton"' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json +cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["crisis"]["constant_fee"]["denom"]="aphoton"' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json +cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["gov"]["deposit_params"]["min_deposit"][0]["denom"]="aphoton"' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json +cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["mint"]["params"]["mint_denom"]="aphoton"' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json + +# Enable faucet +cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["faucet"]["enable_faucet"]=true' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json + +# Allocate genesis accounts (cosmos formatted addresses) +$PWD/build/ethermintd add-genesis-account "$("$PWD"/build/ethermintcli keys show "$KEY$i" -a)" 100000000000000000000aphoton + +# Sign genesis transaction +$PWD/build/ethermintd gentx --name $KEY --amount=1000000000000000000aphoton --keyring-backend test + +# Collect genesis tx +$PWD/build/ethermintd collect-gentxs + +# Run this to ensure everything worked and that the genesis file is setup correctly +$PWD/build/ethermintd validate-genesis + +# Start the node (remove the --pruning=nothing flag if historical queries are not needed) in background and log to file +$PWD/build/ethermintd start --pruning=nothing --rpc.unsafe --log_level "main:info,state:info,mempool:info" --trace > ethermintd.log & + +sleep 1 + +# Start the rest server with unlocked faucet key in background and log to file +$PWD/build/ethermintcli rest-server --laddr "tcp://localhost:8545" --unlock-key $KEY --chain-id $CHAINID --trace > ethermintcli.log & + +solcjs --abi $PWD/tests-solidity/suites/basic/contracts/Counter.sol --bin -o $PWD/tests-solidity/suites/basic/counter +mv $PWD/tests-solidity/suites/basic/counter/*.abi $PWD/tests-solidity/suites/basic/counter/counter_sol.abi +mv $PWD/tests-solidity/suites/basic/counter/*.bin $PWD/tests-solidity/suites/basic/counter/counter_sol.bin + +ACCT=$(curl --fail --silent -X POST --data '{"jsonrpc":"2.0","method":"eth_accounts","params":[],"id":1}' -H "Content-Type: application/json" http://localhost:8545 | grep -o '\0x[^"]*' 2>&1) + +echo $ACCT + +curl -X POST --data '{"jsonrpc":"2.0","method":"personal_unlockAccount","params":["'$ACCT'", ""],"id":1}' -H "Content-Type: application/json" http://localhost:8545 + +PRIVKEY="$("$PWD"/build/ethermintcli keys unsafe-export-eth-key $KEY)" + +echo $PRIVKEY + +## need to get the private key from the account in order to check this functionality. +cd tests-solidity/suites/basic/ && go get && go run main.go $ACCT diff --git a/scripts/integration-test-all.sh b/scripts/integration-test-all.sh new file mode 100755 index 0000000000..0001f4aab5 --- /dev/null +++ b/scripts/integration-test-all.sh @@ -0,0 +1,175 @@ +#!/bin/bash + +# "stable" mode tests assume data is static +# "live" mode tests assume data dynamic + +SCRIPT=$(basename ${BASH_SOURCE[0]}) +TEST="" +QTD=1 +SLEEP_TIMEOUT=5 +TEST_QTD=1 + +#PORT AND RPC_PORT 3 initial digits, to be concat with a suffix later when node is initialized +RPC_PORT="854" +IP_ADDR="0.0.0.0" +MODE="rpc" + +KEY="mykey" +CHAINID="ethermint-2" +MONIKER="mymoniker" + +## default port prefixes for ethermintd +NODE_P2P_PORT="2660" +NODE_PORT="2663" +NODE_RPC_PORT="2666" + +usage() { + echo "Usage: $SCRIPT" + echo "Optional command line arguments" + echo "-t -- Test to run. eg: rpc" + echo "-q -- Quantity of nodes to run. eg: 3" + echo "-z -- Quantity of nodes to run tests against eg: 3" + echo "-s -- Sleep between operations in secs. eg: 5" + exit 1 +} + +while getopts "h?t:q:z:s:" args; do + case $args in + h|\?) + usage; + exit;; + t ) TEST=${OPTARG};; + q ) QTD=${OPTARG};; + z ) TEST_QTD=${OPTARG};; + s ) SLEEP_TIMEOUT=${OPTARG};; + esac +done + +set -euxo pipefail + +DATA_DIR=$(mktemp -d -t ethermint-datadir.XXXXX) + +if [[ ! "$DATA_DIR" ]]; then + echo "Could not create $DATA_DIR" + exit 1 +fi + +DATA_CLI_DIR=$(mktemp -d -t ethermint-cli-datadir.XXXXX) + +if [[ ! "$DATA_CLI_DIR" ]]; then + echo "Could not create $DATA_CLI_DIR" + exit 1 +fi + +# Compile ethermint +echo "compiling ethermint" +make build-ethermint + +# PID array declaration +arr=() + +# PID arraycli declaration +arrcli=() + +init_func() { + echo "create and add new keys" + "$PWD"/build/ethermintcli config keyring-backend test --home "$DATA_CLI_DIR$i" + "$PWD"/build/ethermintcli keys add $KEY"$i" --home "$DATA_CLI_DIR$i" --no-backup --chain-id $CHAINID + echo "init Ethermint with moniker=$MONIKER and chain-id=$CHAINID" + "$PWD"/build/ethermintd init $MONIKER --chain-id $CHAINID --home "$DATA_DIR$i" + echo "init ethermintcli with chain-id=$CHAINID and config it trust-node true" + "$PWD"/build/ethermintcli config chain-id $CHAINID --home "$DATA_CLI_DIR$i" + "$PWD"/build/ethermintcli config output json --home "$DATA_CLI_DIR$i" + "$PWD"/build/ethermintcli config indent true --home "$DATA_CLI_DIR$i" + "$PWD"/build/ethermintcli config trust-node true --home "$DATA_CLI_DIR$i" + echo "prepare genesis: Allocate genesis accounts" + "$PWD"/build/ethermintd add-genesis-account \ + "$("$PWD"/build/ethermintcli keys show "$KEY$i" -a --home "$DATA_CLI_DIR$i" )" 1000000000000000000aphoton,1000000000000000000stake \ + --home "$DATA_DIR$i" --home-client "$DATA_CLI_DIR$i" + echo "prepare genesis: Sign genesis transaction" + "$PWD"/build/ethermintd gentx --name $KEY"$i" --keyring-backend test --home "$DATA_DIR$i" --home-client "$DATA_CLI_DIR$i" + echo "prepare genesis: Collect genesis tx" + "$PWD"/build/ethermintd collect-gentxs --home "$DATA_DIR$i" + echo "prepare genesis: Run validate-genesis to ensure everything worked and that the genesis file is setup correctly" + "$PWD"/build/ethermintd validate-genesis --home "$DATA_DIR$i" +} + +start_func() { + echo "starting ethermint node $i in background ..." + "$PWD"/build/ethermintd start --pruning=nothing --rpc.unsafe --log_level "main:info,state:info,mempool:info" \ + --p2p.laddr tcp://$IP_ADDR:$NODE_P2P_PORT"$i" --address tcp://$IP_ADDR:$NODE_PORT"$i" --rpc.laddr tcp://$IP_ADDR:$NODE_RPC_PORT"$i" \ + --home "$DATA_DIR$i" \ + >"$DATA_DIR"/node"$i".log 2>&1 & disown + + ETHERMINT_PID=$! + echo "started ethermint node, pid=$ETHERMINT_PID" + # add PID to array + arr+=("$ETHERMINT_PID") +} + +start_cli_func() { + echo "starting ethermint node $i in background ..." + "$PWD"/build/ethermintcli rest-server --unlock-key $KEY"$i" --chain-id $CHAINID --trace \ + --laddr "tcp://localhost:$RPC_PORT$i" --node tcp://$IP_ADDR:$NODE_RPC_PORT"$i" \ + --home "$DATA_CLI_DIR$i" --read-timeout 30 --write-timeout 30 \ + >"$DATA_CLI_DIR"/cli"$i".log 2>&1 & disown + + ETHERMINT_CLI_PID=$! + echo "started ethermintcli node, pid=$ETHERMINT_CLI_PID" + # add PID to array + arrcli+=("$ETHERMINT_CLI_PID") +} + +# Run node with static blockchain database +# For loop N times +for i in $(seq 1 "$QTD"); do + init_func "$i" + start_func "$i" + sleep 1 + start_cli_func "$i" + echo "sleeping $SLEEP_TIMEOUT seconds for startup" + sleep "$SLEEP_TIMEOUT" + echo "done sleeping" +done + +echo "sleeping $SLEEP_TIMEOUT seconds before running tests ... " +sleep "$SLEEP_TIMEOUT" +echo "done sleeping" + +set +e + +if [[ -z $TEST || $TEST == "rpc" ]]; then + + for i in $(seq 1 "$TEST_QTD"); do + HOST_RPC=http://$IP_ADDR:$RPC_PORT"$i" + echo "going to test ethermint node $HOST_RPC ..." + MODE=$MODE HOST=$HOST_RPC go test ./tests/... -timeout=300s -v -short + + RPC_FAIL=$? + done + +fi + +stop_func() { + ETHERMINT_PID=$i + echo "shutting down node, pid=$ETHERMINT_PID ..." + + # Shutdown ethermint node + kill -9 "$ETHERMINT_PID" + wait "$ETHERMINT_PID" +} + + +for i in "${arrcli[@]}"; do + stop_func "$i" +done + +for i in "${arr[@]}"; do + stop_func "$i" +done + +if [[ (-z $TEST || $TEST == "rpc") && $RPC_FAIL -ne 0 ]]; then + exit $RPC_FAIL +else + exit 0 +fi diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh new file mode 100755 index 0000000000..0818824a41 --- /dev/null +++ b/scripts/protocgen.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -eo pipefail + +proto_dirs=$(find . -path ./third_party -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) +for dir in $proto_dirs; do + protoc \ + -I. \ + --gocosmos_out=plugins=interfacetype,paths=source_relative:. \ + $(find "${dir}" -name '*.proto') +done diff --git a/scripts/run-solidity-tests.sh b/scripts/run-solidity-tests.sh new file mode 100755 index 0000000000..c56afd1b6b --- /dev/null +++ b/scripts/run-solidity-tests.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +export GOPATH=~/go +export PATH=$PATH:$GOPATH/bin +go build -o ./build/ethermintd ./cmd/ethermintd +go build -o ./build/ethermintcli ./cmd/ethermintcli +mkdir $GOPATH/bin +cp ./build/ethermintd $GOPATH/bin +cp ./build/ethermintcli $GOPATH/bin + +CHAINID="ethermint-1337" + +cd tests-solidity + +if command -v yarn &> /dev/null; then + yarn install +else + curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - + echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list + sudo apt update && sudo apt install yarn + yarn install +fi + +chmod +x ./init-test-node.sh +./init-test-node.sh > ethermintd.log & +sleep 5 +ethermintcli rest-server --laddr "tcp://localhost:8545" --unlock-key localkey,user1,user2 --chain-id $CHAINID --trace --wsport 8546 > ethermintcli.log & + +cd suites/initializable +yarn test-ethermint + +ok=$? + +if (( $? != 0 )); then + echo "initializable test failed: exit code $?" +fi + +killall ethermintcli +killall ethermintd + +echo "Script exited with code $ok" +exit $ok + +# initializable-buidler fails on CI, re-add later + +./../../init-test-node.sh > ethermintd.log & +sleep 5 +ethermintcli rest-server --laddr "tcp://localhost:8545" --unlock-key localkey,user1,user2 --chain-id $CHAINID --trace --wsport 8546 > ethermintcli.log & + +cd ../initializable-buidler +yarn test-ethermint + +ok=$(($? + $ok)) + +if (( $? != 0 )); then + echo "initializable-buidler test failed: exit code $?" +fi + +killall ethermintcli +killall ethermintd + +echo "Script exited with code $ok" +exit $ok \ No newline at end of file diff --git a/scripts/start.sh b/scripts/start.sh new file mode 100644 index 0000000000..da1320edc2 --- /dev/null +++ b/scripts/start.sh @@ -0,0 +1,5 @@ +#!/bin/sh +ethermintd --home /ethermint/node$ID/ethermintd/ start > ethermintd.log & +sleep 5 +ethermintcli rest-server --laddr "tcp://localhost:8545" --chain-id "ethermint-7305661614933169792" --trace > ethermintcli.log & +tail -f /dev/null \ No newline at end of file diff --git a/server/rpc/apis_test.go b/server/rpc/apis_test.go deleted file mode 100644 index e014086284..0000000000 --- a/server/rpc/apis_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package rpc - -import ( - "context" - "testing" - "time" - - "github.com/cosmos/ethermint/version" - "github.com/ethereum/go-ethereum/rpc" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" -) - -type apisTestSuite struct { - suite.Suite - Stop context.CancelFunc - Port int -} - -func (s *apisTestSuite) SetupSuite() { - stop, port, err := startAPIServer() - require.Nil(s.T(), err, "unexpected error") - s.Stop = stop - s.Port = port -} - -func (s *apisTestSuite) TearDownSuite() { - s.Stop() -} - -func (s *apisTestSuite) TestPublicWeb3APIClientVersion() { - res, err := rpcCall(s.Port, "web3_clientVersion", []string{}) - require.Nil(s.T(), err, "unexpected error") - require.Equal(s.T(), version.ClientVersion(), res) -} - -func (s *apisTestSuite) TestPublicWeb3APISha3() { - res, err := rpcCall(s.Port, "web3_sha3", []string{"0x67656c6c6f20776f726c64"}) - require.Nil(s.T(), err, "unexpected error") - require.Equal(s.T(), "0x1b84adea42d5b7d192fd8a61a85b25abe0757e9a65cab1da470258914053823f", res) -} - -func (s *apisTestSuite) TestMiningAPIs() { - res, err := rpcCall(s.Port, "eth_mining", nil) - require.Nil(s.T(), err, "unexpected error") - require.Equal(s.T(), false, res) - - res, err = rpcCall(s.Port, "eth_hashrate", nil) - require.Nil(s.T(), err, "unexpected error") - require.Equal(s.T(), "0x0", res) -} - -func TestAPIsTestSuite(t *testing.T) { - suite.Run(t, new(apisTestSuite)) -} - -func startAPIServer() (context.CancelFunc, int, error) { - config := &Config{ - RPCAddr: "127.0.0.1", - RPCPort: randomPort(), - } - timeouts := rpc.HTTPTimeouts{ - ReadTimeout: 5 * time.Second, - WriteTimeout: 5 * time.Second, - IdleTimeout: 5 * time.Second, - } - - ctx, cancel := context.WithCancel(context.Background()) - - _, err := StartHTTPEndpoint(ctx, config, GetRPCAPIs(), timeouts) - if err != nil { - return cancel, 0, err - } - - return cancel, config.RPCPort, nil -} diff --git a/server/rpc/config.go b/server/rpc/config.go deleted file mode 100644 index 2c030f1b0d..0000000000 --- a/server/rpc/config.go +++ /dev/null @@ -1,16 +0,0 @@ -package rpc - -// Config contains configuration fields that determine the -// behavior of the RPC HTTP server. -type Config struct { - // EnableRPC defines whether or not to enable the RPC server - EnableRPC bool - // RPCAddr defines the IP address to listen on - RPCAddr string - // RPCPort defines the port to listen on - RPCPort int - // RPCCORSDomains defines list of domains to enable CORS headers for (used by browsers) - RPCCORSDomains []string - // RPCVhosts defines list of domains to listen on (useful if Tendermint is addressable via DNS) - RPCVHosts []string -} diff --git a/server/rpc/eth_api.go b/server/rpc/eth_api.go deleted file mode 100644 index fc212ff07a..0000000000 --- a/server/rpc/eth_api.go +++ /dev/null @@ -1,196 +0,0 @@ -package rpc - -import ( - "github.com/cosmos/ethermint/version" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/signer/core" - "math/big" -) - -// PublicEthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec. -type PublicEthAPI struct{} - -// NewPublicEthAPI creates an instance of the public ETH Web3 API. -func NewPublicEthAPI() *PublicEthAPI { - return &PublicEthAPI{} -} - -// ProtocolVersion returns the supported Ethereum protocol version. -func (e *PublicEthAPI) ProtocolVersion() string { - return version.ProtocolVersion -} - -// Syncing returns whether or not the current node is syncing with other peers. Returns false if not, or a struct -// outlining the state of the sync if it is. -func (e *PublicEthAPI) Syncing() interface{} { - return false -} - -// Coinbase returns this node's coinbase address. Not used in Ethermint. -func (e *PublicEthAPI) Coinbase() (addr common.Address) { - return -} - -// Mining returns whether or not this node is currently mining. Always false. -func (e *PublicEthAPI) Mining() bool { - return false -} - -// Hashrate returns the current node's hashrate. Always 0. -func (e *PublicEthAPI) Hashrate() hexutil.Uint64 { - return 0 -} - -// GasPrice returns the current gas price based on Ethermint's gas price oracle. -func (e *PublicEthAPI) GasPrice() *hexutil.Big { - out := big.NewInt(0) - return (*hexutil.Big)(out) -} - -// Accounts returns the list of accounts available to this node. -func (e *PublicEthAPI) Accounts() []common.Address { - return nil -} - -// BlockNumber returns the current block number. -func (e *PublicEthAPI) BlockNumber() *big.Int { - return big.NewInt(0) -} - -// GetBalance returns the provided account's balance up to the provided block number. -func (e *PublicEthAPI) GetBalance(address common.Address, blockNum rpc.BlockNumber) *hexutil.Big { - out := big.NewInt(0) - return (*hexutil.Big)(out) -} - -// GetStorageAt returns the contract storage at the given address, block number, and key. -func (e *PublicEthAPI) GetStorageAt(address common.Address, key string, blockNum rpc.BlockNumber) hexutil.Bytes { - return nil -} - -// GetTransactionCount returns the number of transactions at the given address up to the given block number. -func (e *PublicEthAPI) GetTransactionCount(address common.Address, blockNum rpc.BlockNumber) hexutil.Uint64 { - return 0 -} - -// GetBlockTransactionCountByHash returns the number of transactions in the block identified by hash. -func (e *PublicEthAPI) GetBlockTransactionCountByHash(hash common.Hash) hexutil.Uint { - return 0 -} - -// GetBlockTransactionCountByNumber returns the number of transactions in the block identified by number. -func (e *PublicEthAPI) GetBlockTransactionCountByNumber(blockNum rpc.BlockNumber) hexutil.Uint { - return 0 -} - -// GetUncleCountByBlockHash returns the number of uncles in the block idenfied by hash. Always zero. -func (e *PublicEthAPI) GetUncleCountByBlockHash(hash common.Hash) hexutil.Uint { - return 0 -} - -// GetUncleCountByBlockNumber returns the number of uncles in the block idenfied by number. Always zero. -func (e *PublicEthAPI) GetUncleCountByBlockNumber(blockNum rpc.BlockNumber) hexutil.Uint { - return 0 -} - -// GetCode returns the contract code at the given address and block number. -func (e *PublicEthAPI) GetCode(address common.Address, blockNumber rpc.BlockNumber) hexutil.Bytes { - return nil -} - -// Sign signs the provided data using the private key of address via Geth's signature standard. -func (e *PublicEthAPI) Sign(address common.Address, data hexutil.Bytes) hexutil.Bytes { - return nil -} - -// SendTransaction sends an Ethereum transaction. -func (e *PublicEthAPI) SendTransaction(args core.SendTxArgs) common.Hash { - var h common.Hash - return h -} - -// SendRawTransaction send a raw Ethereum transaction. -func (e *PublicEthAPI) SendRawTransaction(data hexutil.Bytes) common.Hash { - var h common.Hash - return h -} - -// CallArgs represents arguments to a smart contract call as provided by RPC clients. -type CallArgs struct { - From common.Address `json:"from"` - To common.Address `json:"to"` - Gas hexutil.Uint64 `json:"gas"` - GasPrice hexutil.Big `json:"gasPrice"` - Value hexutil.Big `json:"value"` - Data hexutil.Bytes `json:"data"` -} - -// Call performs a raw contract call. -func (e *PublicEthAPI) Call(args CallArgs, blockNum rpc.BlockNumber) hexutil.Bytes { - return nil -} - -// EstimateGas estimates gas usage for the given smart contract call. -func (e *PublicEthAPI) EstimateGas(args CallArgs, blockNum rpc.BlockNumber) hexutil.Uint64 { - return 0 -} - -// GetBlockByHash returns the block identified by hash. -func (e *PublicEthAPI) GetBlockByHash(hash common.Hash, fullTx bool) map[string]interface{} { - return nil -} - -// GetBlockByNumber returns the block identified by number. -func (e *PublicEthAPI) GetBlockByNumber(blockNum rpc.BlockNumber, fullTx bool) map[string]interface{} { - return nil -} - -// Transaction represents a transaction returned to RPC clients. -type Transaction struct { - BlockHash common.Hash `json:"blockHash"` - BlockNumber *hexutil.Big `json:"blockNumber"` - From common.Address `json:"from"` - Gas hexutil.Uint64 `json:"gas"` - GasPrice *hexutil.Big `json:"gasPrice"` - Hash common.Hash `json:"hash"` - Input hexutil.Bytes `json:"input"` - Nonce hexutil.Uint64 `json:"nonce"` - To *common.Address `json:"to"` - TransactionIndex hexutil.Uint `json:"transactionIndex"` - Value *hexutil.Big `json:"value"` - V *hexutil.Big `json:"v"` - R *hexutil.Big `json:"r"` - S *hexutil.Big `json:"s"` -} - -// GetTransactionByHash returns the transaction identified by hash. -func (e *PublicEthAPI) GetTransactionByHash(hash common.Hash) *Transaction { - return nil -} - -// GetTransactionByBlockHashAndIndex returns the transaction identified by hash and index. -func (e *PublicEthAPI) GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) *Transaction { - return nil -} - -// GetTransactionByBlockNumberAndIndex returns the transaction identified by number and index. -func (e *PublicEthAPI) GetTransactionByBlockNumberAndIndex(blockNumber rpc.BlockNumber, idx hexutil.Uint) *Transaction { - return nil -} - -// GetTransactionReceipt returns the transaction receipt identified by hash. -func (e *PublicEthAPI) GetTransactionReceipt(hash common.Hash) map[string]interface{} { - return nil -} - -// GetUncleByBlockHashAndIndex returns the uncle identified by hash and index. Always returns nil. -func (e *PublicEthAPI) GetUncleByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) map[string]interface{} { - return nil -} - -// GetUncleByBlockNumberAndIndex returns the uncle identified by number and index. Always returns nil. -func (e *PublicEthAPI) GetUncleByBlockNumberAndIndex(number hexutil.Uint, idx hexutil.Uint) map[string]interface{} { - return nil -} diff --git a/server/rpc/rpc.go b/server/rpc/rpc.go deleted file mode 100644 index 1b56f63a56..0000000000 --- a/server/rpc/rpc.go +++ /dev/null @@ -1,38 +0,0 @@ -package rpc - -import ( - "context" - "fmt" - - "github.com/ethereum/go-ethereum/rpc" -) - -// StartHTTPEndpoint starts the Tendermint Web3-compatible RPC layer. Consumes -// a Context for cancellation, a config struct, and a list of rpc.API interfaces -// that will be automatically wired into a JSON-RPC webserver. -func StartHTTPEndpoint(ctx context.Context, config *Config, apis []rpc.API, timeouts rpc.HTTPTimeouts) (*rpc.Server, error) { - uniqModules := make(map[string]string) - for _, api := range apis { - uniqModules[api.Namespace] = api.Namespace - } - - modules := make([]string, len(uniqModules)) - i := 0 - for k := range uniqModules { - modules[i] = k - i++ - } - - endpoint := fmt.Sprintf("%s:%d", config.RPCAddr, config.RPCPort) - _, server, err := rpc.StartHTTPEndpoint( - endpoint, apis, modules, config.RPCCORSDomains, config.RPCVHosts, timeouts, - ) - - go func() { - <-ctx.Done() - fmt.Println("Shutting down server.") - server.Stop() - }() - - return server, err -} diff --git a/server/rpc/rpc_test.go b/server/rpc/rpc_test.go deleted file mode 100644 index 19f47e33d3..0000000000 --- a/server/rpc/rpc_test.go +++ /dev/null @@ -1,94 +0,0 @@ -package rpc - -import ( - "context" - "encoding/json" - "fmt" - "io/ioutil" - "math/rand" - "net/http" - "strings" - "testing" - "time" - - "github.com/ethereum/go-ethereum/rpc" - "github.com/stretchr/testify/require" -) - -type TestService struct{} - -func (s *TestService) Foo(arg string) string { - return arg -} - -func TestStartHTTPEndpointStartStop(t *testing.T) { - config := &Config{ - RPCAddr: "127.0.0.1", - RPCPort: randomPort(), - } - - ctx, cancel := context.WithCancel(context.Background()) - - _, err := StartHTTPEndpoint( - ctx, config, []rpc.API{ - { - Namespace: "test", - Version: "1.0", - Service: &TestService{}, - Public: true, - }, - }, - rpc.HTTPTimeouts{ - ReadTimeout: 5 * time.Second, - WriteTimeout: 5 * time.Second, - IdleTimeout: 5 * time.Second, - }, - ) - require.Nil(t, err, "unexpected error") - - res, err := rpcCall(config.RPCPort, "test_foo", []string{"baz"}) - require.Nil(t, err, "unexpected error") - - resStr := res.(string) - require.Equal(t, "baz", resStr) - - cancel() - - _, err = rpcCall(config.RPCPort, "test_foo", []string{"baz"}) - require.NotNil(t, err) -} - -func rpcCall(port int, method string, params []string) (interface{}, error) { - parsedParams, err := json.Marshal(params) - if err != nil { - return nil, err - } - - fullBody := fmt.Sprintf( - `{ "id": 1, "jsonrpc": "2.0", "method": "%s", "params": %s }`, - method, string(parsedParams), - ) - - res, err := http.Post(fmt.Sprintf("http://127.0.0.1:%d", port), "application/json", strings.NewReader(fullBody)) - if err != nil { - return nil, err - } - - data, err := ioutil.ReadAll(res.Body) - if err != nil { - return nil, err - } - - var out map[string]interface{} - err = json.Unmarshal(data, &out) - if err != nil { - return nil, err - } - - result := out["result"].(interface{}) - return result, nil -} - -func randomPort() int { - return rand.Intn(65535-1025) + 1025 -} diff --git a/tests-solidity/.gitattributes b/tests-solidity/.gitattributes new file mode 100644 index 0000000000..52031de51c --- /dev/null +++ b/tests-solidity/.gitattributes @@ -0,0 +1 @@ +*.sol linguist-language=Solidity diff --git a/tests-solidity/.gitignore b/tests-solidity/.gitignore new file mode 100644 index 0000000000..7a3e49774e --- /dev/null +++ b/tests-solidity/.gitignore @@ -0,0 +1,5 @@ +# dependencies +node_modules/ + +# ignore package-lock files (only use yarn.lock) +package-lock.json diff --git a/tests-solidity/README.md b/tests-solidity/README.md new file mode 100644 index 0000000000..e39ef4c6a0 --- /dev/null +++ b/tests-solidity/README.md @@ -0,0 +1,102 @@ +# Solidity tests + +Increasingly difficult tests are provided: + +- [Basic](./suites/basic): simple Counter example, for basic calls, transactions, and events +- [Initialize](./suites/initialize): initialization contract and tests from [aragonOS](https://github.com/aragon/aragonOS) +- [Initialize (Buidler)](./suites/initialize-buidler): initialization contract and tests from [aragonOS](https://github.com/aragon/aragonOS), using [buidler](https://buidler.dev/) +- [Proxy](./suites/proxy): depositable delegate proxy contract and tests from [aragonOS](https://github.com/aragon/aragonOS) +- [Staking](./suites/staking): Staking contracts and full test suite from [aragon/staking](http://github.com/aragon/staking) + +### Quick start + +**Prerequisite**: in the repo's root, run `make install` to install the `ethermintd` and `ethermintcli` binaries. When done, come back to this directory. + +**Prerequisite**: install the individual solidity packages. They're set up as individual reops in a yarn monorepo workspace. Install them all via `yarn install`. + +To run the tests, start three terminals (or two, if you run `ethermintd` with `&`). + +In the first, run `ethermintd`: + +```sh +./init-test-node.sh +``` + +In the second, run `ethermintcli` as mentioned in the script's output: + +```sh +ethermintcli rest-server --laddr "tcp://localhost:8545" --unlock-key localkey,user1,user2 --chain-id "ethermint-1337" --trace --wsport 8546 +``` + +You will now have three ethereum accounts unlocked in the test node: + +- `0x3b7252d007059ffc82d16d022da3cbf9992d2f70` (Validator) +- `0xddd64b4712f7c8f1ace3c145c950339eddaf221d` (User 1) +- `0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0` (user 2) + +From here, in your other available terminal, go into any of the tests and run `yarn test-ethermint`. You should see `ethermintd` accepting transactions and producing blocks. You should be able to query for any transaction via: + +- `ethermintcli query tx ` +- `curl localhost:8545 -H "Content-Type:application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_getTransactionByHash","params":[""],"id":1}'` + +And obviously more, via the Ethereum JSON-RPC API). + +When in doubt, you can also run the tests against a Ganache instance via `yarn test-ganache`, to make sure they are behaving correctly. + +### Test node + +The [`init-test-node.sh`](./init-test-node.sh) script sets up ethermint with the following accounts: + +- `eth18de995q8qk0leqk3d5pzmg7tlxvj6tmsku084d` (Validator) + - `0x3b7252d007059ffc82d16d022da3cbf9992d2f70` +- `eth1mhtyk3cj7ly0rt8rc9zuj5pnnmw67gsapygwyq` (User 1) + - `0xddd64b4712f7c8f1ace3c145c950339eddaf221d` +- `eth1pa20g7lehr330vs5ent20slr3wyne4lsy8qae3` (user 2) + - `0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0` + +Each with roughly 100 ETH available (1e18 photon). + +Running `ethermintcli list keys` should output: + +```json +[ + { + "name": "localkey", + "type": "local", + "address": "eth18de995q8qk0leqk3d5pzmg7tlxvj6tmsku084d", + "pubkey": "ethpub1pfqnmk6pq3ycjs34vv4n6rkty89f6m02qcsal3ecdzn7a3uunx0e5ly0846pzg903hxf2zp5gq4grh8jcatcemfrscdfl797zhg5crkcsx43gujzppge3n" + }, + { + "name": "user1", + "type": "local", + "address": "eth1mhtyk3cj7ly0rt8rc9zuj5pnnmw67gsapygwyq", + "pubkey": "ethpub1pfqnmk6pq3wrkx6lh7uug8ss0thggact3n49m5gkmpca4vylldpur5qrept57e0rrxfmeq5mp5xt3cyf4kys53qcv66qxttv970das69hlpkf8cnyd2a2x" + }, + { + "name": "user2", + "type": "local", + "address": "eth1pa20g7lehr330vs5ent20slr3wyne4lsy8qae3", + "pubkey": "ethpub1pfqnmk6pq3art9y45zw5ntyktt2qrt0skmsl0ux9qwk8458ed3d8sgnrs99zlgvj3rt2vggvkh0x56hffugwsyddwqla48npx46pglgs6xhcqpall58tgn" + } +] +``` + +And running: + +```sh +curl localhost:8545 -H "Content-Type:application/json" -X POST --data '{"jsonrpc":"2.0","method":"eth_accounts","params":[],"id":1}' +``` + +Should output: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "result": [ + "0x3b7252d007059ffc82d16d022da3cbf9992d2f70", + "0xddd64b4712f7c8f1ace3c145c950339eddaf221d", + "0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0" + ] +} +``` diff --git a/tests-solidity/init-test-node.sh b/tests-solidity/init-test-node.sh new file mode 100755 index 0000000000..867081aeb5 --- /dev/null +++ b/tests-solidity/init-test-node.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +CHAINID="ethermint-1337" +MONIKER="localtestnet" + +VAL_KEY="localkey" +VAL_MNEMONIC="gesture inject test cycle original hollow east ridge hen combine junk child bacon zero hope comfort vacuum milk pitch cage oppose unhappy lunar seat" + +USER1_KEY="user1" +USER1_MNEMONIC="copper push brief egg scan entry inform record adjust fossil boss egg comic alien upon aspect dry avoid interest fury window hint race symptom" + +USER2_KEY="user2" +USER2_MNEMONIC="maximum display century economy unlock van census kite error heart snow filter midnight usage egg venture cash kick motor survey drastic edge muffin visual" + +# remove existing daemon and client +rm -rf ~/.ethermint* + +ethermintcli config keyring-backend test + +# Set up config for CLI +ethermintcli config chain-id $CHAINID +ethermintcli config output json +ethermintcli config indent true +ethermintcli config trust-node true + +# Import keys from mnemonics +echo $VAL_MNEMONIC | ethermintcli keys add $VAL_KEY --recover +echo $USER1_MNEMONIC | ethermintcli keys add $USER1_KEY --recover +echo $USER2_MNEMONIC | ethermintcli keys add $USER2_KEY --recover + +# Set moniker and chain-id for Ethermint (Moniker can be anything, chain-id must be an integer) +ethermintd init $MONIKER --chain-id $CHAINID + +# Allocate genesis accounts (cosmos formatted addresses) +ethermintd add-genesis-account $(ethermintcli keys show $VAL_KEY -a) 1000000000000000000000aphoton,10000000000000000stake +ethermintd add-genesis-account $(ethermintcli keys show $USER1_KEY -a) 1000000000000000000000aphoton,10000000000000000stake +ethermintd add-genesis-account $(ethermintcli keys show $USER2_KEY -a) 1000000000000000000000aphoton,10000000000000000stake + +# Sign genesis transaction +ethermintd gentx --name $VAL_KEY --keyring-backend test + +# Collect genesis tx +ethermintd collect-gentxs + +# Enable faucet +cat $HOME/.ethermintd/config/genesis.json | jq '.app_state["faucet"]["enable_faucet"]=true' > $HOME/.ethermintd/config/tmp_genesis.json && mv $HOME/.ethermintd/config/tmp_genesis.json $HOME/.ethermintd/config/genesis.json + +echo -e '\n\ntestnet faucet enabled' +echo -e 'to transfer tokens to your account address use:' +echo -e "ethermintcli tx faucet request 100aphoton --from $VAL_KEY\n" + + +# Run this to ensure everything worked and that the genesis file is setup correctly +ethermintd validate-genesis + +# Command to run the rest server in a different terminal/window +echo -e '\nrun the following command in a different terminal/window to run the REST server and JSON-RPC:' +echo -e "ethermintcli rest-server --laddr \"tcp://localhost:8545\" --wsport 8546 --unlock-key $VAL_KEY,$USER1_KEY,$USER2_KEY --chain-id $CHAINID --trace\n" + +# Start the node (remove the --pruning=nothing flag if historical queries are not needed) +ethermintd start --pruning=nothing --rpc.unsafe --log_level "main:info,state:info,mempool:info" --trace diff --git a/tests-solidity/package.json b/tests-solidity/package.json new file mode 100644 index 0000000000..3bccce39d0 --- /dev/null +++ b/tests-solidity/package.json @@ -0,0 +1,15 @@ +{ + "name": "tests-solidity", + "private": true, + "version": "1.0.0", + "author": "Aragon Association ", + "license": "GPL-3.0-or-later", + "workspaces": { + "packages": [ + "suites/*" + ], + "nohoist": [ + "**/@aragon/contract-helpers-test" + ] + } +} diff --git a/tests-solidity/suites/basic/contracts/Counter.sol b/tests-solidity/suites/basic/contracts/Counter.sol new file mode 100644 index 0000000000..351eb4cacf --- /dev/null +++ b/tests-solidity/suites/basic/contracts/Counter.sol @@ -0,0 +1,24 @@ +pragma solidity ^0.5.11; + +contract Counter { + uint256 counter = 0; + string internal constant ERROR_TOO_LOW = "COUNTER_TOO_LOW"; + event Changed(uint256 counter); + event Added(uint256 counter); + + function add() public { + counter++; + emit Added(counter); + emit Changed(counter); + } + + function subtract() public { + require(counter > 0, ERROR_TOO_LOW); + counter--; + emit Changed(counter); + } + + function getCounter() public view returns (uint256) { + return counter; + } +} diff --git a/tests-solidity/suites/basic/contracts/test/Migrations.sol b/tests-solidity/suites/basic/contracts/test/Migrations.sol new file mode 100644 index 0000000000..f4661c6608 --- /dev/null +++ b/tests-solidity/suites/basic/contracts/test/Migrations.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.22 <0.8.0; + +contract Migrations { + address public owner = msg.sender; + uint public last_completed_migration; + + modifier restricted() { + require( + msg.sender == owner, + "This function is restricted to the contract's owner" + ); + _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } +} diff --git a/tests-solidity/suites/basic/main.go b/tests-solidity/suites/basic/main.go new file mode 100644 index 0000000000..93d3276820 --- /dev/null +++ b/tests-solidity/suites/basic/main.go @@ -0,0 +1,154 @@ +package main + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "time" + + "github.com/ethereum/go-ethereum/common/hexutil" +) + +var ( + HOST = os.Getenv("HOST") + HOME = os.Getenv("PWD") +) + +type Request struct { + Version string `json:"jsonrpc"` + Method string `json:"method"` + Params interface{} `json:"params"` + ID int `json:"id"` +} + +type RPCError struct { + Code int `json:"code"` + Message string `json:"message"` + Data interface{} `json:"data,omitempty"` +} + +type Response struct { + Error *RPCError `json:"error"` + ID int `json:"id"` + Result json.RawMessage `json:"result,omitempty"` +} + +func createRequest(method string, params interface{}) Request { + return Request{ + Version: "2.0", + Method: method, + Params: params, + ID: 1, + } +} + +func getTransactionReceipt(hash hexutil.Bytes) (map[string]interface{}, error) { + param := []string{hash.String()} + rpcRes, err := call("eth_getTransactionReceipt", param) + if err != nil { + return nil, err + } + + receipt := make(map[string]interface{}) + err = json.Unmarshal(rpcRes.Result, &receipt) + if err != nil { + return nil, err + } + + return receipt, nil +} + +func waitForReceipt(hash hexutil.Bytes) (map[string]interface{}, error) { + for i := 0; i < 10; i++ { + receipt, err := getTransactionReceipt(hash) + if receipt != nil { + return receipt, err + } else if err != nil { + return nil, err + } + + time.Sleep(time.Second) + } + return nil, errors.New("cound not find transaction on chain") +} + +func call(method string, params interface{}) (*Response, error) { + if HOST == "" { + HOST = "http://localhost:8545" + } + + req, err := json.Marshal(createRequest(method, params)) + if err != nil { + return nil, err + } + + var rpcRes *Response + time.Sleep(1000000 * time.Nanosecond) + /* #nosec */ + res, err := http.Post(HOST, "application/json", bytes.NewBuffer(req)) + if err != nil { + return nil, err + } + + decoder := json.NewDecoder(res.Body) + rpcRes = new(Response) + err = decoder.Decode(&rpcRes) + if err != nil { + return nil, err + } + + err = res.Body.Close() + if err != nil { + return nil, err + } + + return rpcRes, nil +} + +func main() { + dat, err := ioutil.ReadFile(HOME + "/counter/counter_sol.bin") + if err != nil { + log.Fatal(err) + } + + param := make([]map[string]string, 1) + param[0] = make(map[string]string) + param[0]["from"] = os.Args[1] + param[0]["data"] = "0x" + string(dat) + + txRPCRes, err := call("eth_sendTransaction", param) + if err != nil { + log.Fatal(err) + } + + var hash hexutil.Bytes + err = json.Unmarshal(txRPCRes.Result, &hash) + if err != nil { + log.Fatal(err) + } + fmt.Println("Contract TX hash: ", hash) + + receipt, err := waitForReceipt(hash) + if err != nil { + log.Fatal(err) + } + + /* + //test for bad hash + testhash, err := hexutil.Decode("0xe146d95c74a48e730bf825c2a3dcbce8122b8a463bc15bcbb38b9c195402f0a5") + if err != nil { + log.Fatal(err) + } + receipt, err := waitForReceipt(testhash) + if err != nil { + log.Fatal(err) + } + */ + + fmt.Println("receipt: ", receipt) +} diff --git a/tests-solidity/suites/basic/migrations/1_initial_migration.js b/tests-solidity/suites/basic/migrations/1_initial_migration.js new file mode 100644 index 0000000000..16a7ba52f4 --- /dev/null +++ b/tests-solidity/suites/basic/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +const Migrations = artifacts.require("Migrations"); + +module.exports = function (deployer) { + deployer.deploy(Migrations); +}; diff --git a/tests-solidity/suites/basic/package.json b/tests-solidity/suites/basic/package.json new file mode 100644 index 0000000000..778c51f81a --- /dev/null +++ b/tests-solidity/suites/basic/package.json @@ -0,0 +1,17 @@ +{ + "name": "basic", + "version": "1.0.0", + "author": "Aragon Association ", + "license": "GPL-3.0-or-later", + "scripts": { + "test-ganache": "yarn truffle test", + "test-ethermint": "yarn truffle test --network ethermint" + }, + "devDependencies": { + "truffle": "^5.1.42", + "web3": "^1.2.11" + }, + "dependencies": { + "truffle": "^5.1.43" + } +} diff --git a/tests-solidity/suites/basic/test/.gitkeep b/tests-solidity/suites/basic/test/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests-solidity/suites/basic/test/counter.js b/tests-solidity/suites/basic/test/counter.js new file mode 100644 index 0000000000..f8dc42d6c0 --- /dev/null +++ b/tests-solidity/suites/basic/test/counter.js @@ -0,0 +1,80 @@ +const Counter = artifacts.require("Counter") + +contract('Counter', ([one, two, three]) => { + console.log(one, two, three) + let counter + + beforeEach(async() => { + counter = await Counter.new() + console.log('Counter:', counter.address) + + console.log('Current eth:') + console.log(' - ', await web3.eth.getBalance(one)) + console.log(' - ', await web3.eth.getBalance(two)) + console.log(' - ', await web3.eth.getBalance(three)) + console.log('') + }) + + it('should add', async() => { + const balanceOne = await web3.eth.getBalance(one) + const balanceTwo = await web3.eth.getBalance(two) + const balanceThree = await web3.eth.getBalance(three) + + let count + + await counter.add({ from: one }) + count = await counter.getCounter() + console.log(count.toString()) + assert.equal(count, '1', 'Counter should be 1') + assert.notEqual(balanceOne, await web3.eth.getBalance(one), `${one}'s balance should be different`) + + await counter.add({ from: two }) + count = await counter.getCounter() + console.log(count.toString()) + assert.equal(count, '2', 'Counter should be 2') + assert.notEqual(balanceTwo, await web3.eth.getBalance(two), `${two}'s balance should be different`) + + await counter.add({ from: three }) + count = await counter.getCounter() + console.log(count.toString()) + assert.equal(count, '3', 'Counter should be 3') + assert.notEqual(balanceThree, await web3.eth.getBalance(three), `${three}'s balance should be different`) + }) + + it('should subtract', async() => { + let count + + await counter.add() + count = await counter.getCounter() + console.log(count.toString()) + assert.equal(count, '1', 'Counter should be 1') + + // Use receipt to ensure logs are emitted + const receipt = await counter.subtract() + count = await counter.getCounter() + console.log(count.toString()) + console.log() + console.log('Subtract tx receipt:', receipt) + assert.equal(count, '0', 'Counter should be 0') + assert.equal(receipt.logs[0].event, 'Changed', "Should have emitted 'Changed' event") + assert.equal(receipt.logs[0].args.counter, '0', "Should have emitted 'Changed' event with counter being 0") + + // Check lifecycle of events + const contract = new web3.eth.Contract(counter.abi, counter.address) + const allEvents = await contract.getPastEvents("allEvents", { fromBlock: 0, toBlock: 'latest' }) + const changedEvents = await contract.getPastEvents("Changed", { fromBlock: 0, toBlock: 'latest' }) + console.log('allEvents', allEvents) + console.log('changedEvents', changedEvents) + assert.equal(allEvents.length, 3) + assert.equal(changedEvents.length, 2) + + try { + await counter.subtract() + assert.fail('Subtracting past 0 should have reverted') + } catch (err) { + console.log() + console.log('Passed -- was expecting error') + console.log('Error:', err) + } + }) +}) diff --git a/tests-solidity/suites/basic/truffle-config.js b/tests-solidity/suites/basic/truffle-config.js new file mode 100644 index 0000000000..c7f781a8a3 --- /dev/null +++ b/tests-solidity/suites/basic/truffle-config.js @@ -0,0 +1,17 @@ +module.exports = { + networks: { + // Development network is just left as truffle's default settings + ethermint: { + host: "127.0.0.1", // Localhost (default: none) + port: 8545, // Standard Ethereum port (default: none) + network_id: "*", // Any network (default: none) + gas: 5000000, // Gas sent with each transaction + gasPrice: 1000000000, // 1 gwei (in wei) + }, + }, + compilers: { + solc: { + version: "0.5.17", + }, + }, +} diff --git a/tests-solidity/suites/initializable-buidler/.gitignore b/tests-solidity/suites/initializable-buidler/.gitignore new file mode 100644 index 0000000000..22a48f231e --- /dev/null +++ b/tests-solidity/suites/initializable-buidler/.gitignore @@ -0,0 +1,3 @@ +# Buidler +artifacts +cache diff --git a/tests-solidity/suites/initializable-buidler/buidler.config.js b/tests-solidity/suites/initializable-buidler/buidler.config.js new file mode 100644 index 0000000000..d9e096bc16 --- /dev/null +++ b/tests-solidity/suites/initializable-buidler/buidler.config.js @@ -0,0 +1,28 @@ +const { usePlugin } = require('@nomiclabs/buidler/config') + +usePlugin("@nomiclabs/buidler-ganache") +usePlugin('@nomiclabs/buidler-truffle5') + +module.exports = { + networks: { + // Development network is just left as truffle's default settings + ganache: { + url: 'http://localhost:8545', + gasLimit: 5000000, + gasPrice: 1000000000, // 1 gwei (in wei) + defaultBalanceEther: 100 + }, + ethermint: { + url: 'http://localhost:8545', + gasLimit: 5000000, // Gas sent with each transaction + gasPrice: 1000000000, // 1 gwei (in wei) + }, + }, + solc: { + version: '0.4.24', + optimizer: { + enabled: true, + runs: 10000, + }, + }, +} diff --git a/tests-solidity/suites/initializable-buidler/contracts/Initializable.sol b/tests-solidity/suites/initializable-buidler/contracts/Initializable.sol new file mode 100644 index 0000000000..ba20d88f5b --- /dev/null +++ b/tests-solidity/suites/initializable-buidler/contracts/Initializable.sol @@ -0,0 +1,59 @@ +/* + * SPDX-License-Identifier: MIT + */ + +pragma solidity ^0.4.24; + +import "./TimeHelpers.sol"; +import "./UnstructuredStorage.sol"; + + +contract Initializable is TimeHelpers { + using UnstructuredStorage for bytes32; + + // keccak256("aragonOS.initializable.initializationBlock") + bytes32 internal constant INITIALIZATION_BLOCK_POSITION = 0xebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e; + + string private constant ERROR_ALREADY_INITIALIZED = "INIT_ALREADY_INITIALIZED"; + string private constant ERROR_NOT_INITIALIZED = "INIT_NOT_INITIALIZED"; + + modifier onlyInit { + require(getInitializationBlock() == 0, ERROR_ALREADY_INITIALIZED); + _; + } + + modifier isInitialized { + require(hasInitialized(), ERROR_NOT_INITIALIZED); + _; + } + + /** + * @return Block number in which the contract was initialized + */ + function getInitializationBlock() public view returns (uint256) { + return INITIALIZATION_BLOCK_POSITION.getStorageUint256(); + } + + /** + * @return Whether the contract has been initialized by the time of the current block + */ + function hasInitialized() public view returns (bool) { + uint256 initializationBlock = getInitializationBlock(); + return initializationBlock != 0 && getBlockNumber() >= initializationBlock; + } + + /** + * @dev Function to be called by top level contract after initialization has finished. + */ + function initialized() internal onlyInit { + INITIALIZATION_BLOCK_POSITION.setStorageUint256(getBlockNumber()); + } + + /** + * @dev Function to be called by top level contract after initialization to enable the contract + * at a future block number rather than immediately. + */ + function initializedAt(uint256 _blockNumber) internal onlyInit { + INITIALIZATION_BLOCK_POSITION.setStorageUint256(_blockNumber); + } +} diff --git a/tests-solidity/suites/initializable-buidler/contracts/Petrifiable.sol b/tests-solidity/suites/initializable-buidler/contracts/Petrifiable.sol new file mode 100644 index 0000000000..abd2ef9da1 --- /dev/null +++ b/tests-solidity/suites/initializable-buidler/contracts/Petrifiable.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.4.24; + +import "./Initializable.sol"; + + +contract Petrifiable is Initializable { + // Use block UINT256_MAX (which should be never) as the initializable date + uint256 internal constant PETRIFIED_BLOCK = uint256(-1); + + function isPetrified() public view returns (bool) { + return getInitializationBlock() == PETRIFIED_BLOCK; + } + + /** + * @dev Function to be called by top level contract to prevent being initialized. + * Useful for freezing base contracts when they're used behind proxies. + */ + function petrify() internal onlyInit { + initializedAt(PETRIFIED_BLOCK); + } +} diff --git a/tests-solidity/suites/initializable-buidler/contracts/TimeHelpers.sol b/tests-solidity/suites/initializable-buidler/contracts/TimeHelpers.sol new file mode 100644 index 0000000000..5a5ffa528b --- /dev/null +++ b/tests-solidity/suites/initializable-buidler/contracts/TimeHelpers.sol @@ -0,0 +1,44 @@ +pragma solidity ^0.4.24; + +import "./Uint256Helpers.sol"; + + +contract TimeHelpers { + using Uint256Helpers for uint256; + + /** + * @dev Returns the current block number. + * Using a function rather than `block.number` allows us to easily mock the block number in + * tests. + */ + function getBlockNumber() internal view returns (uint256) { + return block.number; + } + + /** + * @dev Returns the current block number, converted to uint64. + * Using a function rather than `block.number` allows us to easily mock the block number in + * tests. + */ + function getBlockNumber64() internal view returns (uint64) { + return getBlockNumber().toUint64(); + } + + /** + * @dev Returns the current timestamp. + * Using a function rather than `block.timestamp` allows us to easily mock it in + * tests. + */ + function getTimestamp() internal view returns (uint256) { + return block.timestamp; // solium-disable-line security/no-block-members + } + + /** + * @dev Returns the current timestamp, converted to uint64. + * Using a function rather than `block.timestamp` allows us to easily mock it in + * tests. + */ + function getTimestamp64() internal view returns (uint64) { + return getTimestamp().toUint64(); + } +} diff --git a/tests-solidity/suites/initializable-buidler/contracts/Uint256Helpers.sol b/tests-solidity/suites/initializable-buidler/contracts/Uint256Helpers.sol new file mode 100644 index 0000000000..919b2e9550 --- /dev/null +++ b/tests-solidity/suites/initializable-buidler/contracts/Uint256Helpers.sol @@ -0,0 +1,13 @@ +pragma solidity ^0.4.24; + + +library Uint256Helpers { + uint256 private constant MAX_UINT64 = uint64(-1); + + string private constant ERROR_NUMBER_TOO_BIG = "UINT64_NUMBER_TOO_BIG"; + + function toUint64(uint256 a) internal pure returns (uint64) { + require(a <= MAX_UINT64, ERROR_NUMBER_TOO_BIG); + return uint64(a); + } +} diff --git a/tests-solidity/suites/initializable-buidler/contracts/UnstructuredStorage.sol b/tests-solidity/suites/initializable-buidler/contracts/UnstructuredStorage.sol new file mode 100644 index 0000000000..7dddc05422 --- /dev/null +++ b/tests-solidity/suites/initializable-buidler/contracts/UnstructuredStorage.sol @@ -0,0 +1,36 @@ +pragma solidity ^0.4.24; + + +library UnstructuredStorage { + function getStorageBool(bytes32 position) internal view returns (bool data) { + assembly { data := sload(position) } + } + + function getStorageAddress(bytes32 position) internal view returns (address data) { + assembly { data := sload(position) } + } + + function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) { + assembly { data := sload(position) } + } + + function getStorageUint256(bytes32 position) internal view returns (uint256 data) { + assembly { data := sload(position) } + } + + function setStorageBool(bytes32 position, bool data) internal { + assembly { sstore(position, data) } + } + + function setStorageAddress(bytes32 position, address data) internal { + assembly { sstore(position, data) } + } + + function setStorageBytes32(bytes32 position, bytes32 data) internal { + assembly { sstore(position, data) } + } + + function setStorageUint256(bytes32 position, uint256 data) internal { + assembly { sstore(position, data) } + } +} diff --git a/tests-solidity/suites/initializable-buidler/contracts/test/InitializableMock.sol b/tests-solidity/suites/initializable-buidler/contracts/test/InitializableMock.sol new file mode 100644 index 0000000000..c5983db901 --- /dev/null +++ b/tests-solidity/suites/initializable-buidler/contracts/test/InitializableMock.sol @@ -0,0 +1,15 @@ +pragma solidity 0.4.24; + +import "../Initializable.sol"; +import "../Petrifiable.sol"; + + +contract LifecycleMock is Initializable, Petrifiable { + function initializeMock() public { + initialized(); + } + + function petrifyMock() public { + petrify(); + } +} diff --git a/tests-solidity/suites/initializable-buidler/package.json b/tests-solidity/suites/initializable-buidler/package.json new file mode 100644 index 0000000000..dd908f6588 --- /dev/null +++ b/tests-solidity/suites/initializable-buidler/package.json @@ -0,0 +1,19 @@ +{ + "name": "initializable-buidler", + "version": "1.0.0", + "author": "Aragon Association ", + "license": "GPL-3.0-or-later", + "scripts": { + "test-ganache": "yarn buidler test --network ganache", + "test-ethermint": "yarn buidler test --network ethermint" + }, + "devDependencies": { + "@aragon/contract-helpers-test": "^0.1.0", + "@nomiclabs/buidler": "^1.4.3", + "@nomiclabs/buidler-ganache": "^1.3.3", + "@nomiclabs/buidler-truffle5": "^1.3.4", + "@nomiclabs/buidler-web3": "^1.3.4", + "chai": "^4.2.0", + "web3": "^1.2.11" + } +} diff --git a/tests-solidity/suites/initializable-buidler/test/lifecycle.js b/tests-solidity/suites/initializable-buidler/test/lifecycle.js new file mode 100644 index 0000000000..cc643786ff --- /dev/null +++ b/tests-solidity/suites/initializable-buidler/test/lifecycle.js @@ -0,0 +1,74 @@ +const { assertRevert } = require('@aragon/contract-helpers-test/src/asserts') + +// Mocks +const LifecycleMock = artifacts.require('LifecycleMock') + +const ERRORS = { + INIT_ALREADY_INITIALIZED: 'INIT_ALREADY_INITIALIZED', +} + +contract('Lifecycle', () => { + let lifecycle + + beforeEach(async () => { + lifecycle = await LifecycleMock.new() + }) + + it('is not initialized', async () => { + assert.isFalse(await lifecycle.hasInitialized(), 'should not be initialized') + }) + + it('is not petrified', async () => { + assert.isFalse(await lifecycle.isPetrified(), 'should not be petrified') + }) + + context('> Initialized', () => { + beforeEach(async () => { + await lifecycle.initializeMock() + }) + + it('is initialized', async () => { + assert.isTrue(await lifecycle.hasInitialized(), 'should be initialized') + }) + + it('is not petrified', async () => { + assert.isFalse(await lifecycle.isPetrified(), 'should not be petrified') + }) + + it('has correct initialization block', async () => { + assert.equal(await lifecycle.getInitializationBlock(), await web3.eth.getBlockNumber(), 'initialization block should be correct') + }) + + it('cannot be re-initialized', async () => { + await assertRevert(lifecycle.initializeMock()/*, ERRORS.INIT_ALREADY_INITIALIZED*/) + }) + + it('cannot be petrified', async () => { + await assertRevert(lifecycle.petrifyMock()/*, ERRORS.INIT_ALREADY_INITIALIZED*/) + }) + }) + + context('> Petrified', () => { + beforeEach(async () => { + await lifecycle.petrifyMock() + }) + + it('is not initialized', async () => { + assert.isFalse(await lifecycle.hasInitialized(), 'should not be initialized') + }) + + it('is petrified', async () => { + assert.isTrue(await lifecycle.isPetrified(), 'should be petrified') + }) + + it('cannot be petrified again', async () => { + await assertRevert(lifecycle.petrifyMock()/*, ERRORS.INIT_ALREADY_INITIALIZED*/) + }) + + it('has initialization block in the future', async () => { + const petrifiedBlock = await lifecycle.getInitializationBlock() + const blockNumber = await web3.eth.getBlockNumber() + assert.isTrue(petrifiedBlock.gt(blockNumber), 'petrified block should be in the future') + }) + }) +}) diff --git a/tests-solidity/suites/initializable/contracts/Initializable.sol b/tests-solidity/suites/initializable/contracts/Initializable.sol new file mode 100644 index 0000000000..ba20d88f5b --- /dev/null +++ b/tests-solidity/suites/initializable/contracts/Initializable.sol @@ -0,0 +1,59 @@ +/* + * SPDX-License-Identifier: MIT + */ + +pragma solidity ^0.4.24; + +import "./TimeHelpers.sol"; +import "./UnstructuredStorage.sol"; + + +contract Initializable is TimeHelpers { + using UnstructuredStorage for bytes32; + + // keccak256("aragonOS.initializable.initializationBlock") + bytes32 internal constant INITIALIZATION_BLOCK_POSITION = 0xebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e; + + string private constant ERROR_ALREADY_INITIALIZED = "INIT_ALREADY_INITIALIZED"; + string private constant ERROR_NOT_INITIALIZED = "INIT_NOT_INITIALIZED"; + + modifier onlyInit { + require(getInitializationBlock() == 0, ERROR_ALREADY_INITIALIZED); + _; + } + + modifier isInitialized { + require(hasInitialized(), ERROR_NOT_INITIALIZED); + _; + } + + /** + * @return Block number in which the contract was initialized + */ + function getInitializationBlock() public view returns (uint256) { + return INITIALIZATION_BLOCK_POSITION.getStorageUint256(); + } + + /** + * @return Whether the contract has been initialized by the time of the current block + */ + function hasInitialized() public view returns (bool) { + uint256 initializationBlock = getInitializationBlock(); + return initializationBlock != 0 && getBlockNumber() >= initializationBlock; + } + + /** + * @dev Function to be called by top level contract after initialization has finished. + */ + function initialized() internal onlyInit { + INITIALIZATION_BLOCK_POSITION.setStorageUint256(getBlockNumber()); + } + + /** + * @dev Function to be called by top level contract after initialization to enable the contract + * at a future block number rather than immediately. + */ + function initializedAt(uint256 _blockNumber) internal onlyInit { + INITIALIZATION_BLOCK_POSITION.setStorageUint256(_blockNumber); + } +} diff --git a/tests-solidity/suites/initializable/contracts/Petrifiable.sol b/tests-solidity/suites/initializable/contracts/Petrifiable.sol new file mode 100644 index 0000000000..abd2ef9da1 --- /dev/null +++ b/tests-solidity/suites/initializable/contracts/Petrifiable.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.4.24; + +import "./Initializable.sol"; + + +contract Petrifiable is Initializable { + // Use block UINT256_MAX (which should be never) as the initializable date + uint256 internal constant PETRIFIED_BLOCK = uint256(-1); + + function isPetrified() public view returns (bool) { + return getInitializationBlock() == PETRIFIED_BLOCK; + } + + /** + * @dev Function to be called by top level contract to prevent being initialized. + * Useful for freezing base contracts when they're used behind proxies. + */ + function petrify() internal onlyInit { + initializedAt(PETRIFIED_BLOCK); + } +} diff --git a/tests-solidity/suites/initializable/contracts/TimeHelpers.sol b/tests-solidity/suites/initializable/contracts/TimeHelpers.sol new file mode 100644 index 0000000000..5a5ffa528b --- /dev/null +++ b/tests-solidity/suites/initializable/contracts/TimeHelpers.sol @@ -0,0 +1,44 @@ +pragma solidity ^0.4.24; + +import "./Uint256Helpers.sol"; + + +contract TimeHelpers { + using Uint256Helpers for uint256; + + /** + * @dev Returns the current block number. + * Using a function rather than `block.number` allows us to easily mock the block number in + * tests. + */ + function getBlockNumber() internal view returns (uint256) { + return block.number; + } + + /** + * @dev Returns the current block number, converted to uint64. + * Using a function rather than `block.number` allows us to easily mock the block number in + * tests. + */ + function getBlockNumber64() internal view returns (uint64) { + return getBlockNumber().toUint64(); + } + + /** + * @dev Returns the current timestamp. + * Using a function rather than `block.timestamp` allows us to easily mock it in + * tests. + */ + function getTimestamp() internal view returns (uint256) { + return block.timestamp; // solium-disable-line security/no-block-members + } + + /** + * @dev Returns the current timestamp, converted to uint64. + * Using a function rather than `block.timestamp` allows us to easily mock it in + * tests. + */ + function getTimestamp64() internal view returns (uint64) { + return getTimestamp().toUint64(); + } +} diff --git a/tests-solidity/suites/initializable/contracts/Uint256Helpers.sol b/tests-solidity/suites/initializable/contracts/Uint256Helpers.sol new file mode 100644 index 0000000000..919b2e9550 --- /dev/null +++ b/tests-solidity/suites/initializable/contracts/Uint256Helpers.sol @@ -0,0 +1,13 @@ +pragma solidity ^0.4.24; + + +library Uint256Helpers { + uint256 private constant MAX_UINT64 = uint64(-1); + + string private constant ERROR_NUMBER_TOO_BIG = "UINT64_NUMBER_TOO_BIG"; + + function toUint64(uint256 a) internal pure returns (uint64) { + require(a <= MAX_UINT64, ERROR_NUMBER_TOO_BIG); + return uint64(a); + } +} diff --git a/tests-solidity/suites/initializable/contracts/UnstructuredStorage.sol b/tests-solidity/suites/initializable/contracts/UnstructuredStorage.sol new file mode 100644 index 0000000000..7dddc05422 --- /dev/null +++ b/tests-solidity/suites/initializable/contracts/UnstructuredStorage.sol @@ -0,0 +1,36 @@ +pragma solidity ^0.4.24; + + +library UnstructuredStorage { + function getStorageBool(bytes32 position) internal view returns (bool data) { + assembly { data := sload(position) } + } + + function getStorageAddress(bytes32 position) internal view returns (address data) { + assembly { data := sload(position) } + } + + function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) { + assembly { data := sload(position) } + } + + function getStorageUint256(bytes32 position) internal view returns (uint256 data) { + assembly { data := sload(position) } + } + + function setStorageBool(bytes32 position, bool data) internal { + assembly { sstore(position, data) } + } + + function setStorageAddress(bytes32 position, address data) internal { + assembly { sstore(position, data) } + } + + function setStorageBytes32(bytes32 position, bytes32 data) internal { + assembly { sstore(position, data) } + } + + function setStorageUint256(bytes32 position, uint256 data) internal { + assembly { sstore(position, data) } + } +} diff --git a/tests-solidity/suites/initializable/contracts/test/InitializableMock.sol b/tests-solidity/suites/initializable/contracts/test/InitializableMock.sol new file mode 100644 index 0000000000..c5983db901 --- /dev/null +++ b/tests-solidity/suites/initializable/contracts/test/InitializableMock.sol @@ -0,0 +1,15 @@ +pragma solidity 0.4.24; + +import "../Initializable.sol"; +import "../Petrifiable.sol"; + + +contract LifecycleMock is Initializable, Petrifiable { + function initializeMock() public { + initialized(); + } + + function petrifyMock() public { + petrify(); + } +} diff --git a/tests-solidity/suites/initializable/contracts/test/Migrations.sol b/tests-solidity/suites/initializable/contracts/test/Migrations.sol new file mode 100644 index 0000000000..f4661c6608 --- /dev/null +++ b/tests-solidity/suites/initializable/contracts/test/Migrations.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.22 <0.8.0; + +contract Migrations { + address public owner = msg.sender; + uint public last_completed_migration; + + modifier restricted() { + require( + msg.sender == owner, + "This function is restricted to the contract's owner" + ); + _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } +} diff --git a/tests-solidity/suites/initializable/migrations/1_initial_migration.js b/tests-solidity/suites/initializable/migrations/1_initial_migration.js new file mode 100644 index 0000000000..16a7ba52f4 --- /dev/null +++ b/tests-solidity/suites/initializable/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +const Migrations = artifacts.require("Migrations"); + +module.exports = function (deployer) { + deployer.deploy(Migrations); +}; diff --git a/tests-solidity/suites/initializable/package.json b/tests-solidity/suites/initializable/package.json new file mode 100644 index 0000000000..9af338f273 --- /dev/null +++ b/tests-solidity/suites/initializable/package.json @@ -0,0 +1,16 @@ +{ + "name": "initializable", + "version": "1.0.0", + "author": "Aragon Association ", + "license": "GPL-3.0-or-later", + "scripts": { + "test-ganache": "yarn truffle test", + "test-ethermint": "yarn truffle test --network ethermint" + }, + "devDependencies": { + "@aragon/contract-helpers-test": "^0.1.0", + "chai": "^4.2.0", + "truffle": "^5.1.42", + "web3": "^1.2.11" + } +} diff --git a/tests-solidity/suites/initializable/test/.gitkeep b/tests-solidity/suites/initializable/test/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests-solidity/suites/initializable/test/lifecycle.js b/tests-solidity/suites/initializable/test/lifecycle.js new file mode 100644 index 0000000000..cc643786ff --- /dev/null +++ b/tests-solidity/suites/initializable/test/lifecycle.js @@ -0,0 +1,74 @@ +const { assertRevert } = require('@aragon/contract-helpers-test/src/asserts') + +// Mocks +const LifecycleMock = artifacts.require('LifecycleMock') + +const ERRORS = { + INIT_ALREADY_INITIALIZED: 'INIT_ALREADY_INITIALIZED', +} + +contract('Lifecycle', () => { + let lifecycle + + beforeEach(async () => { + lifecycle = await LifecycleMock.new() + }) + + it('is not initialized', async () => { + assert.isFalse(await lifecycle.hasInitialized(), 'should not be initialized') + }) + + it('is not petrified', async () => { + assert.isFalse(await lifecycle.isPetrified(), 'should not be petrified') + }) + + context('> Initialized', () => { + beforeEach(async () => { + await lifecycle.initializeMock() + }) + + it('is initialized', async () => { + assert.isTrue(await lifecycle.hasInitialized(), 'should be initialized') + }) + + it('is not petrified', async () => { + assert.isFalse(await lifecycle.isPetrified(), 'should not be petrified') + }) + + it('has correct initialization block', async () => { + assert.equal(await lifecycle.getInitializationBlock(), await web3.eth.getBlockNumber(), 'initialization block should be correct') + }) + + it('cannot be re-initialized', async () => { + await assertRevert(lifecycle.initializeMock()/*, ERRORS.INIT_ALREADY_INITIALIZED*/) + }) + + it('cannot be petrified', async () => { + await assertRevert(lifecycle.petrifyMock()/*, ERRORS.INIT_ALREADY_INITIALIZED*/) + }) + }) + + context('> Petrified', () => { + beforeEach(async () => { + await lifecycle.petrifyMock() + }) + + it('is not initialized', async () => { + assert.isFalse(await lifecycle.hasInitialized(), 'should not be initialized') + }) + + it('is petrified', async () => { + assert.isTrue(await lifecycle.isPetrified(), 'should be petrified') + }) + + it('cannot be petrified again', async () => { + await assertRevert(lifecycle.petrifyMock()/*, ERRORS.INIT_ALREADY_INITIALIZED*/) + }) + + it('has initialization block in the future', async () => { + const petrifiedBlock = await lifecycle.getInitializationBlock() + const blockNumber = await web3.eth.getBlockNumber() + assert.isTrue(petrifiedBlock.gt(blockNumber), 'petrified block should be in the future') + }) + }) +}) diff --git a/tests-solidity/suites/initializable/truffle-config.js b/tests-solidity/suites/initializable/truffle-config.js new file mode 100644 index 0000000000..cef25ba5bd --- /dev/null +++ b/tests-solidity/suites/initializable/truffle-config.js @@ -0,0 +1,23 @@ +module.exports = { + networks: { + // Development network is just left as truffle's default settings + ethermint: { + host: "127.0.0.1", // Localhost (default: none) + port: 8545, // Standard Ethereum port (default: none) + network_id: "*", // Any network (default: none) + gas: 5000000, // Gas sent with each transaction + gasPrice: 1000000000, // 1 gwei (in wei) + }, + }, + compilers: { + solc: { + version: "0.4.24", + settings: { + optimizer: { + enabled: true, + runs: 10000, + }, + }, + }, + }, +} diff --git a/tests-solidity/suites/proxy/contracts/DelegateProxy.sol b/tests-solidity/suites/proxy/contracts/DelegateProxy.sol new file mode 100644 index 0000000000..aa461d55b5 --- /dev/null +++ b/tests-solidity/suites/proxy/contracts/DelegateProxy.sol @@ -0,0 +1,31 @@ +pragma solidity 0.4.24; + +import "./IsContract.sol"; +import "./ERCProxy.sol"; + + +contract DelegateProxy is ERCProxy, IsContract { + uint256 internal constant FWD_GAS_LIMIT = 10000; + + /** + * @dev Performs a delegatecall and returns whatever the delegatecall returned (entire context execution will return!) + * @param _dst Destination address to perform the delegatecall + * @param _calldata Calldata for the delegatecall + */ + function delegatedFwd(address _dst, bytes _calldata) internal { + require(isContract(_dst)); + uint256 fwdGasLimit = FWD_GAS_LIMIT; + + assembly { + let result := delegatecall(sub(gas, fwdGasLimit), _dst, add(_calldata, 0x20), mload(_calldata), 0, 0) + let size := returndatasize + let ptr := mload(0x40) + returndatacopy(ptr, 0, size) + + // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas. + // if the call returned error data, forward it + switch result case 0 { revert(ptr, size) } + default { return(ptr, size) } + } + } +} diff --git a/tests-solidity/suites/proxy/contracts/DepositableDelegateProxy.sol b/tests-solidity/suites/proxy/contracts/DepositableDelegateProxy.sol new file mode 100644 index 0000000000..d7291859d6 --- /dev/null +++ b/tests-solidity/suites/proxy/contracts/DepositableDelegateProxy.sol @@ -0,0 +1,44 @@ +pragma solidity 0.4.24; + +import "./DelegateProxy.sol"; +import "./DepositableStorage.sol"; + + +contract DepositableDelegateProxy is DepositableStorage, DelegateProxy { + event ProxyDeposit(address sender, uint256 value); + + function () external payable { + uint256 forwardGasThreshold = FWD_GAS_LIMIT; + bytes32 isDepositablePosition = DEPOSITABLE_POSITION; + + // Optimized assembly implementation to prevent EIP-1884 from breaking deposits, reference code in Solidity: + // https://github.com/aragon/aragonOS/blob/v4.2.1/contracts/common/DepositableDelegateProxy.sol#L10-L20 + assembly { + // Continue only if the gas left is lower than the threshold for forwarding to the implementation code, + // otherwise continue outside of the assembly block. + if lt(gas, forwardGasThreshold) { + // Only accept the deposit and emit an event if all of the following are true: + // the proxy accepts deposits (isDepositable), msg.data.length == 0, and msg.value > 0 + if and(and(sload(isDepositablePosition), iszero(calldatasize)), gt(callvalue, 0)) { + // Equivalent Solidity code for emitting the event: + // emit ProxyDeposit(msg.sender, msg.value); + + let logData := mload(0x40) // free memory pointer + mstore(logData, caller) // add 'msg.sender' to the log data (first event param) + mstore(add(logData, 0x20), callvalue) // add 'msg.value' to the log data (second event param) + + // Emit an event with one topic to identify the event: keccak256('ProxyDeposit(address,uint256)') = 0x15ee...dee1 + log1(logData, 0x40, 0x15eeaa57c7bd188c1388020bcadc2c436ec60d647d36ef5b9eb3c742217ddee1) + + stop() // Stop. Exits execution context + } + + // If any of above checks failed, revert the execution (if ETH was sent, it is returned to the sender) + revert(0, 0) + } + } + + address target = implementation(); + delegatedFwd(target, msg.data); + } +} diff --git a/tests-solidity/suites/proxy/contracts/DepositableStorage.sol b/tests-solidity/suites/proxy/contracts/DepositableStorage.sol new file mode 100644 index 0000000000..9b9970de39 --- /dev/null +++ b/tests-solidity/suites/proxy/contracts/DepositableStorage.sol @@ -0,0 +1,19 @@ +pragma solidity 0.4.24; + +import "./UnstructuredStorage.sol"; + + +contract DepositableStorage { + using UnstructuredStorage for bytes32; + + // keccak256("aragonOS.depositableStorage.depositable") + bytes32 internal constant DEPOSITABLE_POSITION = 0x665fd576fbbe6f247aff98f5c94a561e3f71ec2d3c988d56f12d342396c50cea; + + function isDepositable() public view returns (bool) { + return DEPOSITABLE_POSITION.getStorageBool(); + } + + function setDepositable(bool _depositable) internal { + DEPOSITABLE_POSITION.setStorageBool(_depositable); + } +} diff --git a/tests-solidity/suites/proxy/contracts/ERCProxy.sol b/tests-solidity/suites/proxy/contracts/ERCProxy.sol new file mode 100644 index 0000000000..f836a30a44 --- /dev/null +++ b/tests-solidity/suites/proxy/contracts/ERCProxy.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.4.24; + + +contract ERCProxy { + uint256 internal constant FORWARDING = 1; + uint256 internal constant UPGRADEABLE = 2; + + function proxyType() public pure returns (uint256 proxyTypeId); + function implementation() public view returns (address codeAddr); +} diff --git a/tests-solidity/suites/proxy/contracts/IsContract.sol b/tests-solidity/suites/proxy/contracts/IsContract.sol new file mode 100644 index 0000000000..cdd115112d --- /dev/null +++ b/tests-solidity/suites/proxy/contracts/IsContract.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.4.24; + + +contract IsContract { + /* + * NOTE: this should NEVER be used for authentication + * (see pitfalls: https://github.com/fergarrui/ethereum-security/tree/master/contracts/extcodesize). + * + * This is only intended to be used as a sanity check that an address is actually a contract, + * RATHER THAN an address not being a contract. + */ + function isContract(address _target) internal view returns (bool) { + if (_target == address(0)) { + return false; + } + + uint256 size; + assembly { size := extcodesize(_target) } + return size > 0; + } +} diff --git a/tests-solidity/suites/proxy/contracts/UnstructuredStorage.sol b/tests-solidity/suites/proxy/contracts/UnstructuredStorage.sol new file mode 100644 index 0000000000..7dddc05422 --- /dev/null +++ b/tests-solidity/suites/proxy/contracts/UnstructuredStorage.sol @@ -0,0 +1,36 @@ +pragma solidity ^0.4.24; + + +library UnstructuredStorage { + function getStorageBool(bytes32 position) internal view returns (bool data) { + assembly { data := sload(position) } + } + + function getStorageAddress(bytes32 position) internal view returns (address data) { + assembly { data := sload(position) } + } + + function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) { + assembly { data := sload(position) } + } + + function getStorageUint256(bytes32 position) internal view returns (uint256 data) { + assembly { data := sload(position) } + } + + function setStorageBool(bytes32 position, bool data) internal { + assembly { sstore(position, data) } + } + + function setStorageAddress(bytes32 position, address data) internal { + assembly { sstore(position, data) } + } + + function setStorageBytes32(bytes32 position, bytes32 data) internal { + assembly { sstore(position, data) } + } + + function setStorageUint256(bytes32 position, uint256 data) internal { + assembly { sstore(position, data) } + } +} diff --git a/tests-solidity/suites/proxy/contracts/test/DepositableDelegateProxyMock.sol b/tests-solidity/suites/proxy/contracts/test/DepositableDelegateProxyMock.sol new file mode 100644 index 0000000000..e5096a85e5 --- /dev/null +++ b/tests-solidity/suites/proxy/contracts/test/DepositableDelegateProxyMock.sol @@ -0,0 +1,24 @@ +pragma solidity 0.4.24; + +import "../DepositableDelegateProxy.sol"; + + +contract DepositableDelegateProxyMock is DepositableDelegateProxy { + address private implementationMock; + + function enableDepositsOnMock() external { + setDepositable(true); + } + + function setImplementationOnMock(address _implementationMock) external { + implementationMock = _implementationMock; + } + + function implementation() public view returns (address) { + return implementationMock; + } + + function proxyType() public pure returns (uint256 proxyTypeId) { + return UPGRADEABLE; + } +} diff --git a/tests-solidity/suites/proxy/contracts/test/EthSender.sol b/tests-solidity/suites/proxy/contracts/test/EthSender.sol new file mode 100644 index 0000000000..6af009a936 --- /dev/null +++ b/tests-solidity/suites/proxy/contracts/test/EthSender.sol @@ -0,0 +1,8 @@ +pragma solidity 0.4.24; + + +contract EthSender { + function sendEth(address to) external payable { + to.transfer(msg.value); + } +} diff --git a/tests-solidity/suites/proxy/contracts/test/Migrations.sol b/tests-solidity/suites/proxy/contracts/test/Migrations.sol new file mode 100644 index 0000000000..f4661c6608 --- /dev/null +++ b/tests-solidity/suites/proxy/contracts/test/Migrations.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.4.22 <0.8.0; + +contract Migrations { + address public owner = msg.sender; + uint public last_completed_migration; + + modifier restricted() { + require( + msg.sender == owner, + "This function is restricted to the contract's owner" + ); + _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } +} diff --git a/tests-solidity/suites/proxy/contracts/test/ProxyTarget.sol b/tests-solidity/suites/proxy/contracts/test/ProxyTarget.sol new file mode 100644 index 0000000000..a68ca3275a --- /dev/null +++ b/tests-solidity/suites/proxy/contracts/test/ProxyTarget.sol @@ -0,0 +1,17 @@ +pragma solidity 0.4.24; + +contract ProxyTargetWithoutFallback { + event Pong(); + + function ping() external { + emit Pong(); + } +} + +contract ProxyTargetWithFallback is ProxyTargetWithoutFallback { + event ReceivedEth(); + + function () external payable { + emit ReceivedEth(); + } +} diff --git a/tests-solidity/suites/proxy/migrations/1_initial_migration.js b/tests-solidity/suites/proxy/migrations/1_initial_migration.js new file mode 100644 index 0000000000..16a7ba52f4 --- /dev/null +++ b/tests-solidity/suites/proxy/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +const Migrations = artifacts.require("Migrations"); + +module.exports = function (deployer) { + deployer.deploy(Migrations); +}; diff --git a/tests-solidity/suites/proxy/package.json b/tests-solidity/suites/proxy/package.json new file mode 100644 index 0000000000..4748bacccd --- /dev/null +++ b/tests-solidity/suites/proxy/package.json @@ -0,0 +1,16 @@ +{ + "name": "proxy", + "version": "1.0.0", + "author": "Aragon Association ", + "license": "GPL-3.0-or-later", + "scripts": { + "test-ganache": "yarn truffle test", + "test-ethermint": "yarn truffle test --network ethermint" + }, + "devDependencies": { + "@aragon/contract-helpers-test": "^0.1.0", + "chai": "^4.2.0", + "truffle": "^5.1.42", + "web3": "^1.2.11" + } +} diff --git a/tests-solidity/suites/proxy/test/.gitkeep b/tests-solidity/suites/proxy/test/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests-solidity/suites/proxy/test/depositable_delegate_proxy.js b/tests-solidity/suites/proxy/test/depositable_delegate_proxy.js new file mode 100644 index 0000000000..8b293ac979 --- /dev/null +++ b/tests-solidity/suites/proxy/test/depositable_delegate_proxy.js @@ -0,0 +1,155 @@ +const { bn } = require('@aragon/contract-helpers-test') +const { assertAmountOfEvents, assertEvent, assertRevert, assertOutOfGas, assertBn } = require('@aragon/contract-helpers-test/src/asserts') + +// Mocks +const DepositableDelegateProxyMock = artifacts.require('DepositableDelegateProxyMock') +const EthSender = artifacts.require('EthSender') +const ProxyTargetWithoutFallback = artifacts.require('ProxyTargetWithoutFallback') +const ProxyTargetWithFallback = artifacts.require('ProxyTargetWithFallback') + +const TX_BASE_GAS = 21000 +const SEND_ETH_GAS = TX_BASE_GAS + 9999 // <10k gas is the threshold for depositing +const PROXY_FORWARD_GAS = TX_BASE_GAS + 2e6 // high gas amount to ensure that the proxy forwards the call +const FALLBACK_SETUP_GAS = 100 // rough estimation of how much gas it spends before executing the fallback code +const SOLIDITY_TRANSFER_GAS = 2300 + +contract('DepositableDelegateProxy', ([ sender ]) => { + let ethSender, proxy, target, proxyTargetWithoutFallbackBase, proxyTargetWithFallbackBase + + // Initial setup + before(async () => { + ethSender = await EthSender.new() + proxyTargetWithoutFallbackBase = await ProxyTargetWithoutFallback.new() + proxyTargetWithFallbackBase = await ProxyTargetWithFallback.new() + }) + + beforeEach(async () => { + proxy = await DepositableDelegateProxyMock.new() + target = await ProxyTargetWithFallback.at(proxy.address) + }) + + const itForwardsToImplementationIfGasIsOverThreshold = () => { + context('when implementation address is set', () => { + const itSuccessfullyForwardsCall = () => { + it('forwards call with data', async () => { + const receipt = await target.ping({ gas: PROXY_FORWARD_GAS }) + assertAmountOfEvents(receipt, 'Pong') + }) + } + + context('when implementation has a fallback', () => { + beforeEach(async () => { + await proxy.setImplementationOnMock(proxyTargetWithFallbackBase.address) + }) + + itSuccessfullyForwardsCall() + + it('can receive ETH [@skip-on-coverage]', async () => { + const receipt = await target.sendTransaction({ value: 1, gas: SEND_ETH_GAS + FALLBACK_SETUP_GAS }) + assertAmountOfEvents(receipt, 'ReceivedEth') + }) + }) + + context('when implementation doesn\'t have a fallback', () => { + beforeEach(async () => { + await proxy.setImplementationOnMock(proxyTargetWithoutFallbackBase.address) + }) + + itSuccessfullyForwardsCall() + + it('reverts when sending ETH', async () => { + await assertRevert(target.sendTransaction({ value: 1, gas: PROXY_FORWARD_GAS })) + }) + }) + }) + + context('when implementation address is not set', () => { + it('reverts when a function is called', async () => { + await assertRevert(target.ping({ gas: PROXY_FORWARD_GAS })) + }) + + it('reverts when sending ETH', async () => { + await assertRevert(target.sendTransaction({ value: 1, gas: PROXY_FORWARD_GAS })) + }) + }) + } + + const itRevertsOnInvalidDeposits = () => { + it('reverts when call has data', async () => { + await proxy.setImplementationOnMock(proxyTargetWithoutFallbackBase.address) + + await assertRevert(target.ping({ gas: SEND_ETH_GAS })) + }) + + it('reverts when call sends 0 value', async () => { + await assertRevert(proxy.sendTransaction({ value: 0, gas: SEND_ETH_GAS })) + }) + } + + context('when proxy is set as depositable', () => { + beforeEach(async () => { + await proxy.enableDepositsOnMock() + }) + + context('when call gas is below the forwarding threshold', () => { + const value = bn(100) + + const assertSendEthToProxy = async ({ value, gas, shouldOOG }) => { + const initialBalance = bn(await web3.eth.getBalance(proxy.address)) + + const sendEthAction = () => proxy.sendTransaction({ from: sender, gas, value }) + + if (shouldOOG) { + await assertOutOfGas(sendEthAction()) + assertBn(bn(await web3.eth.getBalance(proxy.address)), initialBalance, 'Target balance should be the same as before') + } else { + const receipt = await sendEthAction() + + assertBn(bn(await web3.eth.getBalance(proxy.address)), initialBalance.add(value), 'Target balance should be correct') + assertAmountOfEvents(receipt, 'ProxyDeposit', { decodeForAbi: DepositableDelegateProxyMock.abi }) + assertEvent(receipt, 'ProxyDeposit', { decodeForAbi: DepositableDelegateProxyMock.abi, expectedArgs: { sender, value } }) + + return receipt + } + } + + it('can receive ETH', async () => { + await assertSendEthToProxy({ value, gas: SEND_ETH_GAS }) + }) + + it('cannot receive ETH if sent with a small amount of gas [@skip-on-coverage]', async () => { + const oogDecrease = 250 + // deposit cannot be done with this amount of gas + const gas = TX_BASE_GAS + SOLIDITY_TRANSFER_GAS - oogDecrease + await assertSendEthToProxy({ shouldOOG: true, value, gas }) + }) + + it('can receive ETH from contract [@skip-on-coverage]', async () => { + const receipt = await ethSender.sendEth(proxy.address, { value }) + + assertAmountOfEvents(receipt, 'ProxyDeposit', { decodeForAbi: proxy.abi }) + assertEvent(receipt, 'ProxyDeposit', { decodeForAbi: proxy.abi, expectedArgs: { sender: ethSender.address, value } }) + }) + + itRevertsOnInvalidDeposits() + }) + + context('when call gas is over forwarding threshold', () => { + itForwardsToImplementationIfGasIsOverThreshold() + }) + }) + + context('when proxy is not set as depositable', () => { + context('when call gas is below the forwarding threshold', () => { + it('reverts when depositing ETH', async () => { + await assertRevert(proxy.sendTransaction({ value: 1, gas: SEND_ETH_GAS })) + }) + + itRevertsOnInvalidDeposits() + }) + + context('when call gas is over forwarding threshold', () => { + itForwardsToImplementationIfGasIsOverThreshold() + }) + }) +}) diff --git a/tests-solidity/suites/proxy/truffle-config.js b/tests-solidity/suites/proxy/truffle-config.js new file mode 100644 index 0000000000..cef25ba5bd --- /dev/null +++ b/tests-solidity/suites/proxy/truffle-config.js @@ -0,0 +1,23 @@ +module.exports = { + networks: { + // Development network is just left as truffle's default settings + ethermint: { + host: "127.0.0.1", // Localhost (default: none) + port: 8545, // Standard Ethereum port (default: none) + network_id: "*", // Any network (default: none) + gas: 5000000, // Gas sent with each transaction + gasPrice: 1000000000, // 1 gwei (in wei) + }, + }, + compilers: { + solc: { + version: "0.4.24", + settings: { + optimizer: { + enabled: true, + runs: 10000, + }, + }, + }, + }, +} diff --git a/tests-solidity/suites/staking/.github/workflows/ci_contracts.yml b/tests-solidity/suites/staking/.github/workflows/ci_contracts.yml new file mode 100644 index 0000000000..6a51040044 --- /dev/null +++ b/tests-solidity/suites/staking/.github/workflows/ci_contracts.yml @@ -0,0 +1,28 @@ +name: contracts + +on: + push: + branches: master + pull_request: + branches: '*' + +jobs: + CI: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install node + uses: actions/setup-node@v1 + with: + node-version: 12 + - name: Install + run: yarn + - name: Lint + run: yarn lint + - name: Test + run: yarn test + - name: coverage + continue-on-error: true + run: yarn coverage +env: + CI: true diff --git a/tests-solidity/suites/staking/contracts/Staking.sol b/tests-solidity/suites/staking/contracts/Staking.sol new file mode 100644 index 0000000000..11bd38d832 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/Staking.sol @@ -0,0 +1,648 @@ +pragma solidity 0.5.17; + +import "./lib/os/SafeMath.sol"; +import "./lib/os/SafeERC20.sol"; +import "./lib/os/IsContract.sol"; +import "./lib/os/Autopetrified.sol"; +import "./lib/Checkpointing.sol"; + +import "./standards/ERC900.sol"; +import "./locking/IStakingLocking.sol"; +import "./locking/ILockManager.sol"; + + +contract Staking is Autopetrified, ERC900, IStakingLocking, IsContract { + using SafeMath for uint256; + using Checkpointing for Checkpointing.History; + using SafeERC20 for ERC20; + + uint256 private constant MAX_UINT64 = uint256(uint64(-1)); + + string private constant ERROR_TOKEN_NOT_CONTRACT = "STAKING_TOKEN_NOT_CONTRACT"; + string private constant ERROR_AMOUNT_ZERO = "STAKING_AMOUNT_ZERO"; + string private constant ERROR_TOKEN_TRANSFER = "STAKING_TOKEN_TRANSFER_FAIL"; + string private constant ERROR_TOKEN_DEPOSIT = "STAKING_TOKEN_DEPOSIT_FAIL"; + string private constant ERROR_TOKEN_NOT_SENDER = "STAKING_TOKEN_NOT_SENDER"; + string private constant ERROR_WRONG_TOKEN = "STAKING_WRONG_TOKEN"; + string private constant ERROR_NOT_ENOUGH_BALANCE = "STAKING_NOT_ENOUGH_BALANCE"; + string private constant ERROR_NOT_ENOUGH_ALLOWANCE = "STAKING_NOT_ENOUGH_ALLOWANCE"; + string private constant ERROR_SENDER_NOT_ALLOWED = "STAKING_SENDER_NOT_ALLOWED"; + string private constant ERROR_ALLOWANCE_ZERO = "STAKING_ALLOWANCE_ZERO"; + string private constant ERROR_LOCK_ALREADY_EXISTS = "STAKING_LOCK_ALREADY_EXISTS"; + string private constant ERROR_LOCK_DOES_NOT_EXIST = "STAKING_LOCK_DOES_NOT_EXIST"; + string private constant ERROR_NOT_ENOUGH_LOCK = "STAKING_NOT_ENOUGH_LOCK"; + string private constant ERROR_CANNOT_UNLOCK = "STAKING_CANNOT_UNLOCK"; + string private constant ERROR_CANNOT_CHANGE_ALLOWANCE = "STAKING_CANNOT_CHANGE_ALLOWANCE"; + string private constant ERROR_LOCKMANAGER_CALL_FAIL = "STAKING_LOCKMANAGER_CALL_FAIL"; + string private constant ERROR_BLOCKNUMBER_TOO_BIG = "STAKING_BLOCKNUMBER_TOO_BIG"; + + struct Lock { + uint256 amount; + uint256 allowance; // must be greater than zero to consider the lock active, and always greater than or equal to amount + } + + struct Account { + mapping (address => Lock) locks; // from manager to lock + uint256 totalLocked; + Checkpointing.History stakedHistory; + } + + ERC20 internal stakingToken; + mapping (address => Account) internal accounts; + Checkpointing.History internal totalStakedHistory; + + /** + * @notice Initialize Staking app with token `_stakingToken` + * @param _stakingToken ERC20 token used for staking + */ + function initialize(ERC20 _stakingToken) external { + require(isContract(address(_stakingToken)), ERROR_TOKEN_NOT_CONTRACT); + initialized(); + stakingToken = _stakingToken; + } + + /** + * @notice Stakes `@tokenAmount(self.token(): address, _amount)`, transferring them from `msg.sender` + * @param _amount Number of tokens staked + * @param _data Used in Staked event, to add signalling information in more complex staking applications + */ + function stake(uint256 _amount, bytes calldata _data) external isInitialized { + _stakeFor(msg.sender, msg.sender, _amount, _data); + } + + /** + * @notice Stakes `@tokenAmount(self.token(): address, _amount)`, transferring them from `msg.sender`, and assigns them to `_user` + * @param _user The receiving accounts for the tokens staked + * @param _amount Number of tokens staked + * @param _data Used in Staked event, to add signalling information in more complex staking applications + */ + function stakeFor(address _user, uint256 _amount, bytes calldata _data) external isInitialized { + _stakeFor(msg.sender, _user, _amount, _data); + } + + /** + * @notice Unstakes `@tokenAmount(self.token(): address, _amount)`, returning them to the user + * @param _amount Number of tokens to unstake + * @param _data Used in Unstaked event, to add signalling information in more complex staking applications + */ + function unstake(uint256 _amount, bytes calldata _data) external isInitialized { + // unstaking 0 tokens is not allowed + require(_amount > 0, ERROR_AMOUNT_ZERO); + + _unstake(msg.sender, _amount, _data); + } + + /** + * @notice Allow `_lockManager` to lock up to `@tokenAmount(self.token(): address, _allowance)` of `msg.sender` + * It creates a new lock, so the lock for this manager cannot exist before. + * @param _lockManager The manager entity for this particular lock + * @param _allowance Amount of tokens that the manager can lock + * @param _data Data to parametrize logic for the lock to be enforced by the manager + */ + function allowManager(address _lockManager, uint256 _allowance, bytes calldata _data) external isInitialized { + _allowManager(_lockManager, _allowance, _data); + } + + /** + * @notice Lock `@tokenAmount(self.token(): address, _amount)` and assign `_lockManager` as manager with `@tokenAmount(self.token(): address, _allowance)` allowance and `_data` as data, so they can not be unstaked + * @param _amount The amount of tokens to be locked + * @param _lockManager The manager entity for this particular lock. This entity will have full control over the lock, in particular will be able to unlock it + * @param _allowance Amount of tokens that the manager can lock + * @param _data Data to parametrize logic for the lock to be enforced by the manager + */ + function allowManagerAndLock(uint256 _amount, address _lockManager, uint256 _allowance, bytes calldata _data) external isInitialized { + _allowManager(_lockManager, _allowance, _data); + + _lockUnsafe(msg.sender, _lockManager, _amount); + } + + /** + * @notice Transfer `@tokenAmount(self.token(): address, _amount)` to `_to`’s staked balance + * @param _to Recipient of the tokens + * @param _amount Number of tokens to be transferred + */ + function transfer(address _to, uint256 _amount) external isInitialized { + _transfer(msg.sender, _to, _amount); + } + + /** + * @notice Transfer `@tokenAmount(self.token(): address, _amount)` to `_to`’s external balance (i.e. unstaked) + * @param _to Recipient of the tokens + * @param _amount Number of tokens to be transferred + */ + function transferAndUnstake(address _to, uint256 _amount) external isInitialized { + _transfer(msg.sender, _to, _amount); + _unstake(_to, _amount, new bytes(0)); + } + + /** + * @notice Transfer `@tokenAmount(self.token(): address, _amount)` from `_from`'s lock by `msg.sender` to `_to` + * @param _from Owner of locked tokens + * @param _to Recipient of the tokens + * @param _amount Number of tokens to be transferred + */ + function slash( + address _from, + address _to, + uint256 _amount + ) + external + isInitialized + { + _unlockUnsafe(_from, msg.sender, _amount); + _transfer(_from, _to, _amount); + } + + /** + * @notice Transfer `@tokenAmount(self.token(): address, _amount)` from `_from`'s lock by `msg.sender` to `_to` (unstaked) + * @param _from Owner of locked tokens + * @param _to Recipient of the tokens + * @param _amount Number of tokens to be transferred + */ + function slashAndUnstake( + address _from, + address _to, + uint256 _amount + ) + external + isInitialized + { + _unlockUnsafe(_from, msg.sender, _amount); + _transfer(_from, _to, _amount); + _unstake(_to, _amount, new bytes(0)); + } + + /** + * @notice Transfer `@tokenAmount(self.token(): address, _slashAmount)` from `_from`'s lock by `msg.sender` to `_to`, and decrease `@tokenAmount(self.token(): address, _unlockAmount)` from that lock + * @param _from Owner of locked tokens + * @param _to Recipient of the tokens + * @param _unlockAmount Number of tokens to be unlocked + * @param _slashAmount Number of tokens to be transferred + */ + function slashAndUnlock( + address _from, + address _to, + uint256 _unlockAmount, + uint256 _slashAmount + ) + external + isInitialized + { + // No need to check that _slashAmount is positive, as _transfer will fail + // No need to check that have enough locked funds, as _unlockUnsafe will fail + require(_unlockAmount > 0, ERROR_AMOUNT_ZERO); + + _unlockUnsafe(_from, msg.sender, _unlockAmount.add(_slashAmount)); + _transfer(_from, _to, _slashAmount); + } + + /** + * @notice Increase allowance by `@tokenAmount(self.token(): address, _allowance)` of lock manager `_lockManager` for user `msg.sender` + * @param _lockManager The manager entity for this particular lock + * @param _allowance Amount of allowed tokens increase + */ + function increaseLockAllowance(address _lockManager, uint256 _allowance) external isInitialized { + Lock storage lock_ = accounts[msg.sender].locks[_lockManager]; + require(lock_.allowance > 0, ERROR_LOCK_DOES_NOT_EXIST); + + _increaseLockAllowance(_lockManager, lock_, _allowance); + } + + /** + * @notice Decrease allowance by `@tokenAmount(self.token(): address, _allowance)` of lock manager `_lockManager` for user `_user` + * @param _user Owner of locked tokens + * @param _lockManager The manager entity for this particular lock + * @param _allowance Amount of allowed tokens decrease + */ + function decreaseLockAllowance(address _user, address _lockManager, uint256 _allowance) external isInitialized { + // only owner and manager can decrease allowance + require(msg.sender == _user || msg.sender == _lockManager, ERROR_CANNOT_CHANGE_ALLOWANCE); + require(_allowance > 0, ERROR_AMOUNT_ZERO); + + Lock storage lock_ = accounts[_user].locks[_lockManager]; + uint256 newAllowance = lock_.allowance.sub(_allowance); + require(newAllowance >= lock_.amount, ERROR_NOT_ENOUGH_ALLOWANCE); + // unlockAndRemoveManager must be used for this: + require(newAllowance > 0, ERROR_ALLOWANCE_ZERO); + + lock_.allowance = newAllowance; + + emit LockAllowanceChanged(_user, _lockManager, _allowance, false); + } + + /** + * @notice Increase locked amount by `@tokenAmount(self.token(): address, _amount)` for user `_user` by lock manager `_lockManager` + * @param _user Owner of locked tokens + * @param _lockManager The manager entity for this particular lock + * @param _amount Amount of locked tokens increase + */ + function lock(address _user, address _lockManager, uint256 _amount) external isInitialized { + // we are locking funds from owner account, so only owner or manager are allowed + require(msg.sender == _user || msg.sender == _lockManager, ERROR_SENDER_NOT_ALLOWED); + + _lockUnsafe(_user, _lockManager, _amount); + } + + /** + * @notice Decrease locked amount by `@tokenAmount(self.token(): address, _amount)` for user `_user` by lock manager `_lockManager` + * @param _user Owner of locked tokens + * @param _lockManager The manager entity for this particular lock + * @param _amount Amount of locked tokens decrease + */ + function unlock(address _user, address _lockManager, uint256 _amount) external isInitialized { + require(_amount > 0, ERROR_AMOUNT_ZERO); + + // only manager and owner (if manager allows) can unlock + require(_canUnlockUnsafe(msg.sender, _user, _lockManager, _amount), ERROR_CANNOT_UNLOCK); + + _unlockUnsafe(_user, _lockManager, _amount); + } + + /** + * @notice Unlock `_user`'s lock by `_lockManager` so locked tokens can be unstaked again + * @param _user Owner of locked tokens + * @param _lockManager Manager of the lock for the given account + */ + function unlockAndRemoveManager(address _user, address _lockManager) external isInitialized { + // only manager and owner (if manager allows) can unlock + require(_canUnlockUnsafe(msg.sender, _user, _lockManager, 0), ERROR_CANNOT_UNLOCK); + + Account storage account = accounts[_user]; + Lock storage lock_ = account.locks[_lockManager]; + + uint256 amount = lock_.amount; + // update total + account.totalLocked = account.totalLocked.sub(amount); + + emit LockAmountChanged(_user, _lockManager, amount, false); + emit LockManagerRemoved(_user, _lockManager); + + delete account.locks[_lockManager]; + } + + /** + * @notice Change the manager of `_user`'s lock from `msg.sender` to `_newLockManager` + * @param _user Owner of lock + * @param _newLockManager New lock manager + */ + function setLockManager(address _user, address _newLockManager) external isInitialized { + Lock storage lock_ = accounts[_user].locks[msg.sender]; + require(lock_.allowance > 0, ERROR_LOCK_DOES_NOT_EXIST); + + accounts[_user].locks[_newLockManager] = lock_; + + delete accounts[_user].locks[msg.sender]; + + emit LockManagerTransferred(_user, msg.sender, _newLockManager); + } + + /** + * @dev MiniMeToken ApproveAndCallFallBack compliance + * @param _from Account approving tokens + * @param _amount Amount of `_token` tokens being approved + * @param _token MiniMeToken that is being approved and that the call comes from + * @param _data Used in Staked event, to add signalling information in more complex staking applications + */ + function receiveApproval(address _from, uint256 _amount, address _token, bytes calldata _data) external isInitialized { + require(_token == msg.sender, ERROR_TOKEN_NOT_SENDER); + require(_token == address(stakingToken), ERROR_WRONG_TOKEN); + + _stakeFor(_from, _from, _amount, _data); + } + + /** + * @notice Check whether it supports history of stakes + * @return Always true + */ + function supportsHistory() external pure returns (bool) { + return true; + } + + /** + * @notice Get the token used by the contract for staking and locking + * @return The token used by the contract for staking and locking + */ + function token() external view isInitialized returns (address) { + return address(stakingToken); + } + + /** + * @notice Get last time `_user` modified its staked balance + * @param _user Account requesting for + * @return Last block number when account's balance was modified + */ + function lastStakedFor(address _user) external view isInitialized returns (uint256) { + return accounts[_user].stakedHistory.lastUpdate(); + } + + /** + * @notice Get total amount of locked tokens for `_user` + * @param _user Owner of locks + * @return Total amount of locked tokens for the requested account + */ + function lockedBalanceOf(address _user) external view isInitialized returns (uint256) { + return _lockedBalanceOf(_user); + } + + /** + * @notice Get details of `_user`'s lock by `_lockManager` + * @param _user Owner of lock + * @param _lockManager Manager of the lock for the given account + * @return Amount of locked tokens + * @return Amount of tokens that lock manager is allowed to lock + */ + function getLock(address _user, address _lockManager) + external + view + isInitialized + returns ( + uint256 _amount, + uint256 _allowance + ) + { + Lock storage lock_ = accounts[_user].locks[_lockManager]; + _amount = lock_.amount; + _allowance = lock_.allowance; + } + + /** + * @notice Get staked and locked balances of `_user` + * @param _user Account being requested + * @return Amount of staked tokens + * @return Amount of total locked tokens + */ + function getBalancesOf(address _user) external view isInitialized returns (uint256 staked, uint256 locked) { + staked = _totalStakedFor(_user); + locked = _lockedBalanceOf(_user); + } + + /** + * @notice Get the amount of tokens staked by `_user` + * @param _user The owner of the tokens + * @return The amount of tokens staked by the given account + */ + function totalStakedFor(address _user) external view isInitialized returns (uint256) { + return _totalStakedFor(_user); + } + + /** + * @notice Get the total amount of tokens staked by all users + * @return The total amount of tokens staked by all users + */ + function totalStaked() external view isInitialized returns (uint256) { + return _totalStaked(); + } + + /** + * @notice Get the total amount of tokens staked by `_user` at block number `_blockNumber` + * @param _user Account requesting for + * @param _blockNumber Block number at which we are requesting + * @return The amount of tokens staked by the account at the given block number + */ + function totalStakedForAt(address _user, uint256 _blockNumber) external view isInitialized returns (uint256) { + require(_blockNumber <= MAX_UINT64, ERROR_BLOCKNUMBER_TOO_BIG); + + return accounts[_user].stakedHistory.get(uint64(_blockNumber)); + } + + /** + * @notice Get the total amount of tokens staked by all users at block number `_blockNumber` + * @param _blockNumber Block number at which we are requesting + * @return The amount of tokens staked at the given block number + */ + function totalStakedAt(uint256 _blockNumber) external view isInitialized returns (uint256) { + require(_blockNumber <= MAX_UINT64, ERROR_BLOCKNUMBER_TOO_BIG); + + return totalStakedHistory.get(uint64(_blockNumber)); + } + + /** + * @notice Get the staked but unlocked amount of tokens by `_user` + * @param _user Owner of the staked but unlocked balance + * @return Amount of tokens staked but not locked by given account + */ + function unlockedBalanceOf(address _user) external view isInitialized returns (uint256) { + return _unlockedBalanceOf(_user); + } + + /** + * @notice Check if `_sender` can unlock `_user`'s `@tokenAmount(self.token(): address, _amount)` locked by `_lockManager` + * @param _sender Account that would try to unlock tokens + * @param _user Owner of lock + * @param _lockManager Manager of the lock for the given owner + * @param _amount Amount of tokens to be potentially unlocked. If zero, it means the whole locked amount + * @return Whether given lock of given owner can be unlocked by given sender + */ + function canUnlock(address _sender, address _user, address _lockManager, uint256 _amount) external view isInitialized returns (bool) { + return _canUnlockUnsafe(_sender, _user, _lockManager, _amount); + } + + function _stakeFor(address _from, address _user, uint256 _amount, bytes memory _data) internal { + // staking 0 tokens is invalid + require(_amount > 0, ERROR_AMOUNT_ZERO); + + // checkpoint updated staking balance + uint256 newStake = _modifyStakeBalance(_user, _amount, true); + + // checkpoint total supply + _modifyTotalStaked(_amount, true); + + // pull tokens into Staking contract + require(stakingToken.safeTransferFrom(_from, address(this), _amount), ERROR_TOKEN_DEPOSIT); + + emit Staked(_user, _amount, newStake, _data); + } + + function _unstake(address _from, uint256 _amount, bytes memory _data) internal { + // checkpoint updated staking balance + uint256 newStake = _modifyStakeBalance(_from, _amount, false); + + // checkpoint total supply + _modifyTotalStaked(_amount, false); + + // transfer tokens + require(stakingToken.safeTransfer(_from, _amount), ERROR_TOKEN_TRANSFER); + + emit Unstaked(_from, _amount, newStake, _data); + } + + function _modifyStakeBalance(address _user, uint256 _by, bool _increase) internal returns (uint256) { + uint256 currentStake = _totalStakedFor(_user); + + uint256 newStake; + if (_increase) { + newStake = currentStake.add(_by); + } else { + require(_by <= _unlockedBalanceOf(_user), ERROR_NOT_ENOUGH_BALANCE); + newStake = currentStake.sub(_by); + } + + // add new value to account history + accounts[_user].stakedHistory.add(getBlockNumber64(), newStake); + + return newStake; + } + + function _modifyTotalStaked(uint256 _by, bool _increase) internal { + uint256 currentStake = _totalStaked(); + + uint256 newStake; + if (_increase) { + newStake = currentStake.add(_by); + } else { + newStake = currentStake.sub(_by); + } + + // add new value to total history + totalStakedHistory.add(getBlockNumber64(), newStake); + } + + function _allowManager(address _lockManager, uint256 _allowance, bytes memory _data) internal { + Lock storage lock_ = accounts[msg.sender].locks[_lockManager]; + // check if lock exists + require(lock_.allowance == 0, ERROR_LOCK_ALREADY_EXISTS); + + emit NewLockManager(msg.sender, _lockManager, _data); + + _increaseLockAllowance(_lockManager, lock_, _allowance); + } + + function _increaseLockAllowance(address _lockManager, Lock storage _lock, uint256 _allowance) internal { + require(_allowance > 0, ERROR_AMOUNT_ZERO); + + _lock.allowance = _lock.allowance.add(_allowance); + + emit LockAllowanceChanged(msg.sender, _lockManager, _allowance, true); + } + + /** + * @dev Assumes that sender is either owner or lock manager + */ + function _lockUnsafe(address _user, address _lockManager, uint256 _amount) internal { + require(_amount > 0, ERROR_AMOUNT_ZERO); + + // check enough unlocked tokens are available + require(_amount <= _unlockedBalanceOf(_user), ERROR_NOT_ENOUGH_BALANCE); + + Account storage account = accounts[_user]; + Lock storage lock_ = account.locks[_lockManager]; + + uint256 newAmount = lock_.amount.add(_amount); + // check allowance is enough, it also means that lock exists, as newAmount is greater than zero + require(newAmount <= lock_.allowance, ERROR_NOT_ENOUGH_ALLOWANCE); + + lock_.amount = newAmount; + + // update total + account.totalLocked = account.totalLocked.add(_amount); + + emit LockAmountChanged(_user, _lockManager, _amount, true); + } + + /** + * @dev Assumes `canUnlock` passes + */ + function _unlockUnsafe(address _user, address _lockManager, uint256 _amount) internal { + Account storage account = accounts[_user]; + Lock storage lock_ = account.locks[_lockManager]; + + uint256 lockAmount = lock_.amount; + require(lockAmount >= _amount, ERROR_NOT_ENOUGH_LOCK); + + // update lock amount + // No need for SafeMath: checked just above + lock_.amount = lockAmount - _amount; + + // update total + account.totalLocked = account.totalLocked.sub(_amount); + + emit LockAmountChanged(_user, _lockManager, _amount, false); + } + + function _transfer(address _from, address _to, uint256 _amount) internal { + // transferring 0 staked tokens is invalid + require(_amount > 0, ERROR_AMOUNT_ZERO); + + // update stakes + _modifyStakeBalance(_from, _amount, false); + _modifyStakeBalance(_to, _amount, true); + + emit StakeTransferred(_from, _to, _amount); + } + + /** + * @notice Get the amount of tokens staked by `_user` + * @param _user The owner of the tokens + * @return The amount of tokens staked by the given account + */ + function _totalStakedFor(address _user) internal view returns (uint256) { + // we assume it's not possible to stake in the future + return accounts[_user].stakedHistory.getLast(); + } + + /** + * @notice Get the total amount of tokens staked by all users + * @return The total amount of tokens staked by all users + */ + function _totalStaked() internal view returns (uint256) { + // we assume it's not possible to stake in the future + return totalStakedHistory.getLast(); + } + + /** + * @notice Get the staked but unlocked amount of tokens by `_user` + * @param _user Owner of the staked but unlocked balance + * @return Amount of tokens staked but not locked by given account + */ + function _unlockedBalanceOf(address _user) internal view returns (uint256) { + return _totalStakedFor(_user).sub(_lockedBalanceOf(_user)); + } + + function _lockedBalanceOf(address _user) internal view returns (uint256) { + return accounts[_user].totalLocked; + } + + /** + * @notice Check if `_sender` can unlock `_user`'s `@tokenAmount(self.token(): address, _amount)` locked by `_lockManager` + * @dev If calling this from a state modifying function trying to unlock tokens, make sure first parameter is `msg.sender` + * @param _sender Account that would try to unlock tokens + * @param _user Owner of lock + * @param _lockManager Manager of the lock for the given owner + * @param _amount Amount of locked tokens to unlock. If zero, the full locked amount + * @return Whether given lock of given owner can be unlocked by given sender + */ + function _canUnlockUnsafe(address _sender, address _user, address _lockManager, uint256 _amount) internal view returns (bool) { + Lock storage lock_ = accounts[_user].locks[_lockManager]; + require(lock_.allowance > 0, ERROR_LOCK_DOES_NOT_EXIST); + require(lock_.amount >= _amount, ERROR_NOT_ENOUGH_LOCK); + + uint256 amount = _amount == 0 ? lock_.amount : _amount; + + // If the sender is the lock manager, unlocking is allowed + if (_sender == _lockManager) { + return true; + } + + // If the sender is neither the lock manager nor the owner, unlocking is not allowed + if (_sender != _user) { + return false; + } + + // The sender must therefore be the owner of the tokens + // Allow unlocking if the amount of locked tokens has already been decreased to 0 + if (amount == 0) { + return true; + } + + // Otherwise, check whether the lock manager allows unlocking + return ILockManager(_lockManager).canUnlock(_user, amount); + } + + function _toBytes4(bytes memory _data) internal pure returns (bytes4 result) { + if (_data.length < 4) { + return bytes4(0); + } + + assembly { result := mload(add(_data, 0x20)) } + } +} diff --git a/tests-solidity/suites/staking/contracts/StakingFactory.sol b/tests-solidity/suites/staking/contracts/StakingFactory.sol new file mode 100644 index 0000000000..f37b85279d --- /dev/null +++ b/tests-solidity/suites/staking/contracts/StakingFactory.sol @@ -0,0 +1,44 @@ +pragma solidity ^0.5.17; + +import "./lib/os/ERC20.sol"; + +import "./Staking.sol"; +import "./proxies/StakingProxy.sol"; + + +contract StakingFactory { + Staking public baseImplementation; + mapping (address => address) internal instances; + + event NewStaking(address indexed instance, address token); + + constructor() public { + baseImplementation = new Staking(); + } + + function existsInstance(ERC20 token) external view returns (bool) { + return _getInstance(token) != address(0); + } + + function getInstance(ERC20 token) external view returns (Staking) { + return Staking(_getInstance(token)); + } + + function getOrCreateInstance(ERC20 token) external returns (Staking) { + address instance = _getInstance(token); + return instance != address(0) ? Staking(instance) : _createInstance(token); + } + + function _getInstance(ERC20 token) internal view returns (address) { + return instances[address(token)]; + } + + function _createInstance(ERC20 token) internal returns (Staking) { + StakingProxy instance = new StakingProxy(baseImplementation, token); + address tokenAddress = address(token); + address instanceAddress = address(instance); + instances[tokenAddress] = instanceAddress; + emit NewStaking(instanceAddress, tokenAddress); + return Staking(instanceAddress); + } +} diff --git a/tests-solidity/suites/staking/contracts/lib/Checkpointing.sol b/tests-solidity/suites/staking/contracts/lib/Checkpointing.sol new file mode 100644 index 0000000000..aa37110651 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/lib/Checkpointing.sol @@ -0,0 +1,155 @@ +pragma solidity ^0.5.17; + + +/** +* @title Checkpointing - Library to handle a historic set of numeric values +*/ +library Checkpointing { + uint256 private constant MAX_UINT192 = uint256(uint192(-1)); + + string private constant ERROR_VALUE_TOO_BIG = "CHECKPOINT_VALUE_TOO_BIG"; + string private constant ERROR_CANNOT_ADD_PAST_VALUE = "CHECKPOINT_CANNOT_ADD_PAST_VALUE"; + + /** + * @dev To specify a value at a given point in time, we need to store two values: + * - `time`: unit-time value to denote the first time when a value was registered + * - `value`: a positive numeric value to registered at a given point in time + * + * Note that `time` does not need to refer necessarily to a timestamp value, any time unit could be used + * for it like block numbers, terms, etc. + */ + struct Checkpoint { + uint64 time; + uint192 value; + } + + /** + * @dev A history simply denotes a list of checkpoints + */ + struct History { + Checkpoint[] history; + } + + /** + * @dev Add a new value to a history for a given point in time. This function does not allow to add values previous + * to the latest registered value, if the value willing to add corresponds to the latest registered value, it + * will be updated. + * @param self Checkpoints history to be altered + * @param _time Point in time to register the given value + * @param _value Numeric value to be registered at the given point in time + */ + function add(History storage self, uint64 _time, uint256 _value) internal { + require(_value <= MAX_UINT192, ERROR_VALUE_TOO_BIG); + _add192(self, _time, uint192(_value)); + } + + /** + * TODO + */ + function lastUpdate(History storage self) internal view returns (uint256) { + uint256 length = self.history.length; + + if (length > 0) { + return uint256(self.history[length - 1].time); + } + + return 0; + } + + /** + * @dev Fetch the latest registered value of history, it will return zero if there was no value registered + * @param self Checkpoints history to be queried + */ + function getLast(History storage self) internal view returns (uint256) { + uint256 length = self.history.length; + if (length > 0) { + return uint256(self.history[length - 1].value); + } + + return 0; + } + + /** + * @dev Fetch the most recent registered past value of a history based on a given point in time that is not known + * how recent it is beforehand. It will return zero if there is no registered value or if given time is + * previous to the first registered value. + * It uses a binary search. + * @param self Checkpoints history to be queried + * @param _time Point in time to query the most recent registered past value of + */ + function get(History storage self, uint64 _time) internal view returns (uint256) { + return _binarySearch(self, _time); + } + + /** + * @dev Private function to add a new value to a history for a given point in time. This function does not allow to + * add values previous to the latest registered value, if the value willing to add corresponds to the latest + * registered value, it will be updated. + * @param self Checkpoints history to be altered + * @param _time Point in time to register the given value + * @param _value Numeric value to be registered at the given point in time + */ + function _add192(History storage self, uint64 _time, uint192 _value) private { + uint256 length = self.history.length; + if (length == 0 || self.history[self.history.length - 1].time < _time) { + // If there was no value registered or the given point in time is after the latest registered value, + // we can insert it to the history directly. + self.history.push(Checkpoint(_time, _value)); + } else { + // If the point in time given for the new value is not after the latest registered value, we must ensure + // we are only trying to update the latest value, otherwise we would be changing past data. + Checkpoint storage currentCheckpoint = self.history[length - 1]; + require(_time == currentCheckpoint.time, ERROR_CANNOT_ADD_PAST_VALUE); + currentCheckpoint.value = _value; + } + } + + /** + * @dev Private function execute a binary search to find the most recent registered past value of a history based on + * a given point in time. It will return zero if there is no registered value or if given time is previous to + * the first registered value. Note that this function will be more suitable when don't know how recent the + * time used to index may be. + * @param self Checkpoints history to be queried + * @param _time Point in time to query the most recent registered past value of + */ + function _binarySearch(History storage self, uint64 _time) private view returns (uint256) { + // If there was no value registered for the given history return simply zero + uint256 length = self.history.length; + if (length == 0) { + return 0; + } + + // If the requested time is equal to or after the time of the latest registered value, return latest value + uint256 lastIndex = length - 1; + if (_time >= self.history[lastIndex].time) { + return uint256(self.history[lastIndex].value); + } + + // If the requested time is previous to the first registered value, return zero to denote missing checkpoint + if (_time < self.history[0].time) { + return 0; + } + + // Execute a binary search between the checkpointed times of the history + uint256 low = 0; + uint256 high = lastIndex; + + while (high > low) { + // No need for SafeMath: for this to overflow array size should be ~2^255 + uint256 mid = (high + low + 1) / 2; + Checkpoint storage checkpoint = self.history[mid]; + uint64 midTime = checkpoint.time; + + if (_time > midTime) { + low = mid; + } else if (_time < midTime) { + // No need for SafeMath: high > low >= 0 => high >= 1 => mid >= 1 + high = mid - 1; + } else { + return uint256(checkpoint.value); + } + } + + return uint256(self.history[low].value); + } +} diff --git a/tests-solidity/suites/staking/contracts/lib/os/Autopetrified.sol b/tests-solidity/suites/staking/contracts/lib/os/Autopetrified.sol new file mode 100644 index 0000000000..2ad29273fd --- /dev/null +++ b/tests-solidity/suites/staking/contracts/lib/os/Autopetrified.sol @@ -0,0 +1,15 @@ +// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/Autopetrified.sol +// Adapted to use pragma ^0.5.17 and satisfy our linter rules + +pragma solidity ^0.5.17; + +import "./Petrifiable.sol"; + + +contract Autopetrified is Petrifiable { + constructor() public { + // Immediately petrify base (non-proxy) instances of inherited contracts on deploy. + // This renders them uninitializable (and unusable without a proxy). + petrify(); + } +} diff --git a/tests-solidity/suites/staking/contracts/lib/os/DelegateProxy.sol b/tests-solidity/suites/staking/contracts/lib/os/DelegateProxy.sol new file mode 100644 index 0000000000..0d7158a06d --- /dev/null +++ b/tests-solidity/suites/staking/contracts/lib/os/DelegateProxy.sol @@ -0,0 +1,34 @@ +// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/Autopetrified.sol +// Adapted to use pragma ^0.5.17 and satisfy our linter rules + +pragma solidity 0.5.17; + +import "./ERCProxy.sol"; +import "./IsContract.sol"; + + +contract DelegateProxy is ERCProxy, IsContract { + uint256 internal constant FWD_GAS_LIMIT = 10000; + + /** + * @dev Performs a delegatecall and returns whatever the delegatecall returned (entire context execution will return!) + * @param _dst Destination address to perform the delegatecall + * @param _calldata Calldata for the delegatecall + */ + function delegatedFwd(address _dst, bytes memory _calldata) internal { + require(isContract(_dst)); + uint256 fwdGasLimit = FWD_GAS_LIMIT; + + assembly { + let result := delegatecall(sub(gas, fwdGasLimit), _dst, add(_calldata, 0x20), mload(_calldata), 0, 0) + let size := returndatasize + let ptr := mload(0x40) + returndatacopy(ptr, 0, size) + + // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas. + // if the call returned error data, forward it + switch result case 0 { revert(ptr, size) } + default { return(ptr, size) } + } + } +} diff --git a/tests-solidity/suites/staking/contracts/lib/os/ERC20.sol b/tests-solidity/suites/staking/contracts/lib/os/ERC20.sol new file mode 100644 index 0000000000..fa792cada2 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/lib/os/ERC20.sol @@ -0,0 +1,35 @@ +// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/lib/token/ERC20.sol +// Adapted to use pragma ^0.5.8 and satisfy our linter rules + +pragma solidity ^0.5.8; + + +/** + * @title ERC20 interface + * @dev see https://github.com/ethereum/EIPs/issues/20 + */ +contract ERC20 { + function totalSupply() public view returns (uint256); + + function balanceOf(address _who) public view returns (uint256); + + function allowance(address _owner, address _spender) public view returns (uint256); + + function transfer(address _to, uint256 _value) public returns (bool); + + function approve(address _spender, uint256 _value) public returns (bool); + + function transferFrom(address _from, address _to, uint256 _value) public returns (bool); + + event Transfer( + address indexed from, + address indexed to, + uint256 value + ); + + event Approval( + address indexed owner, + address indexed spender, + uint256 value + ); +} diff --git a/tests-solidity/suites/staking/contracts/lib/os/ERCProxy.sol b/tests-solidity/suites/staking/contracts/lib/os/ERCProxy.sol new file mode 100644 index 0000000000..6159ca5c5d --- /dev/null +++ b/tests-solidity/suites/staking/contracts/lib/os/ERCProxy.sol @@ -0,0 +1,13 @@ +// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/lib/misc/ERCProxy.sol +// Adapted to use pragma ^0.5.17 and satisfy our linter rules + +pragma solidity ^0.5.17; + + +contract ERCProxy { + uint256 internal constant FORWARDING = 1; + uint256 internal constant UPGRADEABLE = 2; + + function proxyType() public pure returns (uint256 proxyTypeId); + function implementation() public view returns (address codeAddr); +} diff --git a/tests-solidity/suites/staking/contracts/lib/os/Initializable.sol b/tests-solidity/suites/staking/contracts/lib/os/Initializable.sol new file mode 100644 index 0000000000..83ab5737a8 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/lib/os/Initializable.sol @@ -0,0 +1,58 @@ +// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/Initializable.sol +// Adapted to use pragma ^0.5.17 and satisfy our linter rules + +pragma solidity ^0.5.17; + +import "./TimeHelpers.sol"; +import "./UnstructuredStorage.sol"; + + +contract Initializable is TimeHelpers { + using UnstructuredStorage for bytes32; + + // keccak256("aragonOS.initializable.initializationBlock") + bytes32 internal constant INITIALIZATION_BLOCK_POSITION = 0xebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e; + + string private constant ERROR_ALREADY_INITIALIZED = "INIT_ALREADY_INITIALIZED"; + string private constant ERROR_NOT_INITIALIZED = "INIT_NOT_INITIALIZED"; + + modifier onlyInit { + require(getInitializationBlock() == 0, ERROR_ALREADY_INITIALIZED); + _; + } + + modifier isInitialized { + require(hasInitialized(), ERROR_NOT_INITIALIZED); + _; + } + + /** + * @return Block number in which the contract was initialized + */ + function getInitializationBlock() public view returns (uint256) { + return INITIALIZATION_BLOCK_POSITION.getStorageUint256(); + } + + /** + * @return Whether the contract has been initialized by the time of the current block + */ + function hasInitialized() public view returns (bool) { + uint256 initializationBlock = getInitializationBlock(); + return initializationBlock != 0 && getBlockNumber() >= initializationBlock; + } + + /** + * @dev Function to be called by top level contract after initialization has finished. + */ + function initialized() internal onlyInit { + INITIALIZATION_BLOCK_POSITION.setStorageUint256(getBlockNumber()); + } + + /** + * @dev Function to be called by top level contract after initialization to enable the contract + * at a future block number rather than immediately. + */ + function initializedAt(uint256 _blockNumber) internal onlyInit { + INITIALIZATION_BLOCK_POSITION.setStorageUint256(_blockNumber); + } +} diff --git a/tests-solidity/suites/staking/contracts/lib/os/IsContract.sol b/tests-solidity/suites/staking/contracts/lib/os/IsContract.sol new file mode 100644 index 0000000000..275f4b3d12 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/lib/os/IsContract.sol @@ -0,0 +1,24 @@ +// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/IsContract.sol +// Adapted to use pragma ^0.5.8 and satisfy our linter rules + +pragma solidity ^0.5.8; + + +contract IsContract { + /* + * NOTE: this should NEVER be used for authentication + * (see pitfalls: https://github.com/fergarrui/ethereum-security/tree/master/contracts/extcodesize). + * + * This is only intended to be used as a sanity check that an address is actually a contract, + * RATHER THAN an address not being a contract. + */ + function isContract(address _target) internal view returns (bool) { + if (_target == address(0)) { + return false; + } + + uint256 size; + assembly { size := extcodesize(_target) } + return size > 0; + } +} diff --git a/tests-solidity/suites/staking/contracts/lib/os/Migrations.sol b/tests-solidity/suites/staking/contracts/lib/os/Migrations.sol new file mode 100644 index 0000000000..948cf3b5d1 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/lib/os/Migrations.sol @@ -0,0 +1,29 @@ +// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/lib/misc/Migrations.sol +// Adapted to use pragma ^0.5.8 and satisfy our linter rules + +pragma solidity ^0.5.8; + + +contract Migrations { + address public owner; + uint256 public lastCompletedMigration; + + modifier restricted() { + if (msg.sender == owner) { + _; + } + } + + constructor() public { + owner = msg.sender; + } + + function setCompleted(uint256 completed) public restricted { + lastCompletedMigration = completed; + } + + function upgrade(address newAddress) public restricted { + Migrations upgraded = Migrations(newAddress); + upgraded.setCompleted(lastCompletedMigration); + } +} diff --git a/tests-solidity/suites/staking/contracts/lib/os/Petrifiable.sol b/tests-solidity/suites/staking/contracts/lib/os/Petrifiable.sol new file mode 100644 index 0000000000..5690b3b99f --- /dev/null +++ b/tests-solidity/suites/staking/contracts/lib/os/Petrifiable.sol @@ -0,0 +1,24 @@ +// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/Petrifiable.sol +// Adapted to use pragma ^0.5.17 and satisfy our linter rules + +pragma solidity ^0.5.17; + +import "./Initializable.sol"; + + +contract Petrifiable is Initializable { + // Use block UINT256_MAX (which should be never) as the initializable date + uint256 internal constant PETRIFIED_BLOCK = uint256(-1); + + function isPetrified() public view returns (bool) { + return getInitializationBlock() == PETRIFIED_BLOCK; + } + + /** + * @dev Function to be called by top level contract to prevent being initialized. + * Useful for freezing base contracts when they're used behind proxies. + */ + function petrify() internal onlyInit { + initializedAt(PETRIFIED_BLOCK); + } +} diff --git a/tests-solidity/suites/staking/contracts/lib/os/SafeERC20.sol b/tests-solidity/suites/staking/contracts/lib/os/SafeERC20.sol new file mode 100644 index 0000000000..d5b79432d5 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/lib/os/SafeERC20.sol @@ -0,0 +1,91 @@ +// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/SafeERC20.sol +// Adapted to use pragma ^0.5.8 and satisfy our linter rules + +pragma solidity ^0.5.8; + +import "./ERC20.sol"; + + +library SafeERC20 { + // Before 0.5, solidity has a mismatch between `address.transfer()` and `token.transfer()`: + // https://github.com/ethereum/solidity/issues/3544 + bytes4 private constant TRANSFER_SELECTOR = 0xa9059cbb; + + /** + * @dev Same as a standards-compliant ERC20.transfer() that never reverts (returns false). + * Note that this makes an external call to the token. + */ + function safeTransfer(ERC20 _token, address _to, uint256 _amount) internal returns (bool) { + bytes memory transferCallData = abi.encodeWithSelector( + TRANSFER_SELECTOR, + _to, + _amount + ); + return invokeAndCheckSuccess(address(_token), transferCallData); + } + + /** + * @dev Same as a standards-compliant ERC20.transferFrom() that never reverts (returns false). + * Note that this makes an external call to the token. + */ + function safeTransferFrom(ERC20 _token, address _from, address _to, uint256 _amount) internal returns (bool) { + bytes memory transferFromCallData = abi.encodeWithSelector( + _token.transferFrom.selector, + _from, + _to, + _amount + ); + return invokeAndCheckSuccess(address(_token), transferFromCallData); + } + + /** + * @dev Same as a standards-compliant ERC20.approve() that never reverts (returns false). + * Note that this makes an external call to the token. + */ + function safeApprove(ERC20 _token, address _spender, uint256 _amount) internal returns (bool) { + bytes memory approveCallData = abi.encodeWithSelector( + _token.approve.selector, + _spender, + _amount + ); + return invokeAndCheckSuccess(address(_token), approveCallData); + } + + function invokeAndCheckSuccess(address _addr, bytes memory _calldata) private returns (bool) { + bool ret; + assembly { + let ptr := mload(0x40) // free memory pointer + + let success := call( + gas, // forward all gas + _addr, // address + 0, // no value + add(_calldata, 0x20), // calldata start + mload(_calldata), // calldata length + ptr, // write output over free memory + 0x20 // uint256 return + ) + + if gt(success, 0) { + // Check number of bytes returned from last function call + switch returndatasize + + // No bytes returned: assume success + case 0 { + ret := 1 + } + + // 32 bytes returned: check if non-zero + case 0x20 { + // Only return success if returned data was true + // Already have output in ptr + ret := eq(mload(ptr), 1) + } + + // Not sure what was returned: don't mark as success + default { } + } + } + return ret; + } +} diff --git a/tests-solidity/suites/staking/contracts/lib/os/SafeMath.sol b/tests-solidity/suites/staking/contracts/lib/os/SafeMath.sol new file mode 100644 index 0000000000..72011ac561 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/lib/os/SafeMath.sol @@ -0,0 +1,73 @@ +// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/lib/math/SafeMath.sol +// Adapted to use pragma ^0.5.8 and satisfy our linter rules + +pragma solidity >=0.4.24 <0.6.0; + + +/** + * @title SafeMath + * @dev Math operations with safety checks that revert on error + */ +library SafeMath { + string private constant ERROR_ADD_OVERFLOW = "MATH_ADD_OVERFLOW"; + string private constant ERROR_SUB_UNDERFLOW = "MATH_SUB_UNDERFLOW"; + string private constant ERROR_MUL_OVERFLOW = "MATH_MUL_OVERFLOW"; + string private constant ERROR_DIV_ZERO = "MATH_DIV_ZERO"; + + /** + * @dev Multiplies two numbers, reverts on overflow. + */ + function mul(uint256 _a, uint256 _b) internal pure returns (uint256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 + if (_a == 0) { + return 0; + } + + uint256 c = _a * _b; + require(c / _a == _b, ERROR_MUL_OVERFLOW); + + return c; + } + + /** + * @dev Integer division of two numbers truncating the quotient, reverts on division by zero. + */ + function div(uint256 _a, uint256 _b) internal pure returns (uint256) { + require(_b > 0, ERROR_DIV_ZERO); // Solidity only automatically asserts when dividing by 0 + uint256 c = _a / _b; + // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend). + */ + function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { + require(_b <= _a, ERROR_SUB_UNDERFLOW); + uint256 c = _a - _b; + + return c; + } + + /** + * @dev Adds two numbers, reverts on overflow. + */ + function add(uint256 _a, uint256 _b) internal pure returns (uint256) { + uint256 c = _a + _b; + require(c >= _a, ERROR_ADD_OVERFLOW); + + return c; + } + + /** + * @dev Divides two numbers and returns the remainder (unsigned integer modulo), + * reverts when dividing by zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + require(b != 0, ERROR_DIV_ZERO); + return a % b; + } +} diff --git a/tests-solidity/suites/staking/contracts/lib/os/SafeMath64.sol b/tests-solidity/suites/staking/contracts/lib/os/SafeMath64.sol new file mode 100644 index 0000000000..b9df559ff2 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/lib/os/SafeMath64.sol @@ -0,0 +1,66 @@ +// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/lib/math/SafeMath64.sol +// Adapted to use pragma ^0.5.8 and satisfy our linter rules + +pragma solidity ^0.5.8; + + +/** + * @title SafeMath64 + * @dev Math operations for uint64 with safety checks that revert on error + */ +library SafeMath64 { + string private constant ERROR_ADD_OVERFLOW = "MATH64_ADD_OVERFLOW"; + string private constant ERROR_SUB_UNDERFLOW = "MATH64_SUB_UNDERFLOW"; + string private constant ERROR_MUL_OVERFLOW = "MATH64_MUL_OVERFLOW"; + string private constant ERROR_DIV_ZERO = "MATH64_DIV_ZERO"; + + /** + * @dev Multiplies two numbers, reverts on overflow. + */ + function mul(uint64 _a, uint64 _b) internal pure returns (uint64) { + uint256 c = uint256(_a) * uint256(_b); + require(c < 0x010000000000000000, ERROR_MUL_OVERFLOW); // 2**64 (less gas this way) + + return uint64(c); + } + + /** + * @dev Integer division of two numbers truncating the quotient, reverts on division by zero. + */ + function div(uint64 _a, uint64 _b) internal pure returns (uint64) { + require(_b > 0, ERROR_DIV_ZERO); // Solidity only automatically asserts when dividing by 0 + uint64 c = _a / _b; + // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend). + */ + function sub(uint64 _a, uint64 _b) internal pure returns (uint64) { + require(_b <= _a, ERROR_SUB_UNDERFLOW); + uint64 c = _a - _b; + + return c; + } + + /** + * @dev Adds two numbers, reverts on overflow. + */ + function add(uint64 _a, uint64 _b) internal pure returns (uint64) { + uint64 c = _a + _b; + require(c >= _a, ERROR_ADD_OVERFLOW); + + return c; + } + + /** + * @dev Divides two numbers and returns the remainder (unsigned integer modulo), + * reverts when dividing by zero. + */ + function mod(uint64 a, uint64 b) internal pure returns (uint64) { + require(b != 0, ERROR_DIV_ZERO); + return a % b; + } +} diff --git a/tests-solidity/suites/staking/contracts/lib/os/ScriptHelpers.sol b/tests-solidity/suites/staking/contracts/lib/os/ScriptHelpers.sol new file mode 100644 index 0000000000..13cfa2e1a4 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/lib/os/ScriptHelpers.sol @@ -0,0 +1,47 @@ +// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/evmscript/ScriptHelpers.sol +// Adapted to use pragma ^0.5.17 and satisfy our linter rules + +pragma solidity ^0.5.17; + + +library ScriptHelpers { + function getSpecId(bytes memory _script) internal pure returns (uint32) { + return uint32At(_script, 0); + } + + function uint256At(bytes memory _data, uint256 _location) internal pure returns (uint256 result) { + assembly { + result := mload(add(_data, add(0x20, _location))) + } + } + + function addressAt(bytes memory _data, uint256 _location) internal pure returns (address result) { + uint256 word = uint256At(_data, _location); + + assembly { + result := div(and(word, 0xffffffffffffffffffffffffffffffffffffffff000000000000000000000000), + 0x1000000000000000000000000) + } + } + + function uint32At(bytes memory _data, uint256 _location) internal pure returns (uint32 result) { + uint256 word = uint256At(_data, _location); + + assembly { + result := div(and(word, 0xffffffff00000000000000000000000000000000000000000000000000000000), + 0x100000000000000000000000000000000000000000000000000000000) + } + } + + function locationOf(bytes memory _data, uint256 _location) internal pure returns (uint256 result) { + assembly { + result := add(_data, add(0x20, _location)) + } + } + + function toBytes(bytes4 _sig) internal pure returns (bytes memory) { + bytes memory payload = new bytes(4); + assembly { mstore(add(payload, 0x20), _sig) } + return payload; + } +} diff --git a/tests-solidity/suites/staking/contracts/lib/os/TimeHelpers.sol b/tests-solidity/suites/staking/contracts/lib/os/TimeHelpers.sol new file mode 100644 index 0000000000..c952da14d2 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/lib/os/TimeHelpers.sol @@ -0,0 +1,47 @@ +// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/TimeHelpers.sol +// Adapted to use pragma ^0.5.8 and satisfy our linter rules + +pragma solidity ^0.5.8; + +import "./Uint256Helpers.sol"; + + +contract TimeHelpers { + using Uint256Helpers for uint256; + + /** + * @dev Returns the current block number. + * Using a function rather than `block.number` allows us to easily mock the block number in + * tests. + */ + function getBlockNumber() internal view returns (uint256) { + return block.number; + } + + /** + * @dev Returns the current block number, converted to uint64. + * Using a function rather than `block.number` allows us to easily mock the block number in + * tests. + */ + function getBlockNumber64() internal view returns (uint64) { + return getBlockNumber().toUint64(); + } + + /** + * @dev Returns the current timestamp. + * Using a function rather than `block.timestamp` allows us to easily mock it in + * tests. + */ + function getTimestamp() internal view returns (uint256) { + return block.timestamp; // solium-disable-line security/no-block-members + } + + /** + * @dev Returns the current timestamp, converted to uint64. + * Using a function rather than `block.timestamp` allows us to easily mock it in + * tests. + */ + function getTimestamp64() internal view returns (uint64) { + return getTimestamp().toUint64(); + } +} diff --git a/tests-solidity/suites/staking/contracts/lib/os/Uint256Helpers.sol b/tests-solidity/suites/staking/contracts/lib/os/Uint256Helpers.sol new file mode 100644 index 0000000000..cfd8cd0644 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/lib/os/Uint256Helpers.sol @@ -0,0 +1,23 @@ +// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/Uint256Helpers.sol +// Adapted to use pragma ^0.5.8 and satisfy our linter rules + +pragma solidity ^0.5.8; + + +library Uint256Helpers { + uint256 private constant MAX_UINT8 = uint8(-1); + uint256 private constant MAX_UINT64 = uint64(-1); + + string private constant ERROR_UINT8_NUMBER_TOO_BIG = "UINT8_NUMBER_TOO_BIG"; + string private constant ERROR_UINT64_NUMBER_TOO_BIG = "UINT64_NUMBER_TOO_BIG"; + + function toUint8(uint256 a) internal pure returns (uint8) { + require(a <= MAX_UINT8, ERROR_UINT8_NUMBER_TOO_BIG); + return uint8(a); + } + + function toUint64(uint256 a) internal pure returns (uint64) { + require(a <= MAX_UINT64, ERROR_UINT64_NUMBER_TOO_BIG); + return uint64(a); + } +} diff --git a/tests-solidity/suites/staking/contracts/lib/os/UnstructuredStorage.sol b/tests-solidity/suites/staking/contracts/lib/os/UnstructuredStorage.sol new file mode 100644 index 0000000000..5082877910 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/lib/os/UnstructuredStorage.sol @@ -0,0 +1,39 @@ +// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/UnstructuredStorage.sol +// Adapted to use pragma ^0.5.17 and satisfy our linter rules + +pragma solidity ^0.5.17; + + +library UnstructuredStorage { + function getStorageBool(bytes32 position) internal view returns (bool data) { + assembly { data := sload(position) } + } + + function getStorageAddress(bytes32 position) internal view returns (address data) { + assembly { data := sload(position) } + } + + function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) { + assembly { data := sload(position) } + } + + function getStorageUint256(bytes32 position) internal view returns (uint256 data) { + assembly { data := sload(position) } + } + + function setStorageBool(bytes32 position, bool data) internal { + assembly { sstore(position, data) } + } + + function setStorageAddress(bytes32 position, address data) internal { + assembly { sstore(position, data) } + } + + function setStorageBytes32(bytes32 position, bytes32 data) internal { + assembly { sstore(position, data) } + } + + function setStorageUint256(bytes32 position, uint256 data) internal { + assembly { sstore(position, data) } + } +} diff --git a/tests-solidity/suites/staking/contracts/locking/ILockManager.sol b/tests-solidity/suites/staking/contracts/locking/ILockManager.sol new file mode 100644 index 0000000000..e8d570b18e --- /dev/null +++ b/tests-solidity/suites/staking/contracts/locking/ILockManager.sol @@ -0,0 +1,12 @@ +pragma solidity ^0.5.17; + + +interface ILockManager { + /** + * @notice Check if `_user`'s by `_lockManager` can be unlocked + * @param _user Owner of lock + * @param _amount Amount of locked tokens to unlock + * @return Whether given lock of given owner can be unlocked by given sender + */ + function canUnlock(address _user, uint256 _amount) external view returns (bool); +} diff --git a/tests-solidity/suites/staking/contracts/locking/IStakingLocking.sol b/tests-solidity/suites/staking/contracts/locking/IStakingLocking.sol new file mode 100644 index 0000000000..93b2b7a0b3 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/locking/IStakingLocking.sol @@ -0,0 +1,31 @@ +pragma solidity ^0.5.17; + + +interface IStakingLocking { + event NewLockManager(address indexed account, address indexed lockManager, bytes data); + event Unlocked(address indexed account, address indexed lockManager, uint256 amount); + event LockAmountChanged(address indexed account, address indexed lockManager, uint256 amount, bool increase); + event LockAllowanceChanged(address indexed account, address indexed lockManager, uint256 allowance, bool increase); + event LockManagerRemoved(address indexed account, address lockManager); + event LockManagerTransferred(address indexed account, address indexed oldLockManager, address newLockManager); + event StakeTransferred(address indexed from, address to, uint256 amount); + + function allowManager(address _lockManager, uint256 _allowance, bytes calldata _data) external; + function allowManagerAndLock(uint256 _amount, address _lockManager, uint256 _allowance, bytes calldata _data) external; + function unlockAndRemoveManager(address _account, address _lockManager) external; + function increaseLockAllowance(address _lockManager, uint256 _allowance) external; + function decreaseLockAllowance(address _account, address _lockManager, uint256 _allowance) external; + function lock(address _account, address _lockManager, uint256 _amount) external; + function unlock(address _account, address _lockManager, uint256 _amount) external; + function setLockManager(address _account, address _newLockManager) external; + function transfer(address _to, uint256 _amount) external; + function transferAndUnstake(address _to, uint256 _amount) external; + function slash(address _account, address _to, uint256 _amount) external; + function slashAndUnstake(address _account, address _to, uint256 _amount) external; + + function getLock(address _account, address _lockManager) external view returns (uint256 _amount, uint256 _allowance); + function unlockedBalanceOf(address _account) external view returns (uint256); + function lockedBalanceOf(address _user) external view returns (uint256); + function getBalancesOf(address _user) external view returns (uint256 staked, uint256 locked); + function canUnlock(address _sender, address _account, address _lockManager, uint256 _amount) external view returns (bool); +} diff --git a/tests-solidity/suites/staking/contracts/locking/TimeLockManager.sol b/tests-solidity/suites/staking/contracts/locking/TimeLockManager.sol new file mode 100644 index 0000000000..35b63d2d47 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/locking/TimeLockManager.sol @@ -0,0 +1,72 @@ +pragma solidity 0.5.17; + +import "../lib/os/TimeHelpers.sol"; +import "../lib/os/ScriptHelpers.sol"; + +import "../locking/ILockManager.sol"; +import "../locking/IStakingLocking.sol"; + + +/** + * Time based lock manager for Staking contract + * Allows to set a time interval, either in blocks or seconds, during which the funds are locked. + * Outside that window the owner can unlock them. + */ +contract TimeLockManager is ILockManager, TimeHelpers { + using ScriptHelpers for bytes; + + string private constant ERROR_ALREADY_LOCKED = "TLM_ALREADY_LOCKED"; + string private constant ERROR_WRONG_INTERVAL = "TLM_WRONG_INTERVAL"; + + enum TimeUnit { Blocks, Seconds } + + struct TimeInterval { + uint256 unit; + uint256 start; + uint256 end; + } + + mapping (address => TimeInterval) internal timeIntervals; + + event LogLockCallback(uint256 amount, uint256 allowance, bytes data); + + /** + * @notice Set a locked amount, along with a time interval, either in blocks or seconds during which the funds are locked. + * @param _staking The Staking contract holding the lock + * @param _owner The account owning the locked funds + * @param _amount The amount to be locked + * @param _unit Blocks or seconds, the unit for the time interval + * @param _start The start of the time interval + * @param _end The end of the time interval + */ + function lock(IStakingLocking _staking, address _owner, uint256 _amount, uint256 _unit, uint256 _start, uint256 _end) external { + require(timeIntervals[_owner].end == 0, ERROR_ALREADY_LOCKED); + require(_end > _start, ERROR_WRONG_INTERVAL); + timeIntervals[_owner] = TimeInterval(_unit, _start, _end); + + _staking.lock(_owner, address(this), _amount); + } + + /** + * @notice Check if the owner can unlock the funds, i.e., if current timestamp is outside the lock interval + * @param _owner Owner of the locked funds + * @return True if current timestamp is outside the lock interval + */ + function canUnlock(address _owner, uint256) external view returns (bool) { + TimeInterval storage timeInterval = timeIntervals[_owner]; + uint256 comparingValue; + if (timeInterval.unit == uint256(TimeUnit.Blocks)) { + comparingValue = getBlockNumber(); + } else { + comparingValue = getTimestamp(); + } + + return comparingValue < timeInterval.start || comparingValue > timeInterval.end; + } + + function getTimeInterval(address _owner) external view returns (uint256 unit, uint256 start, uint256 end) { + TimeInterval storage timeInterval = timeIntervals[_owner]; + + return (timeInterval.unit, timeInterval.start, timeInterval.end); + } +} diff --git a/tests-solidity/suites/staking/contracts/proxies/StakingProxy.sol b/tests-solidity/suites/staking/contracts/proxies/StakingProxy.sol new file mode 100644 index 0000000000..adffd13199 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/proxies/StakingProxy.sol @@ -0,0 +1,31 @@ +pragma solidity ^0.5.17; + +import "../lib/os/ERC20.sol"; + +import "../Staking.sol"; +import "./ThinProxy.sol"; + + +contract StakingProxy is ThinProxy { + // keccak256("aragon.network.staking") + bytes32 internal constant IMPLEMENTATION_SLOT = 0xbd536e2e005accda865e2f0d1827f83ec8824f3ea04ecd6131b7c10058635814; + + constructor(Staking _implementation, ERC20 _token) ThinProxy(address(_implementation)) public { + bytes4 selector = _implementation.initialize.selector; + bytes memory initializeData = abi.encodeWithSelector(selector, _token); + (bool success,) = address(_implementation).delegatecall(initializeData); + + if (!success) { + assembly { + let output := mload(0x40) + mstore(0x40, add(output, returndatasize)) + returndatacopy(output, 0, returndatasize) + revert(output, returndatasize) + } + } + } + + function _implementationSlot() internal pure returns (bytes32) { + return IMPLEMENTATION_SLOT; + } +} diff --git a/tests-solidity/suites/staking/contracts/proxies/ThinProxy.sol b/tests-solidity/suites/staking/contracts/proxies/ThinProxy.sol new file mode 100644 index 0000000000..085fb27530 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/proxies/ThinProxy.sol @@ -0,0 +1,27 @@ +pragma solidity ^0.5.17; + +import "../lib/os/DelegateProxy.sol"; +import "../lib/os/UnstructuredStorage.sol"; + + +contract ThinProxy is DelegateProxy { + using UnstructuredStorage for bytes32; + + constructor(address _implementation) public { + _implementationSlot().setStorageAddress(_implementation); + } + + function () external { + delegatedFwd(implementation(), msg.data); + } + + function proxyType() public pure returns (uint256) { + return FORWARDING; + } + + function implementation() public view returns (address) { + return _implementationSlot().getStorageAddress(); + } + + function _implementationSlot() internal pure returns (bytes32); +} diff --git a/tests-solidity/suites/staking/contracts/standards/ERC900.sol b/tests-solidity/suites/staking/contracts/standards/ERC900.sol new file mode 100644 index 0000000000..829d219a01 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/standards/ERC900.sol @@ -0,0 +1,55 @@ +pragma solidity ^0.5.17; + + +// Interface for ERC900: https://eips.ethereum.org/EIPS/eip-900 +interface ERC900 { + event Staked(address indexed user, uint256 amount, uint256 total, bytes data); + event Unstaked(address indexed user, uint256 amount, uint256 total, bytes data); + + /** + * @dev Stake a certain amount of tokens + * @param _amount Amount of tokens to be staked + * @param _data Optional data that can be used to add signalling information in more complex staking applications + */ + function stake(uint256 _amount, bytes calldata _data) external; + + /** + * @dev Stake a certain amount of tokens in favor of someone + * @param _user Address to stake an amount of tokens to + * @param _amount Amount of tokens to be staked + * @param _data Optional data that can be used to add signalling information in more complex staking applications + */ + function stakeFor(address _user, uint256 _amount, bytes calldata _data) external; + + /** + * @dev Unstake a certain amount of tokens + * @param _amount Amount of tokens to be unstaked + * @param _data Optional data that can be used to add signalling information in more complex staking applications + */ + function unstake(uint256 _amount, bytes calldata _data) external; + + /** + * @dev Tell the total amount of tokens staked for an address + * @param _addr Address querying the total amount of tokens staked for + * @return Total amount of tokens staked for an address + */ + function totalStakedFor(address _addr) external view returns (uint256); + + /** + * @dev Tell the total amount of tokens staked + * @return Total amount of tokens staked + */ + function totalStaked() external view returns (uint256); + + /** + * @dev Tell the address of the token used for staking + * @return Address of the token used for staking + */ + function token() external view returns (address); + + /* + * @dev Tell if the current registry supports historic information or not + * @return True if the optional history functions are implemented, false otherwise + */ + function supportsHistory() external pure returns (bool); +} diff --git a/tests-solidity/suites/staking/contracts/test/TestImports.sol b/tests-solidity/suites/staking/contracts/test/TestImports.sol new file mode 100644 index 0000000000..ae301fc667 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/test/TestImports.sol @@ -0,0 +1,23 @@ +pragma solidity 0.5.17; + +import "./lib/MiniMeToken.sol"; +import "../lib/os/Migrations.sol"; + +// You might think this file is a bit odd, but let me explain. +// We only use some contracts in our tests, which means Truffle +// will not compile it for us, because it is from an external +// dependency. +// +// We are now left with three options: +// - Copy/paste these contracts +// - Run the tests with `truffle compile --all` on +// - Or trick Truffle by claiming we use it in a Solidity test +// +// You know which one I went for. + + +contract TestImports { + constructor() public { + // solium-disable-previous-line no-empty-blocks + } +} diff --git a/tests-solidity/suites/staking/contracts/test/lib/EchidnaStaking.sol b/tests-solidity/suites/staking/contracts/test/lib/EchidnaStaking.sol new file mode 100644 index 0000000000..b51ca623f8 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/test/lib/EchidnaStaking.sol @@ -0,0 +1,88 @@ +pragma solidity 0.5.17; + +import "../../Staking.sol"; +import "../../lib/os/SafeMath.sol"; +import "../mocks/NoApproveTokenMock.sol"; + + +contract EchidnaStaking is Staking { + using SafeMath for uint256; + + constructor() public { + stakingToken = ERC20(new NoApproveTokenMock(msg.sender, 10 ** 24)); + } + + // check that staked amount for an account is always >= total locked + function echidna_account_stake_locks() external view returns (bool) { + address _account = msg.sender; + Account storage account = accounts[_account]; + + if (_totalStakedFor(_account) < account.totalLocked) { + return false; + } + + return true; + } + + // TODO: delete. Fake test to check that previous echidna test works + function echidna_account_stake_locks_fake() external view returns (bool) { + address _account = msg.sender; + Account storage account = accounts[_account]; + + if (_totalStakedFor(_account) > account.totalLocked) { + return false; + } + + return true; + } + + + // check that Checkpointing history arrays are ordered + function echidna_global_history_is_ordered() external view returns (bool) { + for (uint256 i = 1; i < totalStakedHistory.history.length; i++) { + if (totalStakedHistory.history[i].time <= totalStakedHistory.history[i - 1].time) { + return false; + } + } + + return true; + } + + function echidna_user_history_is_ordered() external view returns (bool) { + address account = msg.sender; + for (uint256 i = 1; i < accounts[account].stakedHistory.history.length; i++) { + if (accounts[account].stakedHistory.history[i].time <= accounts[account].stakedHistory.history[i - 1].time) { + return false; + } + } + + return true; + } + + // total staked matches less or equal than token balance + function echidna_total_staked_is_balance() external view returns (bool) { + if (_totalStaked() <= stakingToken.balanceOf(address(this))) { + return true; + } + + return false; + } + + function echidna_staked_ge_unlocked() external view returns (bool) { + if (_unlockedBalanceOf(msg.sender) > _totalStakedFor(msg.sender)) { + return false; + } + + return true; + } + + function echidna_staked_ge_locked() external view returns (bool) { + if (_lockedBalanceOf(msg.sender) > _totalStakedFor(msg.sender)) { + return false; + } + + return true; + } + + // sum of all account stakes should be equal to total staked and to staking token balance of staking contract, but it's hard to compute as accounts is a mapping +} diff --git a/tests-solidity/suites/staking/contracts/test/lib/ITokenController.sol b/tests-solidity/suites/staking/contracts/test/lib/ITokenController.sol new file mode 100644 index 0000000000..39f7b76218 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/test/lib/ITokenController.sol @@ -0,0 +1,27 @@ +pragma solidity ^0.5.17; + +/// @dev The token controller contract must implement these functions + + +interface ITokenController { + /// @notice Called when `_owner` sends ether to the MiniMe Token contract + /// @param _owner The address that sent the ether to create tokens + /// @return True if the ether is accepted, false if it throws + function proxyPayment(address _owner) external payable returns(bool); + + /// @notice Notifies the controller about a token transfer allowing the + /// controller to react if desired + /// @param _from The origin of the transfer + /// @param _to The destination of the transfer + /// @param _amount The amount of the transfer + /// @return False if the controller does not authorize the transfer + function onTransfer(address _from, address _to, uint _amount) external returns(bool); + + /// @notice Notifies the controller about an approval allowing the + /// controller to react if desired + /// @param _owner The address that calls `approve()` + /// @param _spender The spender in the `approve()` call + /// @param _amount The amount in the `approve()` call + /// @return False if the controller does not authorize the approval + function onApprove(address _owner, address _spender, uint _amount) external returns(bool); +} diff --git a/tests-solidity/suites/staking/contracts/test/lib/MiniMeToken.sol b/tests-solidity/suites/staking/contracts/test/lib/MiniMeToken.sol new file mode 100644 index 0000000000..a82f8197a4 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/test/lib/MiniMeToken.sol @@ -0,0 +1,576 @@ +pragma solidity ^0.5.17; + +/* + Copyright 2016, Jordi Baylina + 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 . + */ + +/// @title MiniMeToken Contract +/// @author Jordi Baylina +/// @dev This token contract's goal is to make it easy for anyone to clone this +/// token using the token distribution at a given block, this will allow DAO's +/// and DApps to upgrade their features in a decentralized manner without +/// affecting the original token +/// @dev It is ERC20 compliant, but still needs to under go further testing. + +import "./ITokenController.sol"; + + +contract Controlled { + /// @notice The address of the controller is the only address that can call + /// a function with this modifier + modifier onlyController { + require(msg.sender == controller); + _; + } + + address payable public controller; + + constructor() public { controller = msg.sender; } + + /// @notice Changes the controller of the contract + /// @param _newController The new controller of the contract + function changeController(address payable _newController) onlyController public { + controller = _newController; + } +} + +contract ApproveAndCallFallBack { + function receiveApproval( + address from, + uint256 _amount, + address _token, + bytes calldata _data + ) external; +} + +/// @dev The actual token contract, the default controller is the msg.sender +/// that deploys the contract, so usually this token will be deployed by a +/// token controller contract, which Giveth will call a "Campaign" +contract MiniMeToken is Controlled { + + string public name; //The Token's name: e.g. DigixDAO Tokens + uint8 public decimals; //Number of decimals of the smallest unit + string public symbol; //An identifier: e.g. REP + string public version = "MMT_0.1"; //An arbitrary versioning scheme + + + /// @dev `Checkpoint` is the structure that attaches a block number to a + /// given value, the block number attached is the one that last changed the + /// value + struct Checkpoint { + + // `fromBlock` is the block number that the value was generated from + uint128 fromBlock; + + // `value` is the amount of tokens at a specific block number + uint128 value; + } + + // `parentToken` is the Token address that was cloned to produce this token; + // it will be 0x0 for a token that was not cloned + MiniMeToken public parentToken; + + // `parentSnapShotBlock` is the block number from the Parent Token that was + // used to determine the initial distribution of the Clone Token + uint public parentSnapShotBlock; + + // `creationBlock` is the block number that the Clone Token was created + uint public creationBlock; + + // `balances` is the map that tracks the balance of each address, in this + // contract when the balance changes the block number that the change + // occurred is also included in the map + mapping (address => Checkpoint[]) balances; + + // `allowed` tracks any extra transfer rights as in all ERC20 tokens + mapping (address => mapping (address => uint256)) allowed; + + // Tracks the history of the `totalSupply` of the token + Checkpoint[] totalSupplyHistory; + + // Flag that determines if the token is transferable or not. + bool public transfersEnabled; + + // The factory used to create new clone tokens + MiniMeTokenFactory public tokenFactory; + +//////////////// +// Constructor +//////////////// + + /// @notice Constructor to create a MiniMeToken + /// @param _tokenFactory The address of the MiniMeTokenFactory contract that + /// will create the Clone token contracts, the token factory needs to be + /// deployed first + /// @param _parentToken Address of the parent token, set to 0x0 if it is a + /// new token + /// @param _parentSnapShotBlock Block of the parent token that will + /// determine the initial distribution of the clone token, set to 0 if it + /// is a new token + /// @param _tokenName Name of the new token + /// @param _decimalUnits Number of decimals of the new token + /// @param _tokenSymbol Token Symbol for the new token + /// @param _transfersEnabled If true, tokens will be able to be transferred + constructor( + MiniMeTokenFactory _tokenFactory, + MiniMeToken _parentToken, + uint _parentSnapShotBlock, + string memory _tokenName, + uint8 _decimalUnits, + string memory _tokenSymbol, + bool _transfersEnabled + ) public + { + tokenFactory = _tokenFactory; + name = _tokenName; // Set the name + decimals = _decimalUnits; // Set the decimals + symbol = _tokenSymbol; // Set the symbol + parentToken = _parentToken; + parentSnapShotBlock = _parentSnapShotBlock; + transfersEnabled = _transfersEnabled; + creationBlock = block.number; + } + + +/////////////////// +// ERC20 Methods +/////////////////// + + /// @notice Send `_amount` tokens to `_to` from `msg.sender` + /// @param _to The address of the recipient + /// @param _amount The amount of tokens to be transferred + /// @return Whether the transfer was successful or not + function transfer(address _to, uint256 _amount) public returns (bool success) { + require(transfersEnabled); + return doTransfer(msg.sender, _to, _amount); + } + + /// @notice Send `_amount` tokens to `_to` from `_from` on the condition it + /// is approved by `_from` + /// @param _from The address holding the tokens being transferred + /// @param _to The address of the recipient + /// @param _amount The amount of tokens to be transferred + /// @return True if the transfer was successful + function transferFrom(address _from, address _to, uint256 _amount) public returns (bool success) { + + // The controller of this contract can move tokens around at will, + // this is important to recognize! Confirm that you trust the + // controller of this contract, which in most situations should be + // another open source smart contract or 0x0 + if (msg.sender != controller) { + require(transfersEnabled); + + // The standard ERC 20 transferFrom functionality + if (allowed[_from][msg.sender] < _amount) + return false; + allowed[_from][msg.sender] -= _amount; + } + return doTransfer(_from, _to, _amount); + } + + /// @dev This is the actual transfer function in the token contract, it can + /// only be called by other functions in this contract. + /// @param _from The address holding the tokens being transferred + /// @param _to The address of the recipient + /// @param _amount The amount of tokens to be transferred + /// @return True if the transfer was successful + function doTransfer(address _from, address _to, uint _amount) internal returns(bool) { + if (_amount == 0) { + return true; + } + require(parentSnapShotBlock < block.number); + // Do not allow transfer to 0x0 or the token contract itself + require((_to != address(0)) && (_to != address(this))); + // If the amount being transfered is more than the balance of the + // account the transfer returns false + uint256 previousBalanceFrom = balanceOfAt(_from, block.number); + if (previousBalanceFrom < _amount) { + return false; + } + // Alerts the token controller of the transfer + if (isContract(controller)) { + // Adding the ` == true` makes the linter shut up so... + require(ITokenController(controller).onTransfer(_from, _to, _amount) == true); + } + // First update the balance array with the new value for the address + // sending the tokens + updateValueAtNow(balances[_from], previousBalanceFrom - _amount); + // Then update the balance array with the new value for the address + // receiving the tokens + uint256 previousBalanceTo = balanceOfAt(_to, block.number); + require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow + updateValueAtNow(balances[_to], previousBalanceTo + _amount); + // An event to make the transfer easy to find on the blockchain + emit Transfer(_from, _to, _amount); + return true; + } + + /// @param _owner The address that's balance is being requested + /// @return The balance of `_owner` at the current block + function balanceOf(address _owner) public view returns (uint256 balance) { + return balanceOfAt(_owner, block.number); + } + + /// @notice `msg.sender` approves `_spender` to spend `_amount` tokens on + /// its behalf. This is a modified version of the ERC20 approve function + /// to be a little bit safer + /// @param _spender The address of the account able to transfer the tokens + /// @param _amount The amount of tokens to be approved for transfer + /// @return True if the approval was successful + function approve(address _spender, uint256 _amount) public returns (bool success) { + require(transfersEnabled); + + // To change the approve amount you first have to reduce the addresses` + // allowance to zero by calling `approve(_spender,0)` if it is not + // already 0 to mitigate the race condition described here: + // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + require((_amount == 0) || (allowed[msg.sender][_spender] == 0)); + + // Alerts the token controller of the approve function call + if (isContract(controller)) { + // Adding the ` == true` makes the linter shut up so... + require(ITokenController(controller).onApprove(msg.sender, _spender, _amount) == true); + } + + allowed[msg.sender][_spender] = _amount; + emit Approval(msg.sender, _spender, _amount); + return true; + } + + /// @dev This function makes it easy to read the `allowed[]` map + /// @param _owner The address of the account that owns the token + /// @param _spender The address of the account able to transfer the tokens + /// @return Amount of remaining tokens of _owner that _spender is allowed + /// to spend + function allowance(address _owner, address _spender) public view returns (uint256 remaining) { + return allowed[_owner][_spender]; + } + + /// @notice `msg.sender` approves `_spender` to send `_amount` tokens on + /// its behalf, and then a function is triggered in the contract that is + /// being approved, `_spender`. This allows users to use their tokens to + /// interact with contracts in one function call instead of two + /// @param _spender The address of the contract able to transfer the tokens + /// @param _amount The amount of tokens to be approved for transfer + /// @return True if the function call was successful + function approveAndCall(ApproveAndCallFallBack _spender, uint256 _amount, bytes calldata _extraData) external returns (bool success) { + require(approve(address(_spender), _amount)); + + _spender.receiveApproval( + msg.sender, + _amount, + address(this), + _extraData + ); + + return true; + } + + /// @dev This function makes it easy to get the total number of tokens + /// @return The total number of tokens + function totalSupply() public view returns (uint) { + return totalSupplyAt(block.number); + } + + +//////////////// +// Query balance and totalSupply in History +//////////////// + + /// @dev Queries the balance of `_owner` at a specific `_blockNumber` + /// @param _owner The address from which the balance will be retrieved + /// @param _blockNumber The block number when the balance is queried + /// @return The balance at `_blockNumber` + function balanceOfAt(address _owner, uint _blockNumber) public view returns (uint) { + + // These next few lines are used when the balance of the token is + // requested before a check point was ever created for this token, it + // requires that the `parentToken.balanceOfAt` be queried at the + // genesis block for that token as this contains initial balance of + // this token + if ((balances[_owner].length == 0) || (balances[_owner][0].fromBlock > _blockNumber)) { + if (address(parentToken) != address(0)) { + return parentToken.balanceOfAt(_owner, min(_blockNumber, parentSnapShotBlock)); + } else { + // Has no parent + return 0; + } + + // This will return the expected balance during normal situations + } else { + return getValueAt(balances[_owner], _blockNumber); + } + } + + /// @notice Total amount of tokens at a specific `_blockNumber`. + /// @param _blockNumber The block number when the totalSupply is queried + /// @return The total amount of tokens at `_blockNumber` + function totalSupplyAt(uint _blockNumber) public view returns(uint) { + + // These next few lines are used when the totalSupply of the token is + // requested before a check point was ever created for this token, it + // requires that the `parentToken.totalSupplyAt` be queried at the + // genesis block for this token as that contains totalSupply of this + // token at this block number. + if ((totalSupplyHistory.length == 0) || (totalSupplyHistory[0].fromBlock > _blockNumber)) { + if (address(parentToken) != address(0)) { + return parentToken.totalSupplyAt(min(_blockNumber, parentSnapShotBlock)); + } else { + return 0; + } + + // This will return the expected totalSupply during normal situations + } else { + return getValueAt(totalSupplyHistory, _blockNumber); + } + } + +//////////////// +// Clone Token Method +//////////////// + + /// @notice Creates a new clone token with the initial distribution being + /// this token at `_snapshotBlock` + /// @param _cloneTokenName Name of the clone token + /// @param _cloneDecimalUnits Number of decimals of the smallest unit + /// @param _cloneTokenSymbol Symbol of the clone token + /// @param _snapshotBlock Block when the distribution of the parent token is + /// copied to set the initial distribution of the new clone token; + /// if the block is zero than the actual block, the current block is used + /// @param _transfersEnabled True if transfers are allowed in the clone + /// @return The address of the new MiniMeToken Contract + function createCloneToken( + string calldata _cloneTokenName, + uint8 _cloneDecimalUnits, + string calldata _cloneTokenSymbol, + uint _snapshotBlock, + bool _transfersEnabled + ) external returns(MiniMeToken) + { + uint256 snapshot = _snapshotBlock == 0 ? block.number - 1 : _snapshotBlock; + + MiniMeToken cloneToken = tokenFactory.createCloneToken( + this, + snapshot, + _cloneTokenName, + _cloneDecimalUnits, + _cloneTokenSymbol, + _transfersEnabled + ); + + cloneToken.changeController(msg.sender); + + // An event to make the token easy to find on the blockchain + emit NewCloneToken(address(cloneToken), snapshot); + return cloneToken; + } + +//////////////// +// Generate and destroy tokens +//////////////// + + /// @notice Generates `_amount` tokens that are assigned to `_owner` + /// @param _owner The address that will be assigned the new tokens + /// @param _amount The quantity of tokens generated + /// @return True if the tokens are generated correctly + function generateTokens(address _owner, uint _amount) onlyController public returns (bool) { + uint curTotalSupply = totalSupply(); + require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow + uint previousBalanceTo = balanceOf(_owner); + require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow + updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount); + updateValueAtNow(balances[_owner], previousBalanceTo + _amount); + emit Transfer(address(0), _owner, _amount); + return true; + } + + + /// @notice Burns `_amount` tokens from `_owner` + /// @param _owner The address that will lose the tokens + /// @param _amount The quantity of tokens to burn + /// @return True if the tokens are burned correctly + function destroyTokens(address _owner, uint _amount) onlyController public returns (bool) { + uint curTotalSupply = totalSupply(); + require(curTotalSupply >= _amount); + uint previousBalanceFrom = balanceOf(_owner); + require(previousBalanceFrom >= _amount); + updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount); + updateValueAtNow(balances[_owner], previousBalanceFrom - _amount); + emit Transfer(_owner, address(0), _amount); + return true; + } + +//////////////// +// Enable tokens transfers +//////////////// + + + /// @notice Enables token holders to transfer their tokens freely if true + /// @param _transfersEnabled True if transfers are allowed in the clone + function enableTransfers(bool _transfersEnabled) onlyController public { + transfersEnabled = _transfersEnabled; + } + +//////////////// +// Internal helper functions to query and set a value in a snapshot array +//////////////// + + /// @dev `getValueAt` retrieves the number of tokens at a given block number + /// @param checkpoints The history of values being queried + /// @param _block The block number to retrieve the value at + /// @return The number of tokens being queried + function getValueAt(Checkpoint[] storage checkpoints, uint _block) view internal returns (uint) { + if (checkpoints.length == 0) + return 0; + + // Shortcut for the actual value + if (_block >= checkpoints[checkpoints.length-1].fromBlock) + return checkpoints[checkpoints.length-1].value; + if (_block < checkpoints[0].fromBlock) + return 0; + + // Binary search of the value in the array + uint min = 0; + uint max = checkpoints.length-1; + while (max > min) { + uint mid = (max + min + 1) / 2; + if (checkpoints[mid].fromBlock<=_block) { + min = mid; + } else { + max = mid-1; + } + } + return checkpoints[min].value; + } + + /// @dev `updateValueAtNow` used to update the `balances` map and the + /// `totalSupplyHistory` + /// @param checkpoints The history of data being updated + /// @param _value The new number of tokens + function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value) internal { + if ((checkpoints.length == 0) || (checkpoints[checkpoints.length - 1].fromBlock < block.number)) { + Checkpoint storage newCheckPoint = checkpoints[checkpoints.length++]; + newCheckPoint.fromBlock = uint128(block.number); + newCheckPoint.value = uint128(_value); + } else { + Checkpoint storage oldCheckPoint = checkpoints[checkpoints.length - 1]; + oldCheckPoint.value = uint128(_value); + } + } + + /// @dev Internal function to determine if an address is a contract + /// @param _addr The address being queried + /// @return True if `_addr` is a contract + function isContract(address _addr) view internal returns(bool) { + uint size; + if (_addr == address(0)) + return false; + + assembly { + size := extcodesize(_addr) + } + + return size>0; + } + + /// @dev Helper function to return a min betwen the two uints + function min(uint a, uint b) pure internal returns (uint) { + return a < b ? a : b; + } + + /// @notice The fallback function: If the contract's controller has not been + /// set to 0, then the `proxyPayment` method is called which relays the + /// ether and creates tokens as described in the token controller contract + function () external payable { + require(isContract(controller)); + // Adding the ` == true` makes the linter shut up so... + require(ITokenController(controller).proxyPayment.value(msg.value)(msg.sender) == true); + } + +////////// +// Safety Methods +////////// + + /// @notice This method can be used by the controller to extract mistakenly + /// sent tokens to this contract. + /// @param _token The address of the token contract that you want to recover + /// set to 0 in case you want to extract ether. + function claimTokens(address payable _token) onlyController public { + if (_token == address(0)) { + controller.transfer(address(this).balance); + return; + } + + MiniMeToken token = MiniMeToken(_token); + uint balance = token.balanceOf(address(this)); + token.transfer(controller, balance); + emit ClaimedTokens(_token, controller, balance); + } + +//////////////// +// Events +//////////////// + event ClaimedTokens(address indexed _token, address indexed _controller, uint _amount); + event Transfer(address indexed _from, address indexed _to, uint256 _amount); + event NewCloneToken(address indexed _cloneToken, uint _snapshotBlock); + event Approval( + address indexed _owner, + address indexed _spender, + uint256 _amount + ); + +} + + +//////////////// +// MiniMeTokenFactory +//////////////// + +/// @dev This contract is used to generate clone contracts from a contract. +/// In solidity this is the way to create a contract from a contract of the +/// same class +contract MiniMeTokenFactory { + + /// @notice Update the DApp by creating a new token with new functionalities + /// the msg.sender becomes the controller of this clone token + /// @param _parentToken Address of the token being cloned + /// @param _snapshotBlock Block of the parent token that will + /// determine the initial distribution of the clone token + /// @param _tokenName Name of the new token + /// @param _decimalUnits Number of decimals of the new token + /// @param _tokenSymbol Token Symbol for the new token + /// @param _transfersEnabled If true, tokens will be able to be transferred + /// @return The address of the new token contract + function createCloneToken( + MiniMeToken _parentToken, + uint _snapshotBlock, + string calldata _tokenName, + uint8 _decimalUnits, + string calldata _tokenSymbol, + bool _transfersEnabled + ) external returns (MiniMeToken) + { + MiniMeToken newToken = new MiniMeToken( + this, + _parentToken, + _snapshotBlock, + _tokenName, + _decimalUnits, + _tokenSymbol, + _transfersEnabled + ); + + newToken.changeController(msg.sender); + return newToken; + } +} diff --git a/tests-solidity/suites/staking/contracts/test/mocks/BadTokenMock.sol b/tests-solidity/suites/staking/contracts/test/mocks/BadTokenMock.sol new file mode 100644 index 0000000000..eef3ebab62 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/test/mocks/BadTokenMock.sol @@ -0,0 +1,218 @@ +// Copied from https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a9f910d34f0ab33a1ae5e714f69f9596a02b4d91/contracts/token/ERC20/StandardToken.sol + +// transfer function always returns false! + +pragma solidity 0.5.17; + +//import "./ERC20.sol"; +import "../../lib/os/ERC20.sol"; +import "../../lib/os/SafeMath.sol"; + + +/** + * @title Standard ERC20 token + * + * @dev Implementation of the basic standard token. + * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md + * Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol + */ +contract BadTokenMock is ERC20 { + using SafeMath for uint256; + + mapping (address => uint256) private balances; + + mapping (address => mapping (address => uint256)) private allowed; + + uint256 private totalSupply_; + + constructor(address initialAccount, uint256 initialBalance) public { + balances[initialAccount] = initialBalance; + totalSupply_ = initialBalance; + } + + function mint (address account, uint256 amount) public { + balances[account] = balances[account].add(amount); + totalSupply_ = totalSupply_.add(amount); + } + + /** + * @dev Total number of tokens in existence + */ + function totalSupply() public view returns (uint256) { + return totalSupply_; + } + + /** + * @dev Gets the balance of the specified address. + * @param _owner The address to query the the balance of. + * @return An uint256 representing the amount owned by the passed address. + */ + function balanceOf(address _owner) public view returns (uint256) { + return balances[_owner]; + } + + /** + * @dev Function to check the amount of tokens that an owner allowed to a spender. + * @param _owner address The address which owns the funds. + * @param _spender address The address which will spend the funds. + * @return A uint256 specifying the amount of tokens still available for the spender. + */ + function allowance( + address _owner, + address _spender + ) + public + view + returns (uint256) + { + return allowed[_owner][_spender]; + } + + /** + * @dev Transfer token for a specified address + * @param _to The address to transfer to. + * @param _value The amount to be transferred. + */ + function transfer(address _to, uint256 _value) public returns (bool) { + require(_value <= balances[msg.sender]); + require(_to != address(0)); + + balances[msg.sender] = balances[msg.sender].sub(_value); + balances[_to] = balances[_to].add(_value); + emit Transfer(msg.sender, _to, _value); + return false; // <--- Bad Token!! + } + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. + * Beware that changing an allowance with this method brings the risk that someone may use both the old + * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this + * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * @param _spender The address which will spend the funds. + * @param _value The amount of tokens to be spent. + */ + function approve(address _spender, uint256 _value) public returns (bool) { + allowed[msg.sender][_spender] = _value; + emit Approval(msg.sender, _spender, _value); + return true; + } + + /** + * @dev Transfer tokens from one address to another + * @param _from address The address which you want to send tokens from + * @param _to address The address which you want to transfer to + * @param _value uint256 the amount of tokens to be transferred + */ + function transferFrom( + address _from, + address _to, + uint256 _value + ) + public + returns (bool) + { + require(_value <= balances[_from]); + require(_value <= allowed[_from][msg.sender]); + require(_to != address(0)); + + balances[_from] = balances[_from].sub(_value); + balances[_to] = balances[_to].add(_value); + allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); + emit Transfer(_from, _to, _value); + return true; + } + + /** + * @dev Increase the amount of tokens that an owner allowed to a spender. + * approve should be called when allowed[_spender] == 0. To increment + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + * @param _spender The address which will spend the funds. + * @param _addedValue The amount of tokens to increase the allowance by. + */ + function increaseApproval( + address _spender, + uint256 _addedValue + ) + public + returns (bool) + { + allowed[msg.sender][_spender] = ( + allowed[msg.sender][_spender].add(_addedValue)); + emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); + return true; + } + + /** + * @dev Decrease the amount of tokens that an owner allowed to a spender. + * approve should be called when allowed[_spender] == 0. To decrement + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + * @param _spender The address which will spend the funds. + * @param _subtractedValue The amount of tokens to decrease the allowance by. + */ + function decreaseApproval( + address _spender, + uint256 _subtractedValue + ) + public + returns (bool) + { + uint256 oldValue = allowed[msg.sender][_spender]; + if (_subtractedValue >= oldValue) { + allowed[msg.sender][_spender] = 0; + } else { + allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue); + } + emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); + return true; + } + + /** + * @dev Internal function that mints an amount of the token and assigns it to + * an account. This encapsulates the modification of balances such that the + * proper events are emitted. + * @param _account The account that will receive the created tokens. + * @param _amount The amount that will be created. + */ + function _mint(address _account, uint256 _amount) internal { + require(_account != address(0)); + totalSupply_ = totalSupply_.add(_amount); + balances[_account] = balances[_account].add(_amount); + emit Transfer(address(0), _account, _amount); + } + + /** + * @dev Internal function that burns an amount of the token of a given + * account. + * @param _account The account whose tokens will be burnt. + * @param _amount The amount that will be burnt. + */ + function _burn(address _account, uint256 _amount) internal { + require(_account != address(0)); + require(_amount <= balances[_account]); + + totalSupply_ = totalSupply_.sub(_amount); + balances[_account] = balances[_account].sub(_amount); + emit Transfer(_account, address(0), _amount); + } + + /** + * @dev Internal function that burns an amount of the token of a given + * account, deducting from the sender's allowance for said account. Uses the + * internal _burn function. + * @param _account The account whose tokens will be burnt. + * @param _amount The amount that will be burnt. + */ + function _burnFrom(address _account, uint256 _amount) internal { + require(_amount <= allowed[_account][msg.sender]); + + // Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted, + // this function needs to emit an event with the updated approval. + allowed[_account][msg.sender] = allowed[_account][msg.sender].sub(_amount); + _burn(_account, _amount); + } +} diff --git a/tests-solidity/suites/staking/contracts/test/mocks/CheckpointingMock.sol b/tests-solidity/suites/staking/contracts/test/mocks/CheckpointingMock.sol new file mode 100644 index 0000000000..3711343420 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/test/mocks/CheckpointingMock.sol @@ -0,0 +1,30 @@ +pragma solidity 0.5.17; + +import "../../lib/Checkpointing.sol"; + + +contract CheckpointingMock { + using Checkpointing for Checkpointing.History; + + Checkpointing.History history; + + function add(uint64 value, uint256 time) public { + history.add(value, time); + } + + function getLast() public view returns (uint256) { + return history.getLast(); + } + + function get(uint64 time) public view returns (uint256) { + return history.get(time); + } + + function getHistorySize() public view returns (uint256) { + return history.history.length; + } + + function lastUpdate() public view returns (uint256) { + return history.lastUpdate(); + } +} diff --git a/tests-solidity/suites/staking/contracts/test/mocks/ERC20.sol b/tests-solidity/suites/staking/contracts/test/mocks/ERC20.sol new file mode 100644 index 0000000000..09b4f64fa7 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/test/mocks/ERC20.sol @@ -0,0 +1,26 @@ +pragma solidity ^0.5.17; + + +/** + * @title ERC20 interface + * @dev see https://github.com/ethereum/EIPs/issues/20 + */ +contract ERC20 { + function totalSupply() public view returns (uint256); + + function balanceOf(address _who) public view returns (uint256); + + function allowance(address _owner, address _spender) + public view returns (uint256); + + function transfer(address _to, uint256 _value) public returns (bool); + + function approve(address _spender, uint256 _value) + public returns (bool); + + function transferFrom(address _from, address _to, uint256 _value) + public returns (bool); + + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); +} diff --git a/tests-solidity/suites/staking/contracts/test/mocks/LockManagerMock.sol b/tests-solidity/suites/staking/contracts/test/mocks/LockManagerMock.sol new file mode 100644 index 0000000000..51de208e2c --- /dev/null +++ b/tests-solidity/suites/staking/contracts/test/mocks/LockManagerMock.sol @@ -0,0 +1,41 @@ +pragma solidity 0.5.17; + +import "../../locking/ILockManager.sol"; +import "../../Staking.sol"; + + +contract LockManagerMock is ILockManager { + bool result; + + function slash(Staking _staking, address _from, address _to, uint256 _amount) external { + _staking.slash(_from, _to, _amount); + } + + function slashAndUnstake(Staking _staking, address _from, address _to, uint256 _amount) external { + _staking.slashAndUnstake(_from, _to, _amount); + } + + function unlock(Staking _staking, address _account, uint256 _amount) external { + _staking.unlock(_account, address(this), _amount); + } + + function unlockAndRemoveManager(Staking _staking, address _account) external { + _staking.unlockAndRemoveManager(_account, address(this)); + } + + function setLockManager(Staking _staking, address _account, ILockManager _newManager) external { + _staking.setLockManager(_account, address(_newManager)); + } + + function canUnlock(address, uint256) external view returns (bool) { + return result; + } + + function setResult(bool _result) public { + result = _result; + } + + function unlockAndRemoveManager(Staking _staking, address _account, address _manager) public { + _staking.unlockAndRemoveManager(_account, _manager); + } +} diff --git a/tests-solidity/suites/staking/contracts/test/mocks/NoApproveTokenMock.sol b/tests-solidity/suites/staking/contracts/test/mocks/NoApproveTokenMock.sol new file mode 100644 index 0000000000..b3d6d0ee0c --- /dev/null +++ b/tests-solidity/suites/staking/contracts/test/mocks/NoApproveTokenMock.sol @@ -0,0 +1,215 @@ +// Copied from https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a9f910d34f0ab33a1ae5e714f69f9596a02b4d91/contracts/token/ERC20/StandardToken.sol + +pragma solidity 0.5.17; + +import "../../lib/os/ERC20.sol"; +import "../../lib/os/SafeMath.sol"; + + +/** + * @title Standard ERC20 token + * + * @dev Implementation of the basic standard token. + * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md + * Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol + */ +contract NoApproveTokenMock is ERC20 { + using SafeMath for uint256; + + mapping (address => uint256) private balances; + + mapping (address => mapping (address => uint256)) private allowed; + + uint256 private totalSupply_; + + constructor(address initialAccount, uint256 initialBalance) public { + balances[initialAccount] = initialBalance; + totalSupply_ = initialBalance; + } + + function mint (address account, uint256 amount) public { + balances[account] = balances[account].add(amount); + totalSupply_ = totalSupply_.add(amount); + } + + /** + * @dev Total number of tokens in existence + */ + function totalSupply() public view returns (uint256) { + return totalSupply_; + } + + /** + * @dev Gets the balance of the specified address. + * @param _owner The address to query the the balance of. + * @return An uint256 representing the amount owned by the passed address. + */ + function balanceOf(address _owner) public view returns (uint256) { + return balances[_owner]; + } + + /** + * @dev Function to check the amount of tokens that an owner allowed to a spender. + * @param _owner address The address which owns the funds. + * @param _spender address The address which will spend the funds. + * @return A uint256 specifying the amount of tokens still available for the spender. + */ + function allowance( + address _owner, + address _spender + ) + public + view + returns (uint256) + { + return allowed[_owner][_spender]; + } + + /** + * @dev Transfer token for a specified address + * @param _to The address to transfer to. + * @param _value The amount to be transferred. + */ + function transfer(address _to, uint256 _value) public returns (bool) { + require(_value <= balances[msg.sender]); + require(_to != address(0)); + + balances[msg.sender] = balances[msg.sender].sub(_value); + balances[_to] = balances[_to].add(_value); + emit Transfer(msg.sender, _to, _value); + return true; + } + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. + * Beware that changing an allowance with this method brings the risk that someone may use both the old + * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this + * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * @param _spender The address which will spend the funds. + * @param _value The amount of tokens to be spent. + */ + function approve(address _spender, uint256 _value) public returns (bool) { + allowed[msg.sender][_spender] = _value; + emit Approval(msg.sender, _spender, _value); + return true; + } + + /** + * @dev Transfer tokens from one address to another + * @param _from address The address which you want to send tokens from + * @param _to address The address which you want to transfer to + * @param _value uint256 the amount of tokens to be transferred + */ + function transferFrom( + address _from, + address _to, + uint256 _value + ) + public + returns (bool) + { + require(_value <= balances[_from]); + //require(_value <= allowed[_from][msg.sender]); + require(_to != address(0)); + + balances[_from] = balances[_from].sub(_value); + balances[_to] = balances[_to].add(_value); + //allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); + emit Transfer(_from, _to, _value); + return true; + } + + /** + * @dev Increase the amount of tokens that an owner allowed to a spender. + * approve should be called when allowed[_spender] == 0. To increment + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + * @param _spender The address which will spend the funds. + * @param _addedValue The amount of tokens to increase the allowance by. + */ + function increaseApproval( + address _spender, + uint256 _addedValue + ) + public + returns (bool) + { + allowed[msg.sender][_spender] = ( + allowed[msg.sender][_spender].add(_addedValue)); + emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); + return true; + } + + /** + * @dev Decrease the amount of tokens that an owner allowed to a spender. + * approve should be called when allowed[_spender] == 0. To decrement + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + * @param _spender The address which will spend the funds. + * @param _subtractedValue The amount of tokens to decrease the allowance by. + */ + function decreaseApproval( + address _spender, + uint256 _subtractedValue + ) + public + returns (bool) + { + uint256 oldValue = allowed[msg.sender][_spender]; + if (_subtractedValue >= oldValue) { + allowed[msg.sender][_spender] = 0; + } else { + allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue); + } + emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); + return true; + } + + /** + * @dev Internal function that mints an amount of the token and assigns it to + * an account. This encapsulates the modification of balances such that the + * proper events are emitted. + * @param _account The account that will receive the created tokens. + * @param _amount The amount that will be created. + */ + function _mint(address _account, uint256 _amount) internal { + require(_account != address(0)); + totalSupply_ = totalSupply_.add(_amount); + balances[_account] = balances[_account].add(_amount); + emit Transfer(address(0), _account, _amount); + } + + /** + * @dev Internal function that burns an amount of the token of a given + * account. + * @param _account The account whose tokens will be burnt. + * @param _amount The amount that will be burnt. + */ + function _burn(address _account, uint256 _amount) internal { + require(_account != address(0)); + require(_amount <= balances[_account]); + + totalSupply_ = totalSupply_.sub(_amount); + balances[_account] = balances[_account].sub(_amount); + emit Transfer(_account, address(0), _amount); + } + + /** + * @dev Internal function that burns an amount of the token of a given + * account, deducting from the sender's allowance for said account. Uses the + * internal _burn function. + * @param _account The account whose tokens will be burnt. + * @param _amount The amount that will be burnt. + */ + function _burnFrom(address _account, uint256 _amount) internal { + require(_amount <= allowed[_account][msg.sender]); + + // Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted, + // this function needs to emit an event with the updated approval. + allowed[_account][msg.sender] = allowed[_account][msg.sender].sub(_amount); + _burn(_account, _amount); + } +} diff --git a/tests-solidity/suites/staking/contracts/test/mocks/StakingMock.sol b/tests-solidity/suites/staking/contracts/test/mocks/StakingMock.sol new file mode 100644 index 0000000000..55184feff9 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/test/mocks/StakingMock.sol @@ -0,0 +1,54 @@ +pragma solidity 0.5.17; + +import "../../Staking.sol"; + +import "../../lib/os/SafeMath.sol"; +import "./TimeHelpersMock.sol"; + + +contract StakingMock is Staking, TimeHelpersMock { + using SafeMath for uint256; + + event LogGas(uint256 gas); + + string private constant ERROR_TOKEN_NOT_CONTRACT = "STAKING_TOKEN_NOT_CONTRACT"; + + uint64 private constant MAX_UINT64 = uint64(-1); + + modifier measureGas { + uint256 initialGas = gasleft(); + _; + emit LogGas(initialGas - gasleft()); + } + + constructor(ERC20 _stakingToken) public { + require(isContract(address(_stakingToken)), ERROR_TOKEN_NOT_CONTRACT); + initialized(); + stakingToken = _stakingToken; + } + + function unlockedBalanceOfGas() external returns (uint256) { + uint256 initialGas = gasleft(); + _unlockedBalanceOf(msg.sender); + uint256 gasConsumed = initialGas - gasleft(); + emit LogGas(gasConsumed); + return gasConsumed; + } + + function transferGas(address _to, address, uint256 _amount) external measureGas { + // have enough unlocked funds + require(_amount <= _unlockedBalanceOf(msg.sender)); + + _transfer(msg.sender, _to, _amount); + } + + function setBlockNumber(uint64 _mockedBlockNumber) public { + mockedBlockNumber = _mockedBlockNumber; + } + + // Override petrify functions to allow mocking the initialization process + function petrify() internal onlyInit { + // solium-disable-previous-line no-empty-blocks + // initializedAt(PETRIFIED_BLOCK); + } +} diff --git a/tests-solidity/suites/staking/contracts/test/mocks/StandardTokenMock.sol b/tests-solidity/suites/staking/contracts/test/mocks/StandardTokenMock.sol new file mode 100644 index 0000000000..e79a2477db --- /dev/null +++ b/tests-solidity/suites/staking/contracts/test/mocks/StandardTokenMock.sol @@ -0,0 +1,215 @@ +// Copied from https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a9f910d34f0ab33a1ae5e714f69f9596a02b4d91/contracts/token/ERC20/StandardToken.sol + +pragma solidity 0.5.17; + +import "../../lib/os/ERC20.sol"; +import "../../lib/os/SafeMath.sol"; + + +/** + * @title Standard ERC20 token + * + * @dev Implementation of the basic standard token. + * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md + * Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol + */ +contract StandardTokenMock is ERC20 { + using SafeMath for uint256; + + mapping (address => uint256) private balances; + + mapping (address => mapping (address => uint256)) private allowed; + + uint256 private totalSupply_; + + constructor(address initialAccount, uint256 initialBalance) public { + balances[initialAccount] = initialBalance; + totalSupply_ = initialBalance; + } + + function mint (address account, uint256 amount) public { + balances[account] = balances[account].add(amount); + totalSupply_ = totalSupply_.add(amount); + } + + /** + * @dev Total number of tokens in existence + */ + function totalSupply() public view returns (uint256) { + return totalSupply_; + } + + /** + * @dev Gets the balance of the specified address. + * @param _owner The address to query the the balance of. + * @return An uint256 representing the amount owned by the passed address. + */ + function balanceOf(address _owner) public view returns (uint256) { + return balances[_owner]; + } + + /** + * @dev Function to check the amount of tokens that an owner allowed to a spender. + * @param _owner address The address which owns the funds. + * @param _spender address The address which will spend the funds. + * @return A uint256 specifying the amount of tokens still available for the spender. + */ + function allowance( + address _owner, + address _spender + ) + public + view + returns (uint256) + { + return allowed[_owner][_spender]; + } + + /** + * @dev Transfer token for a specified address + * @param _to The address to transfer to. + * @param _value The amount to be transferred. + */ + function transfer(address _to, uint256 _value) public returns (bool) { + require(_value <= balances[msg.sender]); + require(_to != address(0)); + + balances[msg.sender] = balances[msg.sender].sub(_value); + balances[_to] = balances[_to].add(_value); + emit Transfer(msg.sender, _to, _value); + return true; + } + + /** + * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. + * Beware that changing an allowance with this method brings the risk that someone may use both the old + * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this + * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * @param _spender The address which will spend the funds. + * @param _value The amount of tokens to be spent. + */ + function approve(address _spender, uint256 _value) public returns (bool) { + allowed[msg.sender][_spender] = _value; + emit Approval(msg.sender, _spender, _value); + return true; + } + + /** + * @dev Transfer tokens from one address to another + * @param _from address The address which you want to send tokens from + * @param _to address The address which you want to transfer to + * @param _value uint256 the amount of tokens to be transferred + */ + function transferFrom( + address _from, + address _to, + uint256 _value + ) + public + returns (bool) + { + require(_value <= balances[_from]); + require(_value <= allowed[_from][msg.sender]); + require(_to != address(0)); + + balances[_from] = balances[_from].sub(_value); + balances[_to] = balances[_to].add(_value); + allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); + emit Transfer(_from, _to, _value); + return true; + } + + /** + * @dev Increase the amount of tokens that an owner allowed to a spender. + * approve should be called when allowed[_spender] == 0. To increment + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + * @param _spender The address which will spend the funds. + * @param _addedValue The amount of tokens to increase the allowance by. + */ + function increaseApproval( + address _spender, + uint256 _addedValue + ) + public + returns (bool) + { + allowed[msg.sender][_spender] = ( + allowed[msg.sender][_spender].add(_addedValue)); + emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); + return true; + } + + /** + * @dev Decrease the amount of tokens that an owner allowed to a spender. + * approve should be called when allowed[_spender] == 0. To decrement + * allowed value is better to use this function to avoid 2 calls (and wait until + * the first transaction is mined) + * From MonolithDAO Token.sol + * @param _spender The address which will spend the funds. + * @param _subtractedValue The amount of tokens to decrease the allowance by. + */ + function decreaseApproval( + address _spender, + uint256 _subtractedValue + ) + public + returns (bool) + { + uint256 oldValue = allowed[msg.sender][_spender]; + if (_subtractedValue >= oldValue) { + allowed[msg.sender][_spender] = 0; + } else { + allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue); + } + emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); + return true; + } + + /** + * @dev Internal function that mints an amount of the token and assigns it to + * an account. This encapsulates the modification of balances such that the + * proper events are emitted. + * @param _account The account that will receive the created tokens. + * @param _amount The amount that will be created. + */ + function _mint(address _account, uint256 _amount) internal { + require(_account != address(0)); + totalSupply_ = totalSupply_.add(_amount); + balances[_account] = balances[_account].add(_amount); + emit Transfer(address(0), _account, _amount); + } + + /** + * @dev Internal function that burns an amount of the token of a given + * account. + * @param _account The account whose tokens will be burnt. + * @param _amount The amount that will be burnt. + */ + function _burn(address _account, uint256 _amount) internal { + require(_account != address(0)); + require(_amount <= balances[_account]); + + totalSupply_ = totalSupply_.sub(_amount); + balances[_account] = balances[_account].sub(_amount); + emit Transfer(_account, address(0), _amount); + } + + /** + * @dev Internal function that burns an amount of the token of a given + * account, deducting from the sender's allowance for said account. Uses the + * internal _burn function. + * @param _account The account whose tokens will be burnt. + * @param _amount The amount that will be burnt. + */ + function _burnFrom(address _account, uint256 _amount) internal { + require(_amount <= allowed[_account][msg.sender]); + + // Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted, + // this function needs to emit an event with the updated approval. + allowed[_account][msg.sender] = allowed[_account][msg.sender].sub(_amount); + _burn(_account, _amount); + } +} diff --git a/tests-solidity/suites/staking/contracts/test/mocks/TimeHelpersMock.sol b/tests-solidity/suites/staking/contracts/test/mocks/TimeHelpersMock.sol new file mode 100644 index 0000000000..4d9511a087 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/test/mocks/TimeHelpersMock.sol @@ -0,0 +1,75 @@ +pragma solidity ^0.5.17; + +import "../../lib/os/TimeHelpers.sol"; +import "../..//lib/os/SafeMath.sol"; +import "../..//lib/os/SafeMath64.sol"; + + +contract TimeHelpersMock is TimeHelpers { + using SafeMath for uint256; + using SafeMath64 for uint64; + + uint256 public mockedTimestamp; + uint256 public mockedBlockNumber; + + /** + * @dev Sets a mocked timestamp value, used only for testing purposes + */ + function mockSetTimestamp(uint256 _timestamp) external { + mockedTimestamp = _timestamp; + } + + /** + * @dev Increases the mocked timestamp value, used only for testing purposes + */ + function mockIncreaseTime(uint256 _seconds) external { + if (mockedTimestamp != 0) mockedTimestamp = mockedTimestamp.add(_seconds); + else mockedTimestamp = block.timestamp.add(_seconds); + } + + /** + * @dev Decreases the mocked timestamp value, used only for testing purposes + */ + function mockDecreaseTime(uint256 _seconds) external { + if (mockedTimestamp != 0) mockedTimestamp = mockedTimestamp.sub(_seconds); + else mockedTimestamp = block.timestamp.sub(_seconds); + } + + /** + * @dev Advances the mocked block number value, used only for testing purposes + */ + function mockAdvanceBlocks(uint256 _number) external { + if (mockedBlockNumber != 0) mockedBlockNumber = mockedBlockNumber.add(_number); + else mockedBlockNumber = block.number.add(_number); + } + + /** + * @dev Returns the mocked timestamp value + */ + function getTimestampPublic() external view returns (uint64) { + return getTimestamp64(); + } + + /** + * @dev Returns the mocked block number value + */ + function getBlockNumberPublic() external view returns (uint256) { + return getBlockNumber(); + } + + /** + * @dev Returns the mocked timestamp if it was set, or current `block.timestamp` + */ + function getTimestamp() internal view returns (uint256) { + if (mockedTimestamp != 0) return mockedTimestamp; + return super.getTimestamp(); + } + + /** + * @dev Returns the mocked block number if it was set, or current `block.number` + */ + function getBlockNumber() internal view returns (uint256) { + if (mockedBlockNumber != 0) return mockedBlockNumber; + return super.getBlockNumber(); + } +} diff --git a/tests-solidity/suites/staking/contracts/test/mocks/TimeLockManagerMock.sol b/tests-solidity/suites/staking/contracts/test/mocks/TimeLockManagerMock.sol new file mode 100644 index 0000000000..6200e0b246 --- /dev/null +++ b/tests-solidity/suites/staking/contracts/test/mocks/TimeLockManagerMock.sol @@ -0,0 +1,36 @@ +pragma solidity 0.5.17; + +import "../../locking/TimeLockManager.sol"; +import "../../Staking.sol"; + + +contract TimeLockManagerMock is TimeLockManager { + uint64 public constant MAX_UINT64 = uint64(-1); + + uint256 _mockTime = now; + uint256 _mockBlockNumber = block.number; + + function getTimestampExt() external view returns (uint256) { + return getTimestamp(); + } + + function getBlockNumberExt() external view returns (uint256) { + return getBlockNumber(); + } + + function setTimestamp(uint256 i) public { + _mockTime = i; + } + + function setBlockNumber(uint256 i) public { + _mockBlockNumber = i; + } + + function getTimestamp() internal view returns (uint256) { + return _mockTime; + } + + function getBlockNumber() internal view returns (uint256) { + return _mockBlockNumber; + } +} diff --git a/tests-solidity/suites/staking/package.json b/tests-solidity/suites/staking/package.json new file mode 100644 index 0000000000..b0f5cc74e1 --- /dev/null +++ b/tests-solidity/suites/staking/package.json @@ -0,0 +1,18 @@ +{ + "name": "staking", + "version": "1.0.0", + "author": "Aragon Association ", + "license": "GPL-3.0-or-later", + "scripts": { + "test-ganache": "yarn truffle test", + "test-ethermint": "yarn truffle test --network ethermint" + }, + "devDependencies": { + "@aragon/contract-helpers-test": "^0.0.3", + "chai": "^4.2.0", + "ganache-cli": "^6.1.0", + "truffle": "^5.1.42", + "web3-eth-abi": "^1.2.11", + "web3-utils": "^1.2.11" + } +} diff --git a/tests-solidity/suites/staking/test/approve_and_call.js b/tests-solidity/suites/staking/test/approve_and_call.js new file mode 100644 index 0000000000..af3c17d7a1 --- /dev/null +++ b/tests-solidity/suites/staking/test/approve_and_call.js @@ -0,0 +1,52 @@ +const { assertRevert } = require('@aragon/contract-helpers-test/assertThrow') +const { bn, assertBn } = require('@aragon/contract-helpers-test/numbers') + +const { DEFAULT_STAKE_AMOUNT, EMPTY_DATA, ZERO_ADDRESS } = require('./helpers/constants') +const { STAKING_ERRORS } = require('./helpers/errors') + +const StakingMock = artifacts.require('StakingMock') +const MiniMeToken = artifacts.require('MiniMeToken') + +contract('Staking app, Approve and call fallback', ([owner, user]) => { + let staking, token, stakingAddress, tokenAddress + + beforeEach(async () => { + const initialAmount = DEFAULT_STAKE_AMOUNT.mul(bn(1000)) + const tokenContract = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'Test Token', 18, 'TT', true) + token = tokenContract + tokenAddress = tokenContract.address + await token.generateTokens(user, DEFAULT_STAKE_AMOUNT) + const stakingContract = await StakingMock.new(tokenAddress) + staking = stakingContract + stakingAddress = stakingContract.address + }) + + it('stakes through approveAndCall', async () => { + const initialUserBalance = await token.balanceOf(user) + const initialStakingBalance = await token.balanceOf(stakingAddress) + + await token.approveAndCall(stakingAddress, DEFAULT_STAKE_AMOUNT, EMPTY_DATA, { from: user }) + + const finalUserBalance = await token.balanceOf(user) + const finalStakingBalance = await token.balanceOf(stakingAddress) + assertBn(finalUserBalance, initialUserBalance.sub(DEFAULT_STAKE_AMOUNT), "user balance should match") + assertBn(finalStakingBalance, initialStakingBalance.add(DEFAULT_STAKE_AMOUNT), "Staking app balance should match") + assertBn(await staking.totalStakedFor(user), DEFAULT_STAKE_AMOUNT, "staked value should match") + // total stake + assertBn(await staking.totalStaked(), DEFAULT_STAKE_AMOUNT, "Total stake should match") + }) + + it('fails staking 0 amount through approveAndCall', async () => { + await assertRevert(token.approveAndCall(stakingAddress, 0, EMPTY_DATA, { from: user })/*, STAKING_ERRORS.ERROR_AMOUNT_ZERO*/) + }) + + it('fails calling approveAndCall on a different token', async () => { + const token2 = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'Test Token 2', 18, 'TT2', true) + await token2.generateTokens(user, DEFAULT_STAKE_AMOUNT) + await assertRevert(token2.approveAndCall(stakingAddress, 0, EMPTY_DATA, { from: user })/*, STAKING_ERRORS.ERROR_WRONG_TOKEN*/) + }) + + it('fails calling receiveApproval from a different account than the token', async () => { + await assertRevert(staking.receiveApproval(user, DEFAULT_STAKE_AMOUNT, tokenAddress, EMPTY_DATA)/*, STAKING_ERRORS.ERROR_TOKEN_NOT_SENDER*/) + }) +}) diff --git a/tests-solidity/suites/staking/test/gas.js b/tests-solidity/suites/staking/test/gas.js new file mode 100644 index 0000000000..b4b58f94d1 --- /dev/null +++ b/tests-solidity/suites/staking/test/gas.js @@ -0,0 +1,83 @@ +const { MAX_UINT64 } = require('@aragon/contract-helpers-test/numbers') + +const getEvent = (receipt, event, arg) => { return receipt.logs.filter(l => l.event == event)[0].args[arg] } + +const { deploy } = require('./helpers/deploy')(artifacts) +const { DEFAULT_STAKE_AMOUNT, DEFAULT_LOCK_AMOUNT, EMPTY_DATA, ZERO_ADDRESS, ACTIVATED_LOCK } = require('./helpers/constants') + +contract.skip('Staking app, gas measures', accounts => { + let staking, token, lockManager, stakingAddress, tokenAddress, lockManagerAddress + let owner, user1, user2 + + const approveAndStake = async (amount = DEFAULT_STAKE_AMOUNT, from = owner) => { + await token.approve(stakingAddress, amount, { from }) + await staking.stake(amount, EMPTY_DATA, { from }) + } + + const approveStakeAndLock = async ( + manager, + lockAmount = DEFAULT_LOCK_AMOUNT, + stakeAmount = DEFAULT_STAKE_AMOUNT, + from = owner + ) => { + await approveAndStake(stakeAmount, from) + await staking.allowManagerAndLock(lockAmount, manager, lockAmount, ACTIVATED_LOCK, { from }) + } + + before(async () => { + owner = accounts[0] + user1 = accounts[1] + user2 = accounts[2] + }) + + beforeEach(async () => { + const deployment = await deploy(owner) + token = deployment.token + staking = deployment.staking + lockManager = deployment.lockManager + + stakingAddress = staking.address + tokenAddress = token.address + lockManagerAddress = lockManager.address + }) + + // increases 1185 gas for each lock + it('measures unlockedBalanceOf gas', async () => { + await approveStakeAndLock(lockManagerAddress) + + const r = await staking.unlockedBalanceOfGas() + const gas = getEvent(r, 'LogGas', 'gas') + console.log(`unlockedBalanceOf gas: ${gas.toNumber()}`) + }) + + // 110973 gas + /* + it('measures lock gas', async () => { + await approveAndStake() + + const r = await staking.lockGas(DEFAULT_LOCK_AMOUNT, lockManagerAddress, ACTIVATED_LOCK, { from: owner }) + const gas = getEvent(r, 'LogGas', 'gas') + console.log('lock gas:', gas.toNumber()) + }) + */ + + // 27601 gas + it('measures transfer gas', async () => { + await approveStakeAndLock(lockManagerAddress) + + const r = await staking.transferGas(owner, lockManagerAddress, DEFAULT_LOCK_AMOUNT) + const gas = getEvent(r, 'LogGas', 'gas') + console.log('transfer gas:', gas.toNumber()) + }) + + /* + it('measures unlock gas', async () => { + await approveStakeAndLock(user1) + + const r = await staking.unlockGas(owner, user1, { from: user1 }) + const gas = getEvent(r, 'LogGas', 'gas') + console.log(`unlock gas: ${gas.toNumber()}`) + await approveStakeAndLock(lockManagerAddress) + }) + */ +}) diff --git a/tests-solidity/suites/staking/test/helpers/constants.js b/tests-solidity/suites/staking/test/helpers/constants.js new file mode 100644 index 0000000000..ce011849fa --- /dev/null +++ b/tests-solidity/suites/staking/test/helpers/constants.js @@ -0,0 +1,10 @@ +const { bn, bigExp } = require('@aragon/contract-helpers-test/numbers') +const DEFAULT_STAKE_AMOUNT = bigExp(120, 18) + +module.exports = { + DEFAULT_STAKE_AMOUNT, + DEFAULT_LOCK_AMOUNT: DEFAULT_STAKE_AMOUNT.div(bn(3)), + EMPTY_DATA: '0x', + ZERO_ADDRESS: '0x' + '0'.repeat(40), + ACTIVATED_LOCK: '0x01' +} diff --git a/tests-solidity/suites/staking/test/helpers/deploy.js b/tests-solidity/suites/staking/test/helpers/deploy.js new file mode 100644 index 0000000000..ac0ffbc523 --- /dev/null +++ b/tests-solidity/suites/staking/test/helpers/deploy.js @@ -0,0 +1,35 @@ +const { bn } = require('@aragon/contract-helpers-test/numbers') + +const { DEFAULT_STAKE_AMOUNT } = require('./constants') + +module.exports = (artifacts) => { + const StakingFactory = artifacts.require('StakingFactory') + const Staking = artifacts.require('Staking') + const StandardTokenMock = artifacts.require('StandardTokenMock') + const LockManagerMock = artifacts.require('LockManagerMock') + + const getEvent = (receipt, event, arg) => { return receipt.logs.filter(l => l.event === event)[0].args[arg] } + + const deploy = async (owner, initialAmount = DEFAULT_STAKE_AMOUNT.mul(bn(1000))) => { + const token = await StandardTokenMock.new(owner, initialAmount) + + const staking = await deployStaking(token) + + const lockManager = await LockManagerMock.new() + + return { token, staking, lockManager } + } + + const deployStaking = async (token) => { + const factory = await StakingFactory.new() + const receipt = await factory.getOrCreateInstance(token.address) + const stakingAddress = getEvent(receipt, 'NewStaking', 'instance') + const staking = await Staking.at(stakingAddress) + + return staking + } + + return { + deploy + } +} diff --git a/tests-solidity/suites/staking/test/helpers/errors.js b/tests-solidity/suites/staking/test/helpers/errors.js new file mode 100644 index 0000000000..4754a26f26 --- /dev/null +++ b/tests-solidity/suites/staking/test/helpers/errors.js @@ -0,0 +1,35 @@ +const CHECKPOINT_ERRORS = { + ERROR_VALUE_TOO_BIG: 'CHECKPOINT_VALUE_TOO_BIG', + ERROR_CANNOT_ADD_PAST_VALUE: 'CHECKPOINT_CANNOT_ADD_PAST_VALUE', +} + +const STAKING_ERRORS = { + ERROR_TOKEN_NOT_CONTRACT: 'STAKING_TOKEN_NOT_CONTRACT', + ERROR_AMOUNT_ZERO: 'STAKING_AMOUNT_ZERO', + ERROR_TOKEN_TRANSFER: 'STAKING_TOKEN_TRANSFER_FAIL', + ERROR_TOKEN_DEPOSIT: 'STAKING_TOKEN_DEPOSIT_FAIL', + ERROR_TOKEN_NOT_SENDER: 'STAKING_TOKEN_NOT_SENDER', + ERROR_WRONG_TOKEN: 'STAKING_WRONG_TOKEN', + ERROR_NOT_ENOUGH_BALANCE: 'STAKING_NOT_ENOUGH_BALANCE', + ERROR_NOT_ENOUGH_ALLOWANCE: 'STAKING_NOT_ENOUGH_ALLOWANCE', + ERROR_SENDER_NOT_ALLOWED: 'STAKING_SENDER_NOT_ALLOWED', + ERROR_ALLOWANCE_ZERO: 'STAKING_ALLOWANCE_ZERO', + ERROR_LOCK_ALREADY_EXISTS: 'STAKING_LOCK_ALREADY_EXISTS', + ERROR_LOCK_DOES_NOT_EXIST: 'STAKING_LOCK_DOES_NOT_EXIST', + ERROR_NOT_ENOUGH_LOCK: 'STAKING_NOT_ENOUGH_LOCK', + ERROR_CANNOT_UNLOCK: 'STAKING_CANNOT_UNLOCK', + ERROR_CANNOT_CHANGE_ALLOWANCE: 'STAKING_CANNOT_CHANGE_ALLOWANCE', + ERROR_LOCKMANAGER_CALL_FAIL: 'STAKING_LOCKMANAGER_CALL_FAIL', + ERROR_BLOCKNUMBER_TOO_BIG: 'STAKING_BLOCKNUMBER_TOO_BIG', +} + +const TIME_LOCK_MANAGER_ERRORS = { + ERROR_ALREADY_LOCKED: 'TLM_ALREADY_LOCKED', + ERROR_WRONG_INTERVAL: 'TLM_WRONG_INTERVAL', +} + +module.exports = { + CHECKPOINT_ERRORS, + STAKING_ERRORS, + TIME_LOCK_MANAGER_ERRORS, +} diff --git a/tests-solidity/suites/staking/test/helpers/helpers.js b/tests-solidity/suites/staking/test/helpers/helpers.js new file mode 100644 index 0000000000..b799ea0a4e --- /dev/null +++ b/tests-solidity/suites/staking/test/helpers/helpers.js @@ -0,0 +1,283 @@ +const { assertRevert } = require('@aragon/contract-helpers-test/assertThrow') +const { bn, assertBn } = require('@aragon/contract-helpers-test/numbers') + +const { DEFAULT_STAKE_AMOUNT, DEFAULT_LOCK_AMOUNT, EMPTY_DATA, ZERO_ADDRESS } = require('./constants') +const { STAKING_ERRORS } = require('../helpers/errors') + +module.exports = (artifacts) => { + const StandardTokenMock = artifacts.require('StandardTokenMock') + const LockManagerMock = artifacts.require('LockManagerMock') + + const approveAndStake = async ({ staking, amount = DEFAULT_STAKE_AMOUNT, from }) => { + const token = await StandardTokenMock.at(await staking.token()) + await token.approve(staking.address, amount, { from }) + await staking.stake(amount, EMPTY_DATA, { from }) + } + + const approveStakeAndLock = async ({ + staking, + manager, + allowanceAmount = DEFAULT_LOCK_AMOUNT, + lockAmount = DEFAULT_LOCK_AMOUNT, + stakeAmount = DEFAULT_STAKE_AMOUNT, + data = EMPTY_DATA, + from + }) => { + await approveAndStake({ staking, stake: stakeAmount, from }) + const receipt = await staking.allowManagerAndLock(lockAmount, manager, allowanceAmount, data, { from }) + + return receipt + } + + // funds flows helpers + + function UserState(address, walletBalance) { + this.address = address + this.walletBalance = walletBalance + this.stakedBalance = bn(0) + this.lockedBalance = bn(0) + + this.walletAdd = (amount) => { this.walletBalance = this.walletBalance.add(amount) } + this.walletSub = (amount) => { this.walletBalance = this.walletBalance.sub(amount) } + + this.stakedAdd = (amount) => { this.stakedBalance = this.stakedBalance.add(amount) } + this.stakedSub = (amount) => { this.stakedBalance = this.stakedBalance.sub(amount) } + + this.lockedAdd = (amount) => { this.lockedBalance = this.lockedBalance.add(amount) } + this.lockedSub = (amount) => { this.lockedBalance = this.lockedBalance.sub(amount) } + + this.totalBalance = () => this.walletBalance.add(this.stakedBalance) + } + + const approveAndStakeWithState = async ({ staking, amount = DEFAULT_STAKE_AMOUNT, user }) => { + await approveAndStake({ staking, amount, from: user.address }) + user.walletSub(amount) + user.stakedAdd(amount) + } + + const approveStakeAndLockWithState = async ({ + staking, + manager, + allowanceAmount = DEFAULT_LOCK_AMOUNT, + lockAmount = DEFAULT_LOCK_AMOUNT, + stakeAmount = DEFAULT_STAKE_AMOUNT, + data = EMPTY_DATA, + user + }) => { + await approveStakeAndLock({ staking, manager, allowanceAmount, lockAmount, stakeAmount, data, from: user.address }) + user.walletSub(stakeAmount) + user.stakedAdd(stakeAmount) + user.lockedAdd(lockAmount) + } + + const unstakeWithState = async ({ staking, unstakeAmount, user }) => { + await staking.unstake(unstakeAmount, EMPTY_DATA, { from: user.address }) + user.walletAdd(unstakeAmount) + user.stakedSub(unstakeAmount) + } + + const unlockWithState = async ({ staking, managerAddress, unlockAmount, user }) => { + await staking.unlock(user.address, managerAddress, unlockAmount, { from: user.address }) + user.lockedSub(unlockAmount) + } + + const unlockFromManagerWithState = async ({ staking, lockManager, unlockAmount, user }) => { + await lockManager.unlock(staking.address, user.address, unlockAmount, { from: user.address }) + user.lockedSub(unlockAmount) + } + + const transferWithState = async ({ staking, transferAmount, userFrom, userTo }) => { + await staking.transfer(userTo.address, transferAmount, { from: userFrom.address }) + userTo.stakedAdd(transferAmount) + userFrom.stakedSub(transferAmount) + } + + const transferAndUnstakeWithState = async ({ staking, transferAmount, userFrom, userTo }) => { + await staking.transferAndUnstake(userTo.address, transferAmount, { from: userFrom.address }) + userTo.walletAdd(transferAmount) + userFrom.stakedSub(transferAmount) + } + + const slashWithState = async ({ staking, slashAmount, userFrom, userTo, managerAddress }) => { + await staking.slash(userFrom.address, userTo.address, slashAmount, { from: managerAddress }) + userTo.stakedAdd(slashAmount) + userFrom.stakedSub(slashAmount) + userFrom.lockedSub(slashAmount) + } + + const slashAndUnstakeWithState = async ({ staking, slashAmount, userFrom, userTo, managerAddress }) => { + await staking.slashAndUnstake(userFrom.address, userTo.address, slashAmount, { from: managerAddress }) + userTo.walletAdd(slashAmount) + userFrom.stakedSub(slashAmount) + userFrom.lockedSub(slashAmount) + } + + const slashFromContractWithState = async ({ staking, slashAmount, userFrom, userTo, lockManager }) => { + await lockManager.slash(staking.address, userFrom.address, userTo.address, slashAmount) + userTo.stakedAdd(slashAmount) + userFrom.stakedSub(slashAmount) + userFrom.lockedSub(slashAmount) + } + + const slashAndUnstakeFromContractWithState = async ({ staking, slashAmount, userFrom, userTo, lockManager }) => { + await lockManager.slashAndUnstake(staking.address, userFrom.address, userTo.address, slashAmount) + userTo.walletAdd(slashAmount) + userFrom.stakedSub(slashAmount) + userFrom.lockedSub(slashAmount) + } + + // check that real user balances (token in external wallet, staked and locked) match with accounted in state + const checkUserBalances = async ({ staking, users }) => { + const token = await StandardTokenMock.at(await staking.token()) + await Promise.all( + users.map(async (user) => { + assertBn(user.walletBalance, await token.balanceOf(user.address), 'token balance doesn’t match') + const balances = await staking.getBalancesOf(user.address) + assertBn(user.stakedBalance, balances.staked, 'staked balance doesn’t match') + assertBn(user.lockedBalance, balances.locked, 'locked balance doesn’t match') + }) + ) + } + + // check that Staking contract total staked matches with: + // - total staked by users in state (must go in combination with checkUserBalances, to make sure this is legit) + // - token balance of staking app + const checkTotalStaked = async ({ staking, users }) => { + const totalStaked = await staking.totalStaked() + const totalStakedState = users.reduce((total, user) => total.add(user.stakedBalance), bn(0)) + assertBn(totalStaked, totalStakedState, 'total staked doesn’t match') + const token = await StandardTokenMock.at(await staking.token()) + const stakingTokenBalance = await token.balanceOf(staking.address) + assertBn(totalStaked, stakingTokenBalance, 'Staking token balance doesn’t match') + } + + // check that staked balance is greater than locked balance for all users + // uses local state for efficiency, so it must go with checkUserBalances + const checkStakeAndLock = ({ staking, users }) => { + users.map(user => assert.isTrue(user.stakedBalance.gte(user.lockedBalance))) + } + + // check that allowed balance is always greater than locked balance, for all pairs of owner-manager + const checkAllowanceAndLock = async ({ staking, users, managers }) => { + await Promise.all( + users.map(async (user) => await Promise.all( + managers.map(async (manager) => { + const lock = await staking.getLock(user.address, manager) + assert.isTrue(lock._amount.lte(lock._allowance)) + }) + )) + ) + } + + // check that users can’t unstake more than unlocked balance + const checkOverUnstaking = async ({ staking, users }) => { + await Promise.all( + users.map(async (user) => { + await assertRevert( + staking.unstake(user.stakedBalance.sub(user.lockedBalance).add(bn(1)), EMPTY_DATA, { from: user.address })/*, + STAKING_ERRORS.ERROR_NOT_ENOUGH_BALANCE + */ + ) + }) + ) + } + + // check that users can’t unlock more than locked balance + const checkOverUnlocking = async ({ staking, users, managers }) => { + await Promise.all( + users.map(async (user) => await Promise.all( + managers.map(async (manager) => { + const lock = await staking.getLock(user.address, manager) + // const errorMessage = lock._allowance.gt(bn(0)) ? STAKING_ERRORS.ERROR_NOT_ENOUGH_LOCK : STAKING_ERRORS.ERROR_LOCK_DOES_NOT_EXIST + await assertRevert( + staking.unlock(user.address, manager, user.lockedBalance.add(bn(1)), { from: user.address })/*, + errorMessage + */ + ) + }) + )) + ) + } + + // check that users can’t transfer more than unlocked balance + const checkOverTransferring = async ({ staking, users }) => { + await Promise.all( + users.map(async (user) => { + const to = user.address === users[0].address ? users[1].address : users[0].address + await assertRevert( + staking.transfer(to, user.stakedBalance.sub(user.lockedBalance).add(bn(1)), { from: user.address })/*, + STAKING_ERRORS.ERROR_NOT_ENOUGH_BALANCE + */ + ) + await assertRevert( + staking.transferAndUnstake(to, user.stakedBalance.sub(user.lockedBalance).add(bn(1)), { from: user.address })/*, + STAKING_ERRORS.ERROR_NOT_ENOUGH_BALANCE + */ + ) + }) + ) + } + + // check that managers can’t slash more than locked balance + const checkOverSlashing = async ({ staking, users, managers }) => { + await Promise.all( + users.map(async (user) => { + const to = user.address === users[0].address ? users[1].address : users[0].address + for (let i = 0; i < managers.length - 1; i++) { + await assertRevert( + staking.slash(user.address, to, user.lockedBalance.add(bn(1)), { from: managers[i] })/*, + STAKING_ERRORS.ERROR_NOT_ENOUGH_LOCK + */ + ) + await assertRevert( + staking.slashAndUnstake(user.address, to, user.lockedBalance.add(bn(1)), { from: managers[i] }),/* + STAKING_ERRORS.ERROR_NOT_ENOUGH_LOCK + */ + ) + } + // last in the array is a contract + const lockManagerAddress = managers[managers.length - 1] + const lockManager = await LockManagerMock.at(lockManagerAddress) + await assertRevert( + lockManager.slash(staking.address, user.address, to, user.lockedBalance.add(bn(1))),/* + STAKING_ERRORS.ERROR_NOT_ENOUGH_LOCK + */ + ) + await assertRevert( + lockManager.slashAndUnstake(staking.address, user.address, to, user.lockedBalance.add(bn(1))),/* + STAKING_ERRORS.ERROR_NOT_ENOUGH_LOCK + */ + ) + }) + ) + } + + const checkInvariants = async ({ staking, users, managers }) => { + await checkUserBalances({ staking, users }) + await checkTotalStaked({ staking, users }) + checkStakeAndLock({ staking, users }) + await checkAllowanceAndLock({ staking, users, managers }) + await checkOverUnstaking({ staking, users }) + await checkOverUnlocking({ staking, users, managers }) + await checkOverTransferring({ staking, users }) + await checkOverSlashing({ staking, users, managers }) + } + + return { + approveAndStake, + approveStakeAndLock, + UserState, + approveAndStakeWithState, + approveStakeAndLockWithState, + unstakeWithState, + unlockWithState, + unlockFromManagerWithState, + transferWithState, + transferAndUnstakeWithState, + slashWithState, + slashAndUnstakeWithState, + slashFromContractWithState, + slashAndUnstakeFromContractWithState, + checkInvariants, + } +} diff --git a/tests-solidity/suites/staking/test/lib/checkpointing.js b/tests-solidity/suites/staking/test/lib/checkpointing.js new file mode 100644 index 0000000000..0e17933667 --- /dev/null +++ b/tests-solidity/suites/staking/test/lib/checkpointing.js @@ -0,0 +1,226 @@ +const { assertRevert } = require('@aragon/contract-helpers-test/assertThrow') +const { bn, assertBn, MAX_UINT256 } = require('@aragon/contract-helpers-test/numbers') +const { CHECKPOINT_ERRORS } = require('../helpers/errors') + +const Checkpointing = artifacts.require('CheckpointingMock') + +contract('Checkpointing', () => { + let checkpointing + + beforeEach('create tree', async () => { + checkpointing = await Checkpointing.new() + }) + + const assertFetchedValue = async (time, expectedValue) => { + assertBn((await checkpointing.get(time)), expectedValue, 'value does not match') + } + + describe('add', () => { + context('when the given value is can be represented by 192 bits', () => { + const value = bn(100) + + context('when there was no value registered yet', async () => { + context('when the given time is zero', async () => { + const time = bn(0) + + it('adds the new value', async () => { + await checkpointing.add(time, value) + + await assertFetchedValue(time, value) + }) + }) + + context('when the given time is greater than zero', async () => { + const time= bn(1) + + it('adds the new value', async () => { + await checkpointing.add(time, value) + + await assertFetchedValue(time, value) + }) + }) + }) + + context('when there were some values already registered', async () => { + beforeEach('add some values', async () => { + await checkpointing.add(30, 1) + await checkpointing.add(50, 2) + await checkpointing.add(90, 3) + }) + + context('when the given time is previous to the latest registered value', async () => { + const time= bn(40) + + it('reverts', async () => { + await assertRevert(checkpointing.add(time, value)/*, CHECKPOINT_ERRORS.CANNOT_ADD_PAST_VALUE*/) + }) + }) + + context('when the given time is equal to the latest registered value', async () => { + const time= bn(90) + + it('updates the already registered value', async () => { + await checkpointing.add(time, value) + + await assertFetchedValue(time, value) + await assertFetchedValue(time.add(bn(1)), value) + }) + }) + + context('when the given time is after the latest registered value', async () => { + const time= bn(95) + + it('adds the new last value', async () => { + const previousLast = await checkpointing.getLast() + + await checkpointing.add(time, value) + + await assertFetchedValue(time, value) + await assertFetchedValue(time.add(bn(1)), value) + await assertFetchedValue(time.sub(bn(1)), previousLast) + }) + }) + }) + }) + + context('when the given value cannot be represented by 192 bits', () => { + const value = MAX_UINT256 + + it('reverts', async () => { + await assertRevert(checkpointing.add(0, value)/*, CHECKPOINT_ERRORS.VALUE_TOO_BIG*/) + }) + }) + }) + + describe('lastUpdate', () => { + context('when there are no values registered yet', () => { + it('returns zero', async () => { + assertBn((await checkpointing.lastUpdate()), bn(0), 'time does not match') + }) + }) + + context('when there are values already registered', () => { + beforeEach('add some values', async () => { + await checkpointing.add(30, 1) + await checkpointing.add(50, 2) + await checkpointing.add(90, 3) + }) + + it('returns the last registered value', async () => { + assertBn((await checkpointing.lastUpdate()), bn(90), 'time does not match') + }) + }) + }) + + describe('getLast', () => { + context('when there are no values registered yet', () => { + it('returns zero', async () => { + assertBn((await checkpointing.getLast()), bn(0), 'value does not match') + }) + }) + + context('when there are values already registered', () => { + beforeEach('add some values', async () => { + await checkpointing.add(30, 1) + await checkpointing.add(50, 2) + await checkpointing.add(90, 3) + }) + + it('returns the last registered value', async () => { + assertBn((await checkpointing.getLast()), bn(3), 'value does not match') + }) + }) + }) + + describe('get', () => { + context('when there are no values registered yet', () => { + context('when there given time is zero', () => { + const time= bn(0) + + it('returns zero', async () => { + await assertFetchedValue(time, bn(0)) + }) + }) + + context('when there given time is greater than zero', () => { + const time= bn(1) + + it('returns zero', async () => { + await assertFetchedValue(time, bn(0)) + }) + }) + }) + + context('when there are values already registered', () => { + beforeEach('add some values', async () => { + await checkpointing.add(30, 1) + await checkpointing.add(50, 2) + await checkpointing.add(90, 3) + }) + + context('when there given time is zero', () => { + const time= bn(0) + + it('returns zero', async () => { + await assertFetchedValue(time, bn(0)) + }) + }) + + context('when the given time is previous to the time of first registered value', () => { + const time= bn(10) + + it('returns zero', async () => { + await assertFetchedValue(time, bn(0)) + }) + }) + + context('when the given time is equal to the time of first registered value', () => { + const time= bn(30) + + it('returns the first registered value', async () => { + await assertFetchedValue(time, bn(1)) + }) + }) + + context('when the given time is between the times of first and the second registered values', () => { + const time= bn(40) + + it('returns the first registered value', async () => { + await assertFetchedValue(time, bn(1)) + }) + }) + + context('when the given time is the time of the second registered values', () => { + const time= bn(50) + + it('returns the second registered value', async () => { + await assertFetchedValue(time, bn(2)) + }) + }) + + context('when the given time is between the times of second and the third registered values', () => { + const time= bn(60) + + it('returns the second registered value', async () => { + await assertFetchedValue(time, bn(2)) + }) + }) + + context('when the given time is equal to the time of the third registered values', () => { + const time= bn(90) + + it('returns the third registered value', async () => { + await assertFetchedValue(time, bn(3)) + }) + }) + + context('when the given time is after the time of the third registered values', () => { + const time= bn(100) + + it('returns the third registered value', async () => { + await assertFetchedValue(time, bn(3)) + }) + }) + }) + }) +}) diff --git a/tests-solidity/suites/staking/test/locking/funds_flows.js b/tests-solidity/suites/staking/test/locking/funds_flows.js new file mode 100644 index 0000000000..1ba1b00659 --- /dev/null +++ b/tests-solidity/suites/staking/test/locking/funds_flows.js @@ -0,0 +1,308 @@ +const { assertRevert } = require('@aragon/contract-helpers-test/assertThrow') +const { bn, assertBn, MAX_UINT64 } = require('@aragon/contract-helpers-test/numbers') + +const { deploy } = require('../helpers/deploy')(artifacts) +const { + UserState, + approveAndStakeWithState, + approveStakeAndLockWithState, + unstakeWithState, + unlockWithState, + unlockFromManagerWithState, + transferWithState, + transferAndUnstakeWithState, + slashWithState, + slashAndUnstakeWithState, + slashFromContractWithState, + slashAndUnstakeFromContractWithState, + checkInvariants, +} = require('../helpers/helpers')(artifacts) +const { DEFAULT_STAKE_AMOUNT, DEFAULT_LOCK_AMOUNT, EMPTY_DATA, ZERO_ADDRESS } = require('../helpers/constants') + +contract('Staking app, Locking funds flows', ([_, owner, user1, user2, user3]) => { + let staking, lockManager, users, managers, token + + beforeEach(async () => { + const deployment = await deploy(owner) + staking = deployment.staking + lockManager = deployment.lockManager + + token = deployment.token + + // fund users and create user state objects + users = [] + const userAddresses = [user1, user2, user3] + await Promise.all(userAddresses.map(async (userAddress, index) => { + const amount = DEFAULT_STAKE_AMOUNT.mul(bn(userAddresses.length - index)) + users.push(new UserState(userAddress, amount)) + await token.transfer(userAddress, amount, { from: owner }) + })) + + // managers + managers = users.reduce((result, user) => { + result.push(user.address) + return result + }, []) + managers.push(lockManager.address) + }) + + describe('same origin and destiny', () => { + context('when user hasn’t staked', () => { + it('check invariants', async () => { + await checkInvariants({ staking, users, managers }) + }) + }) + + context('when user has staked', () => { + const stakeAmount = DEFAULT_STAKE_AMOUNT + + beforeEach('stakes', async () => { + await checkInvariants({ staking, users, managers }) + await approveAndStakeWithState({ staking, amount: stakeAmount, user: users[0] }) + await checkInvariants({ staking, users, managers }) + }) + + context('when user hasn’t locked', () => { + it('unstakes half', async () => { + await unstakeWithState({ staking, unstakeAmount: stakeAmount.div(bn(2)), user: users[0] }) + await checkInvariants({ staking, users, managers }) + }) + + it('unstakes all', async () => { + await unstakeWithState({ staking, unstakeAmount: stakeAmount, user: users[0] }) + await checkInvariants({ staking, users, managers }) + }) + }) + + context('when user has locked', () => { + const lockAmount = DEFAULT_LOCK_AMOUNT + + const moveFunds = ({ isContract, canUnlock = false }) => { + let lockManagerAddress + + beforeEach('stakes and locks', async () => { + lockManagerAddress = isContract ? lockManager.address : user3 + if (isContract && canUnlock) { + await lockManager.setResult(true) + } + + await checkInvariants({ staking, users, managers }) + await approveStakeAndLockWithState({ + staking, + manager: lockManagerAddress, + stakeAmount, + allowanceAmount: stakeAmount, + lockAmount, + user: users[0] + }) + await checkInvariants({ staking, users, managers }) + }) + + const unstake = async (unstakeAmount) => { + await unstakeWithState({ staking, unstakeAmount, user: users[0] }) + await checkInvariants({ staking, users, managers }) + } + + it('unstakes remaining', async () => { + await unstake(stakeAmount.sub(lockAmount)) + }) + + const unlockAndUnstake = async (unlockAmount) => { + await unlockWithState({ staking, managerAddress: lockManagerAddress, unlockAmount, user: users[0]}) + await unstake(stakeAmount.sub(lockAmount.sub(unlockAmount))) + } + + const unlockAndUnstakeFromManager = async (unlockAmount) => { + await unlockFromManagerWithState({ staking, lockManager, unlockAmount, user: users[0]}) + await unstake(stakeAmount.sub(lockAmount.sub(unlockAmount))) + } + + if (canUnlock) { + it('owner unlocks half and then unstakes', async () => { + await unlockAndUnstake(lockAmount.div(bn(2))) + }) + + it('owner unlocks all and then unstakes', async () => { + await unlockAndUnstake(lockAmount) + }) + } else { + it('owner cannot unlock', async () => { + await assertRevert(staking.unlock(users[0].address, lockManagerAddress, bn(1), { from: users[0].address })) + }) + + if (isContract) { + it('manager unlocks half and then owner unstakes', async () => { + await unlockAndUnstakeFromManager(lockAmount.div(bn(2))) + }) + + it('manager unlocks all and then owner unstakes', async () => { + await unlockAndUnstakeFromManager(lockAmount) + }) + } + } + } + + context('when lock manager is EOA', () => { + moveFunds({ isContract: false, canUnlock: false }) + }) + + context('when lock manager is contract', () => { + context('when lock manager allows to unlock', () => { + moveFunds({ isContract: true, canUnlock: true }) + }) + + context('when lock manager doesn’t allow to unlock', () => { + moveFunds({ isContract: true, canUnlock: false }) + }) + }) + }) + }) + }) + + describe('different origin and destiny', () => { + context('when user hasn’t staked', () => { + it('check invariants', async () => { + await checkInvariants({ staking, users, managers }) + }) + }) + + context('when user has staked', () => { + const stakeAmount = DEFAULT_STAKE_AMOUNT + + beforeEach('stakes', async () => { + await checkInvariants({ staking, users, managers }) + await approveAndStakeWithState({ staking, amount: stakeAmount, user: users[0] }) + await checkInvariants({ staking, users, managers }) + }) + + context('when user hasn’t locked', () => { + context('to staking balance', () => { + it('transfers half', async () => { + await transferWithState({ staking, transferAmount: stakeAmount.div(bn(2)), userFrom: users[0], userTo: users[1] }) + await checkInvariants({ staking, users, managers }) + }) + + it('transfers all', async () => { + await transferWithState({ staking, transferAmount: stakeAmount, userFrom: users[0], userTo: users[1] }) + await checkInvariants({ staking, users, managers }) + }) + }) + + context('to external wallet', () => { + it('transfers half', async () => { + await transferAndUnstakeWithState({ staking, transferAmount: stakeAmount.div(bn(2)), userFrom: users[0], userTo: users[1] }) + await checkInvariants({ staking, users, managers }) + }) + + it('transfers all', async () => { + await transferAndUnstakeWithState({ staking, transferAmount: stakeAmount, userFrom: users[0], userTo: users[1] }) + await checkInvariants({ staking, users, managers }) + }) + }) + }) + + context('when user has locked', () => { + const lockAmount = DEFAULT_LOCK_AMOUNT + + const moveFunds = ({ isContract, canUnlock = false, toStaking }) => { + let lockManagerAddress + + beforeEach('stakes and locks', async () => { + lockManagerAddress = isContract ? lockManager.address : user3 + if (isContract && canUnlock) { + await lockManager.setResult(true) + } + + await checkInvariants({ staking, users, managers }) + await approveStakeAndLockWithState({ + staking, + manager: lockManagerAddress, + stakeAmount, + allowanceAmount: stakeAmount, + lockAmount, + user: users[0] + }) + await checkInvariants({ staking, users, managers }) + }) + + const transfer = async (transferAmount) => { + await transferWithState({ staking, transferAmount, userFrom: users[0], userTo: users[1] }) + await checkInvariants({ staking, users, managers }) + } + + it('transfers remaining', async () => { + await transfer(stakeAmount.sub(lockAmount)) + }) + + const slashAndTransfer = async (slashAmount) => { + if (toStaking) { + await slashWithState({ staking, slashAmount, userFrom: users[0], userTo: users[1], managerAddress: lockManagerAddress }) + } else { + await slashAndUnstakeWithState({ staking, slashAmount, userFrom: users[0], userTo: users[1], managerAddress: lockManagerAddress }) + } + await transfer(stakeAmount.sub(lockAmount.sub(slashAmount))) + } + + const slashAndTransferFromContract = async (slashAmount) => { + if (toStaking) { + await slashFromContractWithState({ staking, slashAmount, userFrom: users[0], userTo: users[1], lockManager }) + } else { + await slashAndUnstakeFromContractWithState({ staking, slashAmount, userFrom: users[0], userTo: users[1], lockManager }) + } + await transfer(stakeAmount.sub(lockAmount.sub(slashAmount))) + } + + if (isContract) { + it('manager unlockes half and then owner transfers', async () => { + await slashAndTransferFromContract(lockAmount.div(bn(2))) + }) + + it('manager slashes all and then owner transfers', async () => { + await slashAndTransferFromContract(lockAmount) + }) + } else { + it('manager slashes half and then transfers', async () => { + await slashAndTransfer(lockAmount.div(bn(2))) + }) + + it('manager slashes all and then transfers', async () => { + await slashAndTransfer(lockAmount) + }) + } + } + + context('when lock manager is EOA', () => { + context('to staking balance', () => { + moveFunds({ isContract: false, canUnlock: false, toStaking: true }) + }) + + context('to external wallet', () => { + moveFunds({ isContract: false, canUnlock: false, toStaking: false }) + }) + }) + + context('when lock manager is contract', () => { + context('when lock manager allows to unlock', () => { + context('to staking balance', () => { + moveFunds({ isContract: true, canUnlock: true, toStaking: true }) + }) + + context('to external wallet', () => { + moveFunds({ isContract: true, canUnlock: true, toStaking: false }) + }) + }) + + context('when lock manager doesn’t allow to unlock', () => { + context('to staking balance', () => { + moveFunds({ isContract: true, canUnlock: false, toStaking: true }) + }) + + context('to external wallet', () => { + moveFunds({ isContract: true, canUnlock: false, toStaking: false }) + }) + }) + }) + }) + }) + }) +}) diff --git a/tests-solidity/suites/staking/test/locking/locking.js b/tests-solidity/suites/staking/test/locking/locking.js new file mode 100644 index 0000000000..e688f85a48 --- /dev/null +++ b/tests-solidity/suites/staking/test/locking/locking.js @@ -0,0 +1,395 @@ +const { assertRevert } = require('@aragon/contract-helpers-test/assertThrow') +const { bn, bigExp, assertBn, MAX_UINT64 } = require('@aragon/contract-helpers-test/numbers') + +const { deploy } = require('../helpers/deploy')(artifacts) +const { approveAndStake, approveStakeAndLock } = require('../helpers/helpers')(artifacts) +const { DEFAULT_STAKE_AMOUNT, DEFAULT_LOCK_AMOUNT, EMPTY_DATA, ZERO_ADDRESS } = require('../helpers/constants') +const { STAKING_ERRORS } = require('../helpers/errors') + +contract('Staking app, Locking', ([owner, user1, user2]) => { + let staking, lockManager + + beforeEach(async () => { + const deployment = await deploy(owner) + staking = deployment.staking + lockManager = deployment.lockManager + }) + + it('allows new manager and locks amount', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + + // check lock values + const { _amount, _allowance } = await staking.getLock(owner, user1) + assertBn(_amount, DEFAULT_LOCK_AMOUNT, "locked amount should match") + assertBn(_allowance, DEFAULT_LOCK_AMOUNT, "locked allowance should match") + + assertBn(await staking.unlockedBalanceOf(owner), DEFAULT_STAKE_AMOUNT.sub(DEFAULT_LOCK_AMOUNT), "Unlocked balance should match") + const { staked, locked } = await staking.getBalancesOf(owner) + assertBn(staked, DEFAULT_STAKE_AMOUNT, "Staked balance should match") + assertBn(locked, DEFAULT_LOCK_AMOUNT, "Locked balance should match") + }) + + it('fails locking 0 tokens', async () => { + await approveAndStake({ staking, from: owner }) + await assertRevert(staking.allowManagerAndLock(0, user1, 1, EMPTY_DATA), STAKING_ERRORS.ERROR_AMOUNT_ZERO) + }) + + it('fails locking without enough allowance', async () => { + await approveAndStake({ staking, from: owner }) + await assertRevert(staking.allowManagerAndLock(2, user1, 1, EMPTY_DATA), STAKING_ERRORS.ERROR_NOT_ENOUGH_ALLOWANCE) + }) + + it('fails locking more tokens than staked', async () => { + await approveAndStake({ staking, from: owner }) + await assertRevert(staking.allowManagerAndLock(DEFAULT_STAKE_AMOUNT.add(bn(1)), user1, DEFAULT_STAKE_AMOUNT.add(bn(1)), EMPTY_DATA), STAKING_ERRORS.ERROR_NOT_ENOUGH_BALANCE) + }) + + it('fails locking if already locked', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + + await approveAndStake({ staking, from: owner }) + await assertRevert(staking.allowManagerAndLock(DEFAULT_STAKE_AMOUNT, user1, DEFAULT_STAKE_AMOUNT, "0x02"), STAKING_ERRORS.ERROR_LOCK_ALREADY_EXISTS) + }) + + it('fails unstaking locked tokens', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + + await assertRevert(staking.unstake(DEFAULT_STAKE_AMOUNT, EMPTY_DATA), STAKING_ERRORS.ERROR_NOT_ENOUGH_BALANCE) + }) + + it('creates a new allowance', async () => { + await staking.allowManager(user1, DEFAULT_LOCK_AMOUNT, EMPTY_DATA) + + const { _allowance } = await staking.getLock(owner, user1) + assertBn(_allowance, DEFAULT_LOCK_AMOUNT, "allowed amount should match") + }) + + it('creates a new allowance and then lock manager locks', async () => { + await approveAndStake({ staking, from: owner }) + await staking.allowManager(user1, DEFAULT_LOCK_AMOUNT, EMPTY_DATA) + await staking.lock(owner, user1, DEFAULT_LOCK_AMOUNT, { from: user1 }) + + // check lock values + const { _amount, _allowance } = await staking.getLock(owner, user1) + assertBn(_amount, DEFAULT_LOCK_AMOUNT, "locked amount should match") + assertBn(_allowance, DEFAULT_LOCK_AMOUNT, "locked allowance should match") + + assertBn(await staking.unlockedBalanceOf(owner), DEFAULT_STAKE_AMOUNT.sub(DEFAULT_LOCK_AMOUNT), "Unlocked balance should match") + }) + + it('fails creating allowance of 0 tokens', async () => { + await assertRevert(staking.allowManager(user1, 0, EMPTY_DATA), STAKING_ERRORS.ERROR_AMOUNT_ZERO) + }) + + it('fails creating allowance if lock exists', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + await assertRevert(staking.allowManager(user1, 1, EMPTY_DATA), STAKING_ERRORS.ERROR_LOCK_ALREADY_EXISTS) + }) + + it('increases allowance of existing lock', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + + await staking.increaseLockAllowance(user1, DEFAULT_LOCK_AMOUNT) + + const { _allowance } = await staking.getLock(owner, user1) + assertBn(_allowance, DEFAULT_LOCK_AMOUNT.mul(bn(2)), "allowed amount should match") + }) + + it('fails increasing allowance of non-existing', async () => { + await assertRevert(staking.increaseLockAllowance(user1, 1), STAKING_ERRORS.ERROR_LOCK_DOES_NOT_EXIST) + }) + + it('fails increasing allowance of existing lock by 0', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + + await assertRevert(staking.increaseLockAllowance(user1, 0), STAKING_ERRORS.ERROR_AMOUNT_ZERO) + }) + + it('fails increasing allowance of existing lock if not owner or manager', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + + await assertRevert(staking.increaseLockAllowance(user1, 1, { from: user2 }), STAKING_ERRORS.ERROR_LOCK_DOES_NOT_EXIST) + }) + + it('decreases allowance of existing lock by the owner', async () => { + await approveAndStake({ staking, from: owner }) + await staking.allowManagerAndLock(DEFAULT_LOCK_AMOUNT, user1, DEFAULT_LOCK_AMOUNT.add(bn(1)), EMPTY_DATA) + + await staking.decreaseLockAllowance(owner, user1, 1, { from: owner }) + + const { _allowance } = await staking.getLock(owner, user1) + assertBn(_allowance, DEFAULT_LOCK_AMOUNT, "allowed amount should match") + }) + + it('decreases allowance of existing lock by manager', async () => { + await approveAndStake({ staking, from: owner }) + await staking.allowManagerAndLock(DEFAULT_LOCK_AMOUNT, user1, DEFAULT_LOCK_AMOUNT.add(bn(1)), EMPTY_DATA) + + await staking.decreaseLockAllowance(owner, user1, 1, { from: user1 }) + + const { _allowance } = await staking.getLock(owner, user1) + assertBn(_allowance, DEFAULT_LOCK_AMOUNT, "allowed amount should match") + }) + + it('fails decreasing allowance of existing lock by 0', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + + await assertRevert(staking.decreaseLockAllowance(owner, user1, 0), STAKING_ERRORS.ERROR_AMOUNT_ZERO) + }) + + it('fails decreasing allowance of existing lock to 0', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + + await staking.unlock(owner, user1, DEFAULT_LOCK_AMOUNT, { from: user1 }) + + await assertRevert(staking.decreaseLockAllowance(owner, user1, DEFAULT_LOCK_AMOUNT), STAKING_ERRORS.ERROR_ALLOWANCE_ZERO) + }) + + it('fails decreasing allowance to less than lock', async () => { + await approveAndStake({ staking, from: owner }) + await staking.allowManagerAndLock(DEFAULT_LOCK_AMOUNT, user1, DEFAULT_LOCK_AMOUNT.add(bn(1)), EMPTY_DATA) + + await assertRevert(staking.decreaseLockAllowance(owner, user1, 2), STAKING_ERRORS.ERROR_NOT_ENOUGH_ALLOWANCE) + }) + + it('fails decreasing allowance by 3rd party', async () => { + await approveAndStake({ staking, from: owner }) + await staking.allowManagerAndLock(DEFAULT_LOCK_AMOUNT, user1, DEFAULT_LOCK_AMOUNT.add(bn(1)), EMPTY_DATA) + + await assertRevert(staking.decreaseLockAllowance(owner, user1, 1, { from: user2 }), STAKING_ERRORS.ERROR_CANNOT_CHANGE_ALLOWANCE) + }) + + it('increases amount of existing lock', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + + await approveAndStake({ staking, from: owner }) + await staking.increaseLockAllowance(user1, DEFAULT_LOCK_AMOUNT) + await staking.lock(owner, user1, DEFAULT_LOCK_AMOUNT) + + const { _amount } = await staking.getLock(owner, user1) + assertBn(_amount, DEFAULT_LOCK_AMOUNT.mul(bn(2)), "locked amount should match") + }) + + it('fails increasing lock with 0 tokens', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + + await approveAndStake({ staking, from: owner }) + await assertRevert(staking.lock(owner, user1, 0), STAKING_ERRORS.ERROR_AMOUNT_ZERO) + }) + + it('fails increasing lock with more tokens than staked', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + + await approveAndStake({ staking, from: owner }) + await assertRevert(staking.lock(owner, user1, DEFAULT_STAKE_AMOUNT.mul(bn(2)).add(bn(1))), STAKING_ERRORS.ERROR_NOT_ENOUGH_BALANCE) + }) + + it('fails increasing lock if not owner or manager', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + + await approveAndStake({ staking, from: owner }) + await assertRevert(staking.lock(owner, user1, 1, { from: user2 }), STAKING_ERRORS.ERROR_SENDER_NOT_ALLOWED) + }) + + it('unlocks with only 1 lock, EOA manager', async () => { + await approveStakeAndLock({ staking, manager: user1, lockAmount: DEFAULT_LOCK_AMOUNT, from: owner }) + + // unlock + await staking.unlockAndRemoveManager(owner, user1, { from: user1 }) + + assertBn(await staking.unlockedBalanceOf(owner), DEFAULT_STAKE_AMOUNT, "Unlocked balance should match") + assertBn(await staking.lockedBalanceOf(owner), bn(0), "total locked doesn’t match") + }) + + it('unlocks with more than 1 lock, EOA manager', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + // lock again + await staking.allowManagerAndLock(DEFAULT_LOCK_AMOUNT, user2, DEFAULT_LOCK_AMOUNT, EMPTY_DATA) + + const previousTotalLocked = await staking.lockedBalanceOf(owner) + + // unlock + await staking.unlockAndRemoveManager(owner, user1, { from: user1 }) + + assertBn(await staking.unlockedBalanceOf(owner), DEFAULT_STAKE_AMOUNT.sub(DEFAULT_LOCK_AMOUNT), "Unlocked balance should match") + assertBn(await staking.lockedBalanceOf(owner), previousTotalLocked.sub(bn(DEFAULT_LOCK_AMOUNT)), "total locked doesn’t match") + }) + + it('unlocks completely, contract manager, called by owner', async () => { + await lockManager.setResult(true) + await approveStakeAndLock({ staking, manager: lockManager.address, from: owner }) + + // unlock + await staking.unlockAndRemoveManager(owner, lockManager.address, { from: owner }) + + assertBn(await staking.unlockedBalanceOf(owner), DEFAULT_STAKE_AMOUNT, "Unlocked balance should match") + assertBn(await staking.lockedBalanceOf(owner), bn(0), "total locked doesn’t match") + }) + + it('unlocks completely, contract manager, called by manager', async () => { + await lockManager.setResult(true) + await approveStakeAndLock({ staking, manager: lockManager.address, from: owner }) + + // unlock + await lockManager.unlockAndRemoveManager(staking.address, owner, lockManager.address) + + assertBn(await staking.unlockedBalanceOf(owner), DEFAULT_STAKE_AMOUNT, "Unlocked balance should match") + assertBn(await staking.lockedBalanceOf(owner), bn(0), "total locked doesn’t match") + }) + + it('unlocks completely, contract manager, called by manager, even if condition is not satisfied', async () => { + // not needed, is false by default + //await lockManager.setResult(false) + await approveStakeAndLock({ staking, manager: lockManager.address, from: owner }) + + // unlock + await lockManager.unlockAndRemoveManager(staking.address, owner, lockManager.address) + + assertBn(await staking.unlockedBalanceOf(owner), DEFAULT_STAKE_AMOUNT, "Unlocked balance should match") + assertBn(await staking.lockedBalanceOf(owner), bn(0), "total locked doesn’t match") + }) + + it('fails calling canUnlock, EOA manager', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + + // call canUnlock + await assertRevert(staking.canUnlock(owner, owner, user1, 0)) // no reason: it’s trying to call an EOA + }) + + it('can unlock if amount is zero', async () => { + await staking.allowManager(user1, DEFAULT_LOCK_AMOUNT, EMPTY_DATA, { from: owner }) + assert.isTrue(await staking.canUnlock(owner, owner, user1, 0)) + }) + + it('fails to unlock if it cannot unlock, EOA manager', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + + // tries to unlock + await assertRevert(staking.unlockAndRemoveManager(owner, user1)) // no reason: it’s trying to call an EOA + }) + + it('fails to unlock if can not unlock, contract manager, called by owner', async () => { + // not needed, is false by default + // await lockManager.setResult(false) + await approveStakeAndLock({ staking, manager: lockManager.address, from: owner }) + + // tries to unlock + await assertRevert(staking.unlockAndRemoveManager(owner, lockManager.address, { from: owner }), STAKING_ERRORS.ERROR_CANNOT_UNLOCK) + }) + + it('fails to unlock if, contract manager, called by 3rd party (even if condition is true)', async () => { + await lockManager.setResult(true) + await approveStakeAndLock({ staking, manager: lockManager.address, from: owner }) + + // tries to unlock + await assertRevert(staking.unlockAndRemoveManager(owner, lockManager.address, { from: user1 }), STAKING_ERRORS.ERROR_CANNOT_UNLOCK) + }) + + it('transfers (slash) and unlocks (everything else) in one transaction', async () => { + const totalLock = bigExp(120, 18) + const transferAmount = bigExp(40, 18) + + await approveStakeAndLock({ staking, manager: user1, allowanceAmount: totalLock, lockAmount: totalLock, stakeAmount: totalLock, from: owner }) + + // unlock and transfer + await staking.slashAndUnlock(owner, user2, totalLock.sub(transferAmount), transferAmount, { from: user1 }) + + assertBn(await staking.unlockedBalanceOf(owner), totalLock.sub(transferAmount), "Unlocked balance should match") + assertBn(await staking.lockedBalanceOf(owner), bn(0), "total locked doesn’t match") + // lock manager + assertBn(await staking.unlockedBalanceOf(user1), bn(0), "Unlocked balance should match") + assertBn(await staking.lockedBalanceOf(user1), bn(0), "total locked doesn’t match") + // recipient + assertBn(await staking.unlockedBalanceOf(user2), transferAmount, "Unlocked balance should match") + assertBn(await staking.lockedBalanceOf(user2), bn(0), "total locked doesn’t match") + }) + + it('transfers (slash) and unlocks in one transaction', async () => { + const totalLock = bigExp(120, 18) + const transferAmount = bigExp(40, 18) + const decreaseAmount = bigExp(60, 18) + + await approveStakeAndLock({ staking, manager: user1, allowanceAmount: totalLock, lockAmount: totalLock, stakeAmount: totalLock, from: owner }) + + // unlock and transfer + await staking.slashAndUnlock(owner, user2, decreaseAmount, transferAmount, { from: user1 }) + + assertBn(await staking.unlockedBalanceOf(owner), decreaseAmount, "Unlocked balance should match") + assertBn(await staking.lockedBalanceOf(owner), totalLock.sub(decreaseAmount).sub(transferAmount), "total locked doesn’t match") + // lock manager + assertBn(await staking.unlockedBalanceOf(user1), bn(0), "Unlocked balance should match") + assertBn(await staking.lockedBalanceOf(user1), bn(0), "total locked doesn’t match") + // recipient + assertBn(await staking.unlockedBalanceOf(user2), transferAmount, "Unlocked balance should match") + assertBn(await staking.lockedBalanceOf(user2), bn(0), "total locked doesn’t match") + }) + + it('fails to transfer (slash) and unlocks in one transaction if unlock amount is zero', async () => { + const totalLock = bigExp(120, 18) + const transferAmount = bigExp(40, 18) + const decreaseAmount = bigExp(0, 18) + + await approveStakeAndLock({ staking, manager: user1, allowanceAmount: totalLock, lockAmount: totalLock, stakeAmount: totalLock, from: owner }) + + // unlock and transfer + await assertRevert(staking.slashAndUnlock(owner, user2, decreaseAmount, transferAmount, { from: user1 }), STAKING_ERRORS.ERROR_AMOUNT_ZERO) + }) + + it('fails to transfer (slash) and unlock in one transaction if not owner nor manager', async () => { + const totalLock = bigExp(120, 18) + const transferAmount = bigExp(40, 18) + const decreaseAmount = bigExp(60, 18) + + await approveStakeAndLock({ staking, manager: user1, allowanceAmount: totalLock, lockAmount: totalLock, stakeAmount: totalLock, from: owner }) + + // unlock and transfer + await assertRevert(staking.slashAndUnlock(owner, user2, decreaseAmount, transferAmount, { from: user2 }), STAKING_ERRORS.ERROR_NOT_ENOUGH_LOCK) + }) + + it('change lock amount', async () => { + await approveStakeAndLock({ staking, manager: lockManager.address, from: owner }) + const { _amount: amount1 } = await staking.getLock(owner, lockManager.address) + assertBn(amount1, bn(DEFAULT_LOCK_AMOUNT), "Amount should match") + assertBn(await staking.unlockedBalanceOf(owner), DEFAULT_STAKE_AMOUNT.sub(DEFAULT_LOCK_AMOUNT), "Unlocked balance should match") + + // change amount + const unlockAmount = DEFAULT_LOCK_AMOUNT.div(bn(2)) + await lockManager.unlock(staking.address, owner, unlockAmount) + + const { _amount: amount2 } = await staking.getLock(owner, lockManager.address) + assertBn(amount2, unlockAmount, "Amount should match") + assertBn(await staking.unlockedBalanceOf(owner), DEFAULT_STAKE_AMOUNT.sub(unlockAmount), "Unlocked balance should match") + }) + + it('fails to change lock amount to zero', async () => { + await approveStakeAndLock({ staking, manager: lockManager.address, from: owner }) + + // try to change amount + await assertRevert(lockManager.unlock(staking.address, owner, 0), STAKING_ERRORS.ERROR_AMOUNT_ZERO) + }) + + it('fails to change lock amount to greater than before', async () => { + await approveStakeAndLock({ staking, manager: lockManager.address, from: owner }) + + // try to change amount + await assertRevert(lockManager.unlock(staking.address, owner, DEFAULT_LOCK_AMOUNT.add(bn(1))), STAKING_ERRORS.ERROR_NOT_ENOUGH_LOCK) + }) + + it('change lock manager', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + assert.equal(await staking.canUnlock(user1, owner, user1, 0), true, "User 1 can unlock") + assert.equal(await staking.canUnlock(user2, owner, user1, 0), false, "User 2 can not unlock") + await assertRevert(staking.canUnlock(user2, owner, user2, 0), STAKING_ERRORS.ERROR_LOCK_DOES_NOT_EXIST) // it doesn’t exist + + // change manager + await staking.setLockManager(owner, user2, { from: user1 }) + + await assertRevert(staking.canUnlock(user1, owner, user1, 0), STAKING_ERRORS.ERROR_LOCK_DOES_NOT_EXIST) // it doesn’t exist + assert.equal(await staking.canUnlock(user1, owner, user2, 0), false, "User 1 can not unlock") + assert.equal(await staking.canUnlock(user2, owner, user2, 0), true, "User 2 can unlock") + }) + + it('fails to change lock manager if it doesn’t exist', async () => { + await assertRevert(staking.setLockManager(owner, user2, { from: user1 }), STAKING_ERRORS.ERROR_LOCK_DOES_NOT_EXIST) + }) +}) diff --git a/tests-solidity/suites/staking/test/locking/locking_time.js b/tests-solidity/suites/staking/test/locking/locking_time.js new file mode 100644 index 0000000000..05d39fa76c --- /dev/null +++ b/tests-solidity/suites/staking/test/locking/locking_time.js @@ -0,0 +1,115 @@ +const { assertRevert } = require('@aragon/contract-helpers-test/assertThrow') +const { bn, assertBn } = require('@aragon/contract-helpers-test/numbers') + +const { deploy } = require('../helpers/deploy')(artifacts) +const { approveAndStake } = require('../helpers/helpers')(artifacts) +const { DEFAULT_STAKE_AMOUNT, DEFAULT_LOCK_AMOUNT, EMPTY_DATA } = require('../helpers/constants') +const { STAKING_ERRORS, TIME_LOCK_MANAGER_ERRORS } = require('../helpers/errors') + +const TimeLockManagerMock = artifacts.require('TimeLockManagerMock'); + +contract('Staking app, Time locking', ([owner]) => { + let token, staking, manager + + const TIME_UNIT_BLOCKS = 0 + const TIME_UNIT_SECONDS = 1 + + const DEFAULT_TIME = 1000 + const DEFAULT_BLOCKS = 10 + + const approveStakeAndLock = async(unit, start, end, lockAmount = DEFAULT_LOCK_AMOUNT, stakeAmount = DEFAULT_STAKE_AMOUNT) => { + await approveAndStake({ staking, amount: stakeAmount, from: owner }) + // allow manager + await staking.allowManager(manager.address, lockAmount, EMPTY_DATA) + // lock amount + await manager.lock(staking.address, owner, lockAmount, unit, start, end) + } + + beforeEach(async () => { + const deployment = await deploy(owner) + token = deployment.token + staking = deployment.staking + + manager = await TimeLockManagerMock.new() + }) + + it('locks using seconds', async () => { + const startTime = await manager.getTimestampExt() + const endTime = startTime.add(bn(DEFAULT_TIME)) + await approveStakeAndLock(TIME_UNIT_SECONDS, startTime, endTime) + + // check lock values + const { _amount, _allowance } = await staking.getLock(owner, manager.address) + assertBn(_amount, DEFAULT_LOCK_AMOUNT, "locked amount should match") + assertBn(_allowance, DEFAULT_LOCK_AMOUNT, "locked allowance should match") + + // check time values + const { unit, start, end } = await manager.getTimeInterval(owner) + assert.equal(unit.toString(), TIME_UNIT_SECONDS.toString(), "interval unit should match") + assert.equal(start.toString(), startTime.toString(), "interval start should match") + assert.equal(end.toString(), endTime.toString(), "interval end should match") + + // can not unlock + assert.equal(await staking.canUnlock(owner, owner, manager.address, 0), false, "Shouldn't be able to unlock") + assertBn(await staking.unlockedBalanceOf(owner), DEFAULT_STAKE_AMOUNT.sub(DEFAULT_LOCK_AMOUNT), "Unlocked balance should match") + + await manager.setTimestamp(endTime.add(bn(1))) + // can unlock + assert.equal(await staking.canUnlock(owner, owner, manager.address, 0), true, "Should be able to unlock") + assertBn(await staking.unlockedBalanceOf(owner), DEFAULT_STAKE_AMOUNT.sub(DEFAULT_LOCK_AMOUNT), "Unlocked balance should match") + }) + + it('locks using blocks', async () => { + const startBlock = (await manager.getBlockNumberExt()) + const endBlock = startBlock.add(bn(DEFAULT_BLOCKS)) + await approveStakeAndLock(TIME_UNIT_BLOCKS, startBlock, endBlock) + + // check lock values + const { _amount, _allowance } = await staking.getLock(owner, manager.address) + assertBn(_amount, DEFAULT_LOCK_AMOUNT, "locked amount should match") + assertBn(_allowance, DEFAULT_LOCK_AMOUNT, "locked allowance should match") + + // check time values + const { unit, start, end } = await manager.getTimeInterval(owner) + assert.equal(unit.toString(), TIME_UNIT_BLOCKS.toString(), "interval unit should match") + assert.equal(start.toString(), startBlock.toString(), "interval start should match") + assert.equal(end.toString(), endBlock.toString(), "interval end should match") + + // can not unlock + assert.equal(await staking.canUnlock(owner, owner, manager.address, 0), false, "Shouldn't be able to unlock") + assertBn(await staking.unlockedBalanceOf(owner), DEFAULT_STAKE_AMOUNT.sub(DEFAULT_LOCK_AMOUNT), "Unlocked balance should match") + + await manager.setBlockNumber(endBlock.add(bn(1))) + // can unlock + assert.equal(await staking.canUnlock(owner, owner, manager.address, 0), true, "Should be able to unlock") + }) + + it('fails to unlock if can not unlock', async () => { + const startTime = await manager.getTimestampExt() + const endTime = startTime.add(bn(DEFAULT_TIME)) + await approveStakeAndLock(TIME_UNIT_SECONDS, startTime, endTime) + + // tries to unlock + await assertRevert(staking.unlockAndRemoveManager(owner, manager.address)/*, STAKING_ERRORS.ERROR_CANNOT_UNLOCK*/) + }) + + it('fails trying to lock twice', async () => { + const startTime = await manager.getTimestampExt() + const endTime = startTime.add(bn(DEFAULT_TIME)) + await approveStakeAndLock(TIME_UNIT_SECONDS, startTime, endTime) + + await assertRevert(manager.lock(staking.address, owner, DEFAULT_LOCK_AMOUNT, TIME_UNIT_SECONDS, startTime, endTime)/*, TIME_LOCK_MANAGER_ERRORS.ERROR_ALREADY_LOCKED*/) + }) + + + it('fails trying to lock with wrong interval', async () => { + const startTime = await manager.getTimestampExt() + const endTime = startTime.add(bn(DEFAULT_TIME)) + + await ({ staking, amount: DEFAULT_STAKE_AMOUNT, from: owner }) + // allow manager + await staking.allowManager(manager.address, DEFAULT_STAKE_AMOUNT, EMPTY_DATA) + // times are reverted! + await assertRevert(manager.lock(staking.address, owner, DEFAULT_LOCK_AMOUNT, TIME_UNIT_SECONDS, endTime, startTime)/*, TIME_LOCK_MANAGER_ERRORS.ERROR_WRONG_INTERVAL*/) + }) +}) diff --git a/tests-solidity/suites/staking/test/staking.js b/tests-solidity/suites/staking/test/staking.js new file mode 100644 index 0000000000..d459eee7a3 --- /dev/null +++ b/tests-solidity/suites/staking/test/staking.js @@ -0,0 +1,176 @@ +const { assertRevert } = require('@aragon/contract-helpers-test/assertThrow') +const { bn, assertBn, MAX_UINT64 } = require('@aragon/contract-helpers-test/numbers') + +const { deploy } = require('./helpers/deploy')(artifacts) +const { approveAndStake } = require('./helpers/helpers')(artifacts) +const { DEFAULT_STAKE_AMOUNT, EMPTY_DATA } = require('./helpers/constants') +const { STAKING_ERRORS } = require('./helpers/errors') + +const StakingMock = artifacts.require('StakingMock') +const StandardTokenMock = artifacts.require('StandardTokenMock'); +const BadTokenMock = artifacts.require('BadTokenMock') + +const getTokenBalance = async (token, account) => await token.balanceOf(account) + +contract('Staking app', ([owner, other]) => { + let staking, token, stakingAddress, tokenAddress + + beforeEach(async () => { + const initialAmount = DEFAULT_STAKE_AMOUNT.mul(bn(1000)) + const tokenContract = await StandardTokenMock.new(owner, initialAmount) + token = tokenContract + tokenAddress = tokenContract.address + await token.mint(other, DEFAULT_STAKE_AMOUNT) + const stakingContract = await StakingMock.new(tokenAddress) + staking = stakingContract + stakingAddress = stakingContract.address + }) + + it('has correct initial state', async () => { + assert.equal(await staking.token(), tokenAddress, "Token is wrong") + assert.equal((await staking.totalStaked()).valueOf(), 0, "Initial total staked amount should be zero") + assert.equal(await staking.supportsHistory(), true, "history support should match") + }) + + it('fails deploying if token is not a contract', async() => { + await assertRevert(StakingMock.new(owner)/*, STAKING_ERRORS.ERROR_TOKEN_NOT_CONTRACT*/) + }) + + it('stakes', async () => { + const initialOwnerBalance = await getTokenBalance(token, owner) + const initialStakingBalance = await getTokenBalance(token, stakingAddress) + + await approveAndStake({ staking, from: owner }) + + const finalOwnerBalance = await getTokenBalance(token, owner) + const finalStakingBalance = await getTokenBalance(token, stakingAddress) + assertBn(finalOwnerBalance, initialOwnerBalance.sub(bn(DEFAULT_STAKE_AMOUNT)), "owner balance should match") + assertBn(finalStakingBalance, initialStakingBalance.add(bn(DEFAULT_STAKE_AMOUNT)), "Staking app balance should match") + assertBn(await staking.totalStakedFor(owner), bn(DEFAULT_STAKE_AMOUNT), "staked value should match") + // total stake + assertBn(await staking.totalStaked(), bn(DEFAULT_STAKE_AMOUNT), "Total stake should match") + }) + + it('fails staking 0 amount', async () => { + await token.approve(stakingAddress, 1) + await assertRevert(staking.stake(0, EMPTY_DATA)/*, STAKING_ERRORS.ERROR_AMOUNT_ZERO*/) + }) + + it('fails staking more than balance', async () => { + const balance = await getTokenBalance(token, owner) + const amount = balance.add(bn(1)) + await token.approve(stakingAddress, amount) + await assertRevert(staking.stake(amount, EMPTY_DATA)/*, STAKING_ERRORS.ERROR_TOKEN_DEPOSIT*/) + }) + + it('stakes for', async () => { + const initialOwnerBalance = await getTokenBalance(token, owner) + const initialOtherBalance = await getTokenBalance(token, other) + const initialStakingBalance = await getTokenBalance(token, stakingAddress) + + // allow Staking app to move owner tokens + await token.approve(stakingAddress, DEFAULT_STAKE_AMOUNT) + // stake tokens + await staking.stakeFor(other, DEFAULT_STAKE_AMOUNT, EMPTY_DATA) + + const finalOwnerBalance = await getTokenBalance(token, owner) + const finalOtherBalance = await getTokenBalance(token, other) + const finalStakingBalance = await getTokenBalance(token, stakingAddress) + assertBn(finalOwnerBalance, initialOwnerBalance.sub(bn(DEFAULT_STAKE_AMOUNT)), "owner balance should match") + assertBn(finalOtherBalance, initialOtherBalance, "other balance should match") + assertBn(finalStakingBalance, initialStakingBalance.add(bn(DEFAULT_STAKE_AMOUNT)), "Staking app balance should match") + assertBn(await staking.totalStakedFor(owner), bn(0), "staked value for owner should match") + assertBn(await staking.totalStakedFor(other), bn(DEFAULT_STAKE_AMOUNT), "staked value for other should match") + }) + + it('unstakes', async () => { + const initialOwnerBalance = await getTokenBalance(token, owner) + const initialStakingBalance = await getTokenBalance(token, stakingAddress) + + await approveAndStake({ staking, from: owner }) + + // unstake half of them + await staking.unstake(DEFAULT_STAKE_AMOUNT.div(bn(2)), EMPTY_DATA) + + const finalOwnerBalance = await getTokenBalance(token, owner) + const finalStakingBalance = await getTokenBalance(token, stakingAddress) + assertBn(finalOwnerBalance, initialOwnerBalance.sub(bn(DEFAULT_STAKE_AMOUNT.div(bn(2)))), "owner balance should match") + assertBn(finalStakingBalance, initialStakingBalance.add(bn(DEFAULT_STAKE_AMOUNT.div(bn(2)))), "Staking app balance should match") + assertBn(await staking.totalStakedFor(owner), bn(DEFAULT_STAKE_AMOUNT.div(bn(2))), "staked value should match") + }) + + it('fails unstaking 0 amount', async () => { + await approveAndStake({ staking, from: owner }) + await assertRevert(staking.unstake(0, EMPTY_DATA)/*, STAKING_ERRORS.ERROR_AMOUNT_ZERO*/) + }) + + it('fails unstaking more than staked', async () => { + await approveAndStake({ staking, from: owner }) + await assertRevert(staking.unstake(DEFAULT_STAKE_AMOUNT.add(bn(1)), EMPTY_DATA)/*, STAKING_ERRORS.ERROR_NOT_ENOUGH_BALANCE*/) + }) + + context('History', async () => { + it('supports history', async () => { + assert.equal(await staking.supportsHistory(), true, "It should support History") + }) + + it('has correct "last staked for"', async () => { + const blockNumber = await staking.getBlockNumberPublic() + const lastStaked = blockNumber.add(bn(5)) + await staking.setBlockNumber(lastStaked) + await approveAndStake({ staking, from: owner }) + assertBn(await staking.lastStakedFor(owner), lastStaked, "Last staked for should match") + }) + + it('has correct "total staked for at"', async () => { + const beforeBlockNumber = await staking.getBlockNumberPublic() + const lastStaked = beforeBlockNumber.add(bn(5)) + await staking.setBlockNumber(lastStaked) + await approveAndStake({ staking, from: owner }) + assertBn(await staking.totalStakedForAt(owner, beforeBlockNumber), bn(0), "Staked for at before staking should match") + assertBn(await staking.totalStakedForAt(owner, lastStaked), bn(DEFAULT_STAKE_AMOUNT), "Staked for after staking should match") + }) + + it('has correct "total staked at"', async () => { + const beforeBlockNumber = await staking.getBlockNumberPublic() + const lastStaked = beforeBlockNumber.add(bn(5)) + await staking.setBlockNumber(lastStaked) + await approveAndStake({ staking, from: owner }) + await approveAndStake({ staking, from: other }) + assertBn(await staking.totalStakedAt(beforeBlockNumber), bn(0), "Staked for at before should match") + assertBn(await staking.totalStakedAt(lastStaked), bn(DEFAULT_STAKE_AMOUNT.mul(bn(2))), "Staked for at after staking should match") + }) + + it('fails to call totalStakedForAt with block number greater than max uint64', async () => { + await assertRevert(staking.totalStakedForAt(owner, MAX_UINT64.add(bn(1)))/*, STAKING_ERRORS.ERROR_BLOCKNUMBER_TOO_BIG*/) + }) + + it('fails to call totalStakedAt with block number greater than max uint64', async () => { + await assertRevert(staking.totalStakedAt(MAX_UINT64.add(bn(1)))/*, STAKING_ERRORS.ERROR_BLOCKNUMBER_TOO_BIG*/) + }) + }) + + context('Bad Token', async () => { + let badStaking, badStakingAddress, badToken, badTokenAddress + beforeEach(async () => { + const initialAmount = DEFAULT_STAKE_AMOUNT.mul(bn(1000)) + const tokenContract = await BadTokenMock.new(owner, initialAmount) + badToken = tokenContract + badTokenAddress = tokenContract.address + await badToken.mint(other, DEFAULT_STAKE_AMOUNT) + const stakingContract = await StakingMock.new(badTokenAddress) + badStaking = stakingContract + badStakingAddress = stakingContract.address + }) + + it('fails unstaking because of bad token', async () => { + // allow Staking app to move owner tokens + await badToken.approve(badStakingAddress, DEFAULT_STAKE_AMOUNT, { from: owner }) + // stake tokens + await badStaking.stake(DEFAULT_STAKE_AMOUNT, EMPTY_DATA, { from: owner }) + + // unstake half of them, fails on token transfer + await assertRevert(badStaking.unstake(DEFAULT_STAKE_AMOUNT.div(bn(2)), EMPTY_DATA)/*, STAKING_ERRORS.ERROR_TOKEN_TRANSFER*/) + }) + }) +}) diff --git a/tests-solidity/suites/staking/test/staking_factory.js b/tests-solidity/suites/staking/test/staking_factory.js new file mode 100644 index 0000000000..b7a48ea1bd --- /dev/null +++ b/tests-solidity/suites/staking/test/staking_factory.js @@ -0,0 +1,116 @@ +const { assertRevert } = require('@aragon/contract-helpers-test/assertThrow') + +const { ZERO_ADDRESS } = require('./helpers/constants') +const { STAKING_ERRORS } = require('./helpers/errors') + +const Staking = artifacts.require('Staking') +const StakingFactory = artifacts.require('StakingFactory') +const StandardTokenMock = artifacts.require('StandardTokenMock') + +contract('StakingFactory', ([_, owner, someone]) => { + let token, factory, staking + + const getInstance = receipt => receipt.logs.find(log => log.event === 'NewStaking').args.instance + + beforeEach('deploy sample token and staking factory', async () => { + token = await StandardTokenMock.new(owner, 100000, { from: owner }) + factory = await StakingFactory.new() + }) + + describe('getInstance', () => { + context('when the given token was not registered before', () => { + it('returns the zero address', async () => { + const instance = await factory.getInstance(token.address) + + assert.equal(instance, ZERO_ADDRESS, 'instance address does not match') + }) + }) + + context('when the given token was already registered', () => { + let instance + + beforeEach('create staking instance', async () => { + instance = getInstance(await factory.getOrCreateInstance(token.address)) + }) + + it('returns the corresponding staking instance address', async () => { + const foundInstance = await factory.getInstance(token.address) + + assert.equal(instance, foundInstance, 'instance address does not match') + }) + }) + }) + + describe('existsInstance', () => { + context('when the given token was not registered before', () => { + it('returns false', async () => { + const exists = await factory.existsInstance(token.address) + assert(!exists, 'staking instance does exist') + }) + }) + + context('when the given token was already registered', () => { + beforeEach('create staking instance', async () => { + await factory.getOrCreateInstance(token.address) + }) + + it('returns true', async () => { + const exists = await factory.existsInstance(token.address) + assert(exists, 'staking instance does not exist') + }) + }) + }) + + describe('getOrCreateInstance', () => { + context('when the given token was not registered before', () => { + context('when the given token is a contract', () => { + it('emits a NewStaking event', async () => { + const receipt = await factory.getOrCreateInstance(token.address) + + const events = receipt.logs.filter(l => l.event === 'NewStaking') + assert.equal(events.length, 1, 'number of NewStaking events does not match') + assert.equal(events[0].args.token, token.address, 'token address does not match') + assert.notEqual(events[0].args.instance, ZERO_ADDRESS, 'instance address does not match') + }) + + it('creates a new staking instance', async () => { + const instance = getInstance(await factory.getOrCreateInstance(token.address)) + + staking = await Staking.at(instance) + assert.equal(await staking.token(), token.address, 'token address does not match') + }) + }) + + context('when the given token is the zero address', () => { + const tokenAddress = ZERO_ADDRESS + + it('reverts', async () => { + await assertRevert(factory.getOrCreateInstance(tokenAddress)/*, STAKING_ERRORS.ERROR_TOKEN_NOT_CONTRACT*/) + }) + }) + + context('when the given token is not a contract', () => { + const tokenAddress = someone + + it('reverts', async () => { + await assertRevert(factory.getOrCreateInstance(tokenAddress)/*, STAKING_ERRORS.ERROR_TOKEN_NOT_CONTRACT*/) + }) + }) + }) + + context('when the given token was already registered', () => { + let instance + + beforeEach('create staking instance', async () => { + instance = getInstance(await factory.getOrCreateInstance(token.address)) + }) + + it('does not create a new staking instance', async () => { + const receipt = await factory.getOrCreateInstance(token.address) + + const events = receipt.logs.filter(l => l.event === 'NewStaking') + assert.equal(events.length, 0, 'number of NewStaking events does not match') + }) + }) + }) +}) diff --git a/tests-solidity/suites/staking/test/staking_proxy.js b/tests-solidity/suites/staking/test/staking_proxy.js new file mode 100644 index 0000000000..3e9b1abd57 --- /dev/null +++ b/tests-solidity/suites/staking/test/staking_proxy.js @@ -0,0 +1,47 @@ +const Staking = artifacts.require('Staking') +const StakingProxy = artifacts.require('StakingProxy') +const StandardTokenMock = artifacts.require('StandardTokenMock') + +contract('StakingProxy', ([_, owner]) => { + let proxy, token, implementation + + const FORWARDING_TYPE = 1 + + beforeEach('deploy sample token and staking implementation', async () => { + token = await StandardTokenMock.new(owner, 100000, { from: owner }) + implementation = await Staking.new() + proxy = await StakingProxy.new(implementation.address, token.address) + }) + + describe('initialize', async () => { + it('initializes the given implementation', async () => { + const staking = await Staking.at(proxy.address) + assert(await staking.hasInitialized(), 'should have been initialized') + }) + }) + + describe('implementation', async () => { + it('uses an unstructured storage slot for the implementation address', async () => { + const implementationAddress = await web3.eth.getStorageAt(proxy.address, web3.utils.sha3('aragon.network.staking')) + assert.equal(implementationAddress.toLowerCase(), implementation.address.toLowerCase(), 'implementation address does not match') + }) + + it('uses the given implementation', async () => { + const implementationAddress = await proxy.implementation() + assert.equal(implementationAddress, implementation.address, 'implementation address does not match') + }) + }) + + describe('proxyType', () => { + it('is a forwarding type', async () => { + assert.equal(await proxy.proxyType(), FORWARDING_TYPE, 'proxy type does not match') + }) + }) + + describe('fallback', () => { + it('forward calls to the implementation set', async () => { + const staking = await Staking.at(proxy.address) + assert.equal(await staking.token(), token.address, 'token address does not match') + }) + }) +}) diff --git a/tests-solidity/suites/staking/test/transfers.js b/tests-solidity/suites/staking/test/transfers.js new file mode 100644 index 0000000000..7093ea465e --- /dev/null +++ b/tests-solidity/suites/staking/test/transfers.js @@ -0,0 +1,143 @@ +const { assertRevert } = require('@aragon/contract-helpers-test/assertThrow') +const { assertBn, bn, MAX_UINT64 } = require('@aragon/contract-helpers-test/numbers') + +const { deploy } = require('./helpers/deploy')(artifacts) +const { approveAndStake, approveStakeAndLock } = require('./helpers/helpers')(artifacts) +const { DEFAULT_STAKE_AMOUNT, DEFAULT_LOCK_AMOUNT, EMPTY_DATA, ZERO_ADDRESS } = require('./helpers/constants') +const { STAKING_ERRORS } = require('./helpers/errors') + +contract('Staking app, Transferring', ([owner, user1, user2]) => { + let staking, token, lockManager + + beforeEach(async () => { + const deployment = await deploy(owner) + token = deployment.token + staking = deployment.staking + lockManager = deployment.lockManager + }) + + context('Transfers', async () => { + + context('From stake', async () => { + + const transfersFromStake = (transferType) => { + it('transfers', async () => { + //const initialTotalStake = await staking.totalStaked() + const transferAmount = DEFAULT_STAKE_AMOUNT.div(bn(2)) + await approveAndStake({ staking, from: owner }) + await staking[transferType](user1, transferAmount) + + assertBn(await staking.unlockedBalanceOf(owner), DEFAULT_STAKE_AMOUNT.sub(transferAmount), "Owner balance should match") + + const userStakedBalance = transferType == 'transfer' ? transferAmount : bn(0) + assertBn(await staking.unlockedBalanceOf(user1), userStakedBalance, "User 1 unlocked balance should match") + + const userExternalBalance = transferType == 'transfer' ? bn(0) : transferAmount + assertBn(await token.balanceOf(user1), userExternalBalance, "User 1 external balance should match") + + // total stake + const totalStaked = transferType == 'transfer' ? DEFAULT_STAKE_AMOUNT : DEFAULT_STAKE_AMOUNT.sub(transferAmount) + assertBn(await staking.totalStaked(), totalStaked, "Total stake should match") + }) + + it('fails transferring zero tokens', async () => { + await approveAndStake({ staking, from: owner }) + await assertRevert(staking[transferType](user1, 0)/*, STAKING_ERRORS.ERROR_AMOUNT_ZERO*/) + }) + + it('fails transferring more than staked balance', async () => { + await approveAndStake({ staking, amount: DEFAULT_STAKE_AMOUNT, from: owner }) + await assertRevert(staking[transferType](user1, DEFAULT_STAKE_AMOUNT.add(bn(1)))/*, STAKING_ERRORS.ERROR_NOT_ENOUGH_BALANCE*/) + }) + + it('fails transferring more than unlocked balance', async () => { + await approveStakeAndLock({ staking, manager: lockManager.address, from: owner }) + await assertRevert(staking[transferType](user1, DEFAULT_STAKE_AMOUNT)/*, STAKING_ERRORS.ERROR_NOT_ENOUGH_BALANCE*/) + }) + } + + context('within Staking app', () => { + transfersFromStake('transfer') + }) + + context('to external balance (unstaked)', () => { + transfersFromStake('transferAndUnstake') + }) + }) + + const transfersFromLock = (transferType) => { + it('transfers', async () => { + await approveStakeAndLock({ staking, manager: lockManager.address, from: owner }) + const transferAmount = DEFAULT_LOCK_AMOUNT.div(bn(2)) + await lockManager[transferType](staking.address, owner, user1, transferAmount) + + assertBn(await staking.unlockedBalanceOf(owner), DEFAULT_STAKE_AMOUNT.sub(DEFAULT_LOCK_AMOUNT), "Owner balance should match") + const userUnlockedBalance = transferType == 'slash' ? transferAmount : bn(0) + assertBn(await staking.unlockedBalanceOf(user1), userUnlockedBalance, "User 1 unlocked balance should match") + + const userExternalBalance = transferType == 'slash' ? bn(0) : transferAmount + assertBn(await token.balanceOf(user1), userExternalBalance, "User 1 external balance should match") + + // total stake + const totalStaked = transferType == 'slash' ? DEFAULT_STAKE_AMOUNT : DEFAULT_STAKE_AMOUNT.sub(transferAmount) + assertBn(await staking.totalStaked(), totalStaked, "Total stake should match") + + // check lock values + const { _amount: amount, _data: data } = await staking.getLock(owner, lockManager.address) + assertBn(amount, DEFAULT_LOCK_AMOUNT.sub(transferAmount), "locked amount should match") + }) + + it('transfers the whole lock amount', async () => { + await approveStakeAndLock({ staking, manager: lockManager.address, from: owner }) + await lockManager[transferType](staking.address, owner, user1, DEFAULT_LOCK_AMOUNT) + + assertBn(await staking.unlockedBalanceOf(owner), DEFAULT_STAKE_AMOUNT.sub(DEFAULT_LOCK_AMOUNT), "Owner balance should match") + const userUnlockedBalance = transferType == 'slash' ? DEFAULT_LOCK_AMOUNT : bn(0) + assertBn(await staking.unlockedBalanceOf(user1), userUnlockedBalance, "User 1 unlocked balance should match") + + const userExternalBalance = transferType == 'slash' ? bn(0) : DEFAULT_LOCK_AMOUNT + assertBn(await token.balanceOf(user1), userExternalBalance, "User 1 external balance should match") + + // total stake + const totalStaked = transferType == 'slash' ? DEFAULT_STAKE_AMOUNT : DEFAULT_STAKE_AMOUNT.sub(DEFAULT_LOCK_AMOUNT) + assertBn(await staking.totalStaked(), totalStaked, "Total stake should match") + + // check lock values + const { _amount: amount, _data: data } = await staking.getLock(owner, lockManager.address) + assertBn(amount, bn(0), "locked amount should match") + }) + + it('fails transferring zero tokens', async () => { + await approveStakeAndLock({ staking, manager: lockManager.address, from: owner }) + await assertRevert(lockManager[transferType](staking.address, owner, user1, 0)/*, STAKING_ERRORS.ERROR_AMOUNT_ZERO*/) + }) + + it('fails transferring more than locked balance', async () => { + await approveStakeAndLock({ staking, manager: lockManager.address, from: owner }) + await assertRevert(lockManager[transferType](staking.address, owner, user1, DEFAULT_LOCK_AMOUNT.add(bn(1)))/*, STAKING_ERRORS.ERROR_NOT_ENOUGH_LOCK*/) + }) + + it('fails if sender is not manager', async () => { + await approveStakeAndLock({ staking, manager: user1, from: owner }) + await assertRevert(lockManager[transferType](staking.address, owner, user1, DEFAULT_LOCK_AMOUNT)/*, STAKING_ERRORS.ERROR_NOT_ENOUGH_LOCK*/) + }) + + it('fails transferring from unlocked lock', async () => { + await approveStakeAndLock({ staking, manager: lockManager.address, from: owner }) + // unlock + await lockManager.unlockAndRemoveManager(staking.address, owner) + await assertRevert(lockManager[transferType](staking.address, owner, user2, DEFAULT_LOCK_AMOUNT, { from: user1 })/*, STAKING_ERRORS.ERROR_NOT_ENOUGH_LOCK*/) + }) + } + + context('From Lock', async () => { + context('within Staking app', () => { + transfersFromLock('slash') + }) + + context('to external balance (unstaked)', () => { + transfersFromLock('slashAndUnstake') + }) + }) + }) +}) diff --git a/tests-solidity/suites/staking/truffle-config.js b/tests-solidity/suites/staking/truffle-config.js new file mode 100644 index 0000000000..a5642089aa --- /dev/null +++ b/tests-solidity/suites/staking/truffle-config.js @@ -0,0 +1,23 @@ +module.exports = { + networks: { + // Development network is just left as truffle's default settings + ethermint: { + host: "127.0.0.1", // Localhost (default: none) + port: 8545, // Standard Ethereum port (default: none) + network_id: "*", // Any network (default: none) + gas: 7000000, // Gas sent with each transaction + gasPrice: 1000000000, // 1 gwei (in wei) + }, + }, + compilers: { + solc: { + version: "0.5.17", // A version or constraint - Ex. "^0.5.0". + settings: { + optimizer: { + enabled: true, + runs: 10000, + }, + }, + }, + }, +} diff --git a/tests-solidity/yarn.lock b/tests-solidity/yarn.lock new file mode 100644 index 0000000000..f4a7841524 --- /dev/null +++ b/tests-solidity/yarn.lock @@ -0,0 +1,9204 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@aragon/contract-helpers-test@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@aragon/contract-helpers-test/-/contract-helpers-test-0.0.3.tgz#92da62e348825e89b0c941fcf6d5db6fe643a64b" + integrity sha512-WP5VurPSXxNYmsgoefggyBhp+jDsK14G8UKpWGZvG606SHzM13mI2ZcYpCX8R/E6xeseIEVszbQN6BLqEkR+Ng== + dependencies: + ethereumjs-abi "^0.6.4" + web3-eth-abi "1.2.5" + web3-utils "1.2.5" + +"@aragon/contract-helpers-test@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@aragon/contract-helpers-test/-/contract-helpers-test-0.1.0.tgz#5c5f09739a0b33ab66843bc4849cb2b997d88af0" + integrity sha512-xP2CqqP0Jw/6Jdo1mRg4OxL+3gmsCYV3EJRy7xN8xUrhQIqkOyRptC44X951O7Cr+VeqXJq22rpZSr01TJZhNg== + dependencies: + web3-eth-abi "1.2.5" + web3-utils "1.2.5" + +"@ethersproject/abi@5.0.0-beta.153": + version "5.0.0-beta.153" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.0-beta.153.tgz#43a37172b33794e4562999f6e2d555b7599a8eee" + integrity sha512-aXweZ1Z7vMNzJdLpR1CZUAIgnwjrZeUSvN9syCwlBaEBUFJmFY+HHnfuTI5vIhVs/mRkfJVrbEyl51JZQqyjAg== + dependencies: + "@ethersproject/address" ">=5.0.0-beta.128" + "@ethersproject/bignumber" ">=5.0.0-beta.130" + "@ethersproject/bytes" ">=5.0.0-beta.129" + "@ethersproject/constants" ">=5.0.0-beta.128" + "@ethersproject/hash" ">=5.0.0-beta.128" + "@ethersproject/keccak256" ">=5.0.0-beta.127" + "@ethersproject/logger" ">=5.0.0-beta.129" + "@ethersproject/properties" ">=5.0.0-beta.131" + "@ethersproject/strings" ">=5.0.0-beta.130" + +"@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.0.3.tgz#86489f836d1656135fa6cae56d9fd1ab5b2c95af" + integrity sha512-LMmLxL1wTNtvwgm/eegcaxtG/W7vHXKzHGUkK9KZEI9W+SfHrpT7cGX+hBcatcUXPANjS3TmOaQ+mq5JU5sGTw== + dependencies: + "@ethersproject/bignumber" "^5.0.6" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/rlp" "^5.0.3" + bn.js "^4.4.0" + +"@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.0.6": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.6.tgz#1b5494a640c64096538e622b6ba8a5b8439ebde4" + integrity sha512-fLilYOSH3DJXBrimx7PwrJdY/zAI5MGp229Mvhtcur76Lgt4qNWu9HTiwMGHP01Tkm3YP5gweF83GrQrA2tYUA== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + bn.js "^4.4.0" + +"@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.0.4.tgz#328d9d929a3e970964ecf5d62e12568a187189f1" + integrity sha512-9R6A6l9JN8x1U4s1dJCR+9h3MZTT3xQofr/Xx8wbDvj6NnY4CbBB0o8ZgHXvR74yV90pY2EzCekpkMBJnRzkSw== + dependencies: + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.0.3.tgz#7ccb8e2e9f14fbcc2d52d0e1402a83a5613a2f65" + integrity sha512-iN7KBrA0zNFybDyrkcAPOcyU3CHXYFMd+KM2Jr07Kjg+DVB5wPpEXsOdd/K1KWFsFtGfNdPZ7QP8siLtCePXrQ== + dependencies: + "@ethersproject/bignumber" "^5.0.6" + +"@ethersproject/hash@>=5.0.0-beta.128": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.0.3.tgz#41f17fd7972838831620338dad932bfe3d684209" + integrity sha512-KSnJyL0G9lxbOK0UPrUcaYTc/RidrX8c+kn7xnEpTmSGxqlndw4BzvQcRgYt31bOIwuFtwlWvOo6AN2tJgdQtA== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/strings" "^5.0.3" + +"@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.0.3.tgz#f094a8fca3bb913c044593c4f382be424292e588" + integrity sha512-VhW3mgZMBZlETV6AyOmjNeNG+Pg68igiKkPpat8/FZl0CKnfgQ+KZQZ/ee1vT+X0IUM8/djqnei6btmtbA27Ug== + dependencies: + "@ethersproject/bytes" "^5.0.4" + js-sha3 "0.5.7" + +"@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.5": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.0.5.tgz#e3ba3d0bcf9f5be4da5f043b1e328eb98b80002f" + integrity sha512-gJj72WGzQhUtCk6kfvI8elTaPOQyMvrMghp/nbz0ivTo39fZ7IjypFh/ySDeUSdBNplAwhzWKKejQhdpyefg/w== + +"@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.0.3.tgz#991aef39a5f87d4645cee76cec4df868bfb08be6" + integrity sha512-wLCSrbywkQgTO6tIF9ZdKsH9AIxPEqAJF/z5xcPkz1DK4mMAZgAXRNw1MrKYhyb+7CqNHbj3vxenNKFavGY/IA== + dependencies: + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/rlp@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.0.3.tgz#841a5edfdf725f92155fe74424f5510c9043c13a" + integrity sha512-Hz4yyA/ilGafASAqtTlLWkA/YqwhQmhbDAq2LSIp1AJNx+wtbKWFAKSckpeZ+WG/xZmT+fw5OFKK7a5IZ4DR5g== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/signing-key@^5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.0.4.tgz#a5334ce8a52d4e9736dc8fb6ecc384704ecf8783" + integrity sha512-I6pJoga1IvhtjYK5yXzCjs4ZpxrVbt9ZRAlpEw0SW9UuV020YfJH5EIVEGR2evdRceS3nAQIggqbsXSkP8Y1Dg== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + elliptic "6.5.3" + +"@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.3.tgz#756cc4b93203a091966d40824b0b28048e2d5d9b" + integrity sha512-8kEx3+Z6cMn581yh093qnaSa8H7XzmLn6g8YFDHUpzXM7+bvXvnL2ciHrJ+EbvaMQZpej6nNtl0nm7XF4PmQHA== + dependencies: + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + +"@ethersproject/transactions@^5.0.0-beta.135": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.0.3.tgz#7cd82fa6d63043fb5cd561a8ed72df046a968430" + integrity sha512-cqsAAFUQV6iWqfgLL7KCPNfd3pXJPDdYtE6QuBEAIpc7cgbJ7TIDCF/dN+1otfERHJIbjGSNrhh4axKRnSFswg== + dependencies: + "@ethersproject/address" "^5.0.3" + "@ethersproject/bignumber" "^5.0.6" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.3" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/rlp" "^5.0.3" + "@ethersproject/signing-key" "^5.0.4" + +"@nomiclabs/buidler-ganache@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@nomiclabs/buidler-ganache/-/buidler-ganache-1.3.3.tgz#220beb381b0df45bf1860396df4a639267711066" + integrity sha512-5dReZ3qqkA8Y46qeuw4gM3hzyzTB8DPvc5xTz7TKSkCHE5iwe4mMBLjyJ0ZwVaxmQvr4zxkNC9LsQ8JVC/j6IA== + dependencies: + debug "^4.1.1" + ganache-core "^2.7.0" + ts-essentials "^2.0.7" + ts-interface-checker "^0.1.9" + +"@nomiclabs/buidler-truffle5@^1.3.4": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@nomiclabs/buidler-truffle5/-/buidler-truffle5-1.3.4.tgz#aee775eff6a99e4102d993654a5af87ca708feab" + integrity sha512-aPbkdbNUF4R/hbb2B8BULlsQ5AUB5l4baIHFmLQ/fdRmFrkWBx+53tjpf+XOv8g8RZTJstpkxFSBRWD/U1EahA== + dependencies: + "@nomiclabs/truffle-contract" "^4.1.2" + "@types/chai" "^4.2.0" + chai "^4.2.0" + ethereumjs-util "^6.1.0" + fs-extra "^7.0.1" + +"@nomiclabs/buidler-web3@^1.3.4": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@nomiclabs/buidler-web3/-/buidler-web3-1.3.4.tgz#46bef1aa5b11aecb43e08065863d8b35226c1b29" + integrity sha512-fVJczSfZBp1xDJA2Bdh49P6mhaAO8vGY7kbi6uoMLO8J288O6s/Ldw36kb7iRebifNbKP69MJsUu2nXLaklqdg== + dependencies: + "@types/bignumber.js" "^5.0.0" + +"@nomiclabs/buidler@^1.4.3": + version "1.4.5" + resolved "https://registry.yarnpkg.com/@nomiclabs/buidler/-/buidler-1.4.5.tgz#9e332918bf0c19416494e3c4580fd3ca350ed70b" + integrity sha512-jaaVvG7OsrObhEBnLtQ/8L3J92+Jgioaw2+296dDO4Uc+MO/kprORbnpbE/WKvPL1geKQ4uj6VdCpM7qNCwL3g== + dependencies: + "@nomiclabs/ethereumjs-vm" "^4.1.1" + "@sentry/node" "^5.18.1" + "@solidity-parser/parser" "^0.5.2" + "@types/bn.js" "^4.11.5" + "@types/lru-cache" "^5.1.0" + abort-controller "^3.0.0" + ansi-escapes "^4.3.0" + chalk "^2.4.2" + chokidar "^3.4.0" + ci-info "^2.0.0" + debug "^4.1.1" + deepmerge "^2.1.0" + download "^7.1.0" + enquirer "^2.3.0" + eth-sig-util "^2.5.2" + ethereum-cryptography "^0.1.2" + ethereumjs-abi "^0.6.8" + ethereumjs-account "^3.0.0" + ethereumjs-block "^2.2.0" + ethereumjs-common "^1.3.2" + ethereumjs-tx "^2.1.1" + ethereumjs-util "^6.1.0" + find-up "^2.1.0" + fp-ts "1.19.3" + fs-extra "^7.0.1" + glob "^7.1.3" + io-ts "1.10.4" + is-installed-globally "^0.2.0" + lodash "^4.17.11" + merkle-patricia-tree "^3.0.0" + mocha "^7.1.2" + node-fetch "^2.6.0" + qs "^6.7.0" + raw-body "^2.4.1" + semver "^6.3.0" + slash "^3.0.0" + solc "0.6.8" + source-map-support "^0.5.13" + ts-essentials "^2.0.7" + tsort "0.0.1" + uuid "^3.3.2" + ws "^7.2.1" + +"@nomiclabs/ethereumjs-vm@^4.1.1": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@nomiclabs/ethereumjs-vm/-/ethereumjs-vm-4.2.0.tgz#a853bdb4fb032529f810f32bb767551d19d7ce57" + integrity sha512-+XwqoO941bILTO4KDLIUJ37U42ySxw6it7jyoi0tKv0/VUcOrWKF1TCQWMv6dBDRlxpPQd273n9o5SVlYYLRWQ== + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + core-js-pure "^3.0.1" + ethereumjs-account "^3.0.0" + ethereumjs-block "^2.2.2" + ethereumjs-blockchain "^4.0.3" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.2" + ethereumjs-util "^6.2.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "^2.3.2" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + util.promisify "^1.0.0" + +"@nomiclabs/truffle-contract@^4.1.2": + version "4.1.15" + resolved "https://registry.yarnpkg.com/@nomiclabs/truffle-contract/-/truffle-contract-4.1.15.tgz#cce7762ae13a97e4dbb45c366abb720866721c29" + integrity sha512-+3lP8gyiOsnFXx8ueEBsLkk8H2VSj0T++nim2AbBsgYZfkoT8v7hcfh+MdCo5EY0t9fvQgmt2n7Szqku0cmttQ== + dependencies: + "@truffle/blockchain-utils" "^0.0.18" + "@truffle/contract-schema" "^3.0.23" + "@truffle/error" "^0.0.8" + "@truffle/interface-adapter" "^0.4.6" + bignumber.js "^7.2.1" + ethereum-ens "^0.8.0" + ethers "^4.0.0-beta.1" + exorcist "^1.0.1" + source-map-support "^0.5.16" + +"@sentry/core@5.22.3": + version "5.22.3" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.22.3.tgz#030f435f2b518f282ba8bd954dac90cd70888bd7" + integrity sha512-eGL5uUarw3o4i9QUb9JoFHnhriPpWCaqeaIBB06HUpdcvhrjoowcKZj1+WPec5lFg5XusE35vez7z/FPzmJUDw== + dependencies: + "@sentry/hub" "5.22.3" + "@sentry/minimal" "5.22.3" + "@sentry/types" "5.22.3" + "@sentry/utils" "5.22.3" + tslib "^1.9.3" + +"@sentry/hub@5.22.3": + version "5.22.3" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.22.3.tgz#08309a70d2ea8d5e313d05840c1711f34f2fffe5" + integrity sha512-INo47m6N5HFEs/7GMP9cqxOIt7rmRxdERunA3H2L37owjcr77MwHVeeJ9yawRS6FMtbWXplgWTyTIWIYOuqVbw== + dependencies: + "@sentry/types" "5.22.3" + "@sentry/utils" "5.22.3" + tslib "^1.9.3" + +"@sentry/minimal@5.22.3": + version "5.22.3" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.22.3.tgz#706e4029ae5494123d3875c658ba8911aa5cc440" + integrity sha512-HoINpYnVYCpNjn2XIPIlqH5o4BAITpTljXjtAftOx6Hzj+Opjg8tR8PWliyKDvkXPpc4kXK9D6TpEDw8MO0wZA== + dependencies: + "@sentry/hub" "5.22.3" + "@sentry/types" "5.22.3" + tslib "^1.9.3" + +"@sentry/node@^5.18.1": + version "5.22.3" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.22.3.tgz#adea622eae6811e11edc8f34209c912caed91336" + integrity sha512-TCCKO7hJKiQi1nGmJcQfvbbqv98P08LULh7pb/NaO5pV20t1FtICfGx8UMpORRDehbcAiYq/f7rPOF6X/Xl5iw== + dependencies: + "@sentry/core" "5.22.3" + "@sentry/hub" "5.22.3" + "@sentry/tracing" "5.22.3" + "@sentry/types" "5.22.3" + "@sentry/utils" "5.22.3" + cookie "^0.4.1" + https-proxy-agent "^5.0.0" + lru_map "^0.3.3" + tslib "^1.9.3" + +"@sentry/tracing@5.22.3": + version "5.22.3" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.22.3.tgz#9b5a376e3164c007a22e8642ec094104468cac0c" + integrity sha512-Zp59kMCk5v56ZAyErqjv/QvGOWOQ5fRltzeVQVp8unIDTk6gEFXfhwPsYHOokJe1mfkmrgPDV6xAkYgtL3KCDQ== + dependencies: + "@sentry/hub" "5.22.3" + "@sentry/minimal" "5.22.3" + "@sentry/types" "5.22.3" + "@sentry/utils" "5.22.3" + tslib "^1.9.3" + +"@sentry/types@5.22.3": + version "5.22.3" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.22.3.tgz#d1d547b30ee8bd7771fa893af74c4f3d71f0fd18" + integrity sha512-cv+VWK0YFgCVDvD1/HrrBWOWYG3MLuCUJRBTkV/Opdy7nkdNjhCAJQrEyMM9zX0sac8FKWKOHT0sykNh8KgmYw== + +"@sentry/utils@5.22.3": + version "5.22.3" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.22.3.tgz#e3bda3e789239eb16d436f768daa12829f33d18f" + integrity sha512-AHNryXMBvIkIE+GQxTlmhBXD0Ksh+5w1SwM5qi6AttH+1qjWLvV6WB4+4pvVvEoS8t5F+WaVUZPQLmCCWp6zKw== + dependencies: + "@sentry/types" "5.22.3" + tslib "^1.9.3" + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@sindresorhus/is@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" + integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== + +"@solidity-parser/parser@^0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.5.2.tgz#4d74670ead39e4f4fdab605a393ba8ea2390a2c4" + integrity sha512-uRyvnvVYmgNmTBpWDbBsH/0kPESQhQpEc4KsvMRLVzFJ1o1s0uIv0Y6Y9IB5vI1Dwz2CbS4X/y4Wyw/75cTFnQ== + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@truffle/blockchain-utils@^0.0.18": + version "0.0.18" + resolved "https://registry.yarnpkg.com/@truffle/blockchain-utils/-/blockchain-utils-0.0.18.tgz#69e40e380729cc41fd5bf468304f9e32af57e666" + integrity sha512-XnRu5p1QO9krJizOeBY5WfzPDvEOmCnOT5u6qF8uN3Kkq9vcH3ZqW4XTuzz9ERZNpZfWb3UJx4PUosgeHLs5vw== + dependencies: + source-map-support "^0.5.16" + +"@truffle/contract-schema@^3.0.23": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@truffle/contract-schema/-/contract-schema-3.2.4.tgz#4e1227cfdecd31919f771c05fc845b1da4d1332d" + integrity sha512-CkacOyA7WE71nOJ7vHjlY3gqyeM++SplVJcTNc91BaLTh7TNhMa8Qr6pyNNiQJCZ8V7HNiMAlNw7wqQKNTpyBQ== + dependencies: + ajv "^6.10.0" + crypto-js "^3.1.9-1" + debug "^4.1.0" + +"@truffle/error@^0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.0.8.tgz#dc94ca36393403449d4b7461bf9452c241e53ec1" + integrity sha512-x55rtRuNfRO1azmZ30iR0pf0OJ6flQqbax1hJz+Avk1K5fdmOv5cr22s9qFnwTWnS6Bw0jvJEoR0ITsM7cPKtQ== + +"@truffle/interface-adapter@^0.4.6": + version "0.4.16" + resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.4.16.tgz#6bd65d9d17b4a2a51f39d05dd8b467daa8855792" + integrity sha512-lsxk26Lz/h0n8fe37K1ZxowxokXj0AZeNR10QHltDvkHukuTIC4L6fXvrUi74mCwI9hShl4CSBas1Q8kAyJyOA== + dependencies: + bn.js "^4.11.8" + ethers "^4.0.32" + source-map-support "^0.5.19" + web3 "1.2.1" + +"@types/bignumber.js@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/bignumber.js/-/bignumber.js-5.0.0.tgz#d9f1a378509f3010a3255e9cc822ad0eeb4ab969" + integrity sha512-0DH7aPGCClywOFaxxjE6UwpN2kQYe9LwuDQMv+zYA97j5GkOMo8e66LYT+a8JYU7jfmUFRZLa9KycxHDsKXJCA== + dependencies: + bignumber.js "*" + +"@types/bn.js@^4.11.3", "@types/bn.js@^4.11.4", "@types/bn.js@^4.11.5": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/chai@^4.2.0": + version "4.2.12" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.12.tgz#6160ae454cd89dae05adc3bb97997f488b608201" + integrity sha512-aN5IAC8QNtSUdQzxu7lGBgYAOuU1tmRU4c9dIq5OKGf/SBVjXo+ffM2wEjudAWbgpOhy60nLoAGH1xm8fpCKFQ== + +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + +"@types/lru-cache@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.0.tgz#57f228f2b80c046b4a1bd5cac031f81f207f4f03" + integrity sha512-RaE0B+14ToE4l6UqdarKPnXwVDuigfFv+5j9Dze/Nqr23yyuqdNvzcZi3xB+3Agvi5R4EOgAksfv3lXX4vBt9w== + +"@types/node@*": + version "14.6.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.3.tgz#cc4f979548ca4d8e7b90bc0180052ab99ee64224" + integrity sha512-pC/hkcREG6YfDfui1FBmj8e20jFU5Exjw4NYDm8kEdrW+mOh0T1Zve8DWKnS7ZIZvgncrctcNCXF4Q2I+loyww== + +"@types/node@^10.12.18", "@types/node@^10.3.2": + version "10.17.29" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.29.tgz#263b7013f9f4afa53585b199f9a4255d9613b178" + integrity sha512-zLo9rjUeQ5+QVhOufDwrb3XKyso31fJBJnk9wUUQIBDExF/O4LryvpOfozfUaxgqifTnlt7FyqsAPXUq5yFZSA== + +"@types/node@^12.12.6", "@types/node@^12.6.1": + version "12.12.55" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.55.tgz#0aa266441cb9e1fd3e415a8f619cb7d776667cdd" + integrity sha512-Vd6xQUVvPCTm7Nx1N7XHcpX6t047ltm7TgcsOr4gFHjeYgwZevo+V7I1lfzHnj5BT5frztZ42+RTG4MwYw63dw== + +"@types/pbkdf2@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" + integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== + dependencies: + "@types/node" "*" + +"@types/secp256k1@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.1.tgz#fb3aa61a1848ad97d7425ff9dcba784549fca5a4" + integrity sha512-+ZjSA8ELlOp8SlKi0YLB2tz9d5iPNEmOBd+8Rz21wTMdaXQIa9b6TEnD6l5qKOCypE7FSyPyck12qZJxSDNoog== + dependencies: + "@types/node" "*" + +"@web3-js/scrypt-shim@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@web3-js/scrypt-shim/-/scrypt-shim-0.1.0.tgz#0bf7529ab6788311d3e07586f7d89107c3bea2cc" + integrity sha512-ZtZeWCc/s0nMcdx/+rZwY1EcuRdemOK9ag21ty9UsHkFxsNb/AaoucUz0iPuyGe0Ku+PFuRmWZG7Z7462p9xPw== + dependencies: + scryptsy "^2.1.0" + semver "^6.3.0" + +"@web3-js/websocket@^1.0.29": + version "1.0.30" + resolved "https://registry.yarnpkg.com/@web3-js/websocket/-/websocket-1.0.30.tgz#9ea15b7b582cf3bf3e8bc1f4d3d54c0731a87f87" + integrity sha512-fDwrD47MiDrzcJdSeTLF75aCcxVVt8B1N74rA+vh2XCAvFy4tEWJjtnUtj2QG7/zlQ6g9cQ88bZFBxwd9/FmtA== + dependencies: + debug "^2.2.0" + es5-ext "^0.10.50" + nan "^2.14.0" + typedarray-to-buffer "^3.1.5" + yaeti "^0.0.6" + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +abstract-leveldown@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-3.0.0.tgz#5cb89f958a44f526779d740d1440e743e0c30a57" + integrity sha512-KUWx9UWGQD12zsmLNj64/pndaz4iJh/Pj7nopgkfDG6RlCcbMZvT6+9l7dchK4idog2Is8VdC/PvNbFuFmalIQ== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@^2.4.1, abstract-leveldown@~2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz#87a44d7ebebc341d59665204834c8b7e0932cc93" + integrity sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@^5.0.0, abstract-leveldown@~5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz#f7128e1f86ccabf7d2893077ce5d06d798e386c6" + integrity sha512-5mU5P1gXtsMIXg65/rsYGsi93+MlogXZ9FA8JnwKurHQg64bfXwGYVdVdijNTVNOlAsuIiOwHdvFFD5JqCJQ7A== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@~2.6.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz#1c5e8c6a5ef965ae8c35dfb3a8770c476b82c4b8" + integrity sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA== + dependencies: + xtend "~4.0.0" + +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +aes-js@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + integrity sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0= + +aes-js@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" + integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ== + +agent-base@6: + version "6.0.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.1.tgz#808007e4e5867decb0ab6ab2f928fbdb5a596db4" + integrity sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg== + dependencies: + debug "4" + +ajv@^6.10.0, ajv@^6.12.3: + version "6.12.4" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" + integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-colors@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" + integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== + +ansi-colors@4.1.1, ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-colors@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" + integrity sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA== + dependencies: + ansi-wrap "^0.1.0" + +ansi-escapes@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + +ansi-gray@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" + integrity sha1-KWLPVOyXksSFEKPetSRDaGHvclE= + dependencies: + ansi-wrap "0.1.0" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + +ansi-wrap@0.1.0, ansi-wrap@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" + integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= + +any-promise@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +app-module-path@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" + integrity sha1-ZBqlXft9am8KgUHEucCqULbCTdU= + +append-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/append-buffer/-/append-buffer-1.0.2.tgz#d8220cf466081525efea50614f3de6514dfa58f1" + integrity sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE= + dependencies: + buffer-equal "^1.0.0" + +archive-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/archive-type/-/archive-type-4.0.0.tgz#f92e72233056dfc6969472749c267bdb046b1d70" + integrity sha1-+S5yIzBW38aWlHJ0nCZ72wRrHXA= + dependencies: + file-type "^4.2.0" + +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-filter@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/arr-filter/-/arr-filter-1.1.2.tgz#43fdddd091e8ef11aa4c45d9cdc18e2dff1711ee" + integrity sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4= + dependencies: + make-iterator "^1.0.0" + +arr-flatten@^1.0.1, arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-map@^2.0.0, arr-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/arr-map/-/arr-map-2.0.2.tgz#3a77345ffc1cf35e2a91825601f9e58f2e24cac4" + integrity sha1-Onc0X/wc814qkYJWAfnljy4kysQ= + dependencies: + make-iterator "^1.0.0" + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-each@^1.0.0, array-each@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" + integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8= + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-initial@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/array-initial/-/array-initial-1.1.0.tgz#2fa74b26739371c3947bd7a7adc73be334b3d795" + integrity sha1-L6dLJnOTccOUe9enrcc74zSz15U= + dependencies: + array-slice "^1.0.0" + is-number "^4.0.0" + +array-last@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/array-last/-/array-last-1.3.0.tgz#7aa77073fec565ddab2493f5f88185f404a9d336" + integrity sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg== + dependencies: + is-number "^4.0.0" + +array-slice@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" + integrity sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w== + +array-sort@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-sort/-/array-sort-1.0.0.tgz#e4c05356453f56f53512a7d1d6123f2c54c0a88a" + integrity sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg== + dependencies: + default-compare "^1.0.0" + get-value "^2.0.6" + kind-of "^5.0.2" + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +array.prototype.map@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array.prototype.map/-/array.prototype.map-1.0.2.tgz#9a4159f416458a23e9483078de1106b2ef68f8ec" + integrity sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.4" + +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +async-done@^1.2.0, async-done@^1.2.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/async-done/-/async-done-1.3.2.tgz#5e15aa729962a4b07414f528a88cdf18e0b290a2" + integrity sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.2" + process-nextick-args "^2.0.0" + stream-exhaust "^1.0.1" + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +async-eventemitter@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" + integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== + dependencies: + async "^2.4.0" + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async-settle@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-settle/-/async-settle-1.0.0.tgz#1d0a914bb02575bec8a8f3a74e5080f72b2c0c6b" + integrity sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs= + dependencies: + async-done "^1.2.2" + +async@2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" + integrity sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg== + dependencies: + lodash "^4.17.11" + +async@^1.4.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + +async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0, async@^2.6.1: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.10.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428" + integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA== + +babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-core@^6.0.14, babel-core@^6.26.0: + version "6.26.3" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" + integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA== + dependencies: + babel-code-frame "^6.26.0" + babel-generator "^6.26.0" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + convert-source-map "^1.5.1" + debug "^2.6.9" + json5 "^0.5.1" + lodash "^4.17.4" + minimatch "^3.0.4" + path-is-absolute "^1.0.1" + private "^0.1.8" + slash "^1.0.0" + source-map "^0.5.7" + +babel-generator@^6.26.0: + version "6.26.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" + integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA== + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.7" + trim-right "^1.0.1" + +babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" + integrity sha1-zORReto1b0IgvK6KAsKzRvmlZmQ= + dependencies: + babel-helper-explode-assignable-expression "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" + integrity sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340= + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-define-map@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f" + integrity sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8= + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-explode-assignable-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" + integrity sha1-8luCz33BBDPFX3BZLVdGQArCLKo= + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" + integrity sha1-00dbjAPtmCQqJbSDUasYOZ01gKk= + dependencies: + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" + integrity sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" + integrity sha1-HssnaJydJVE+rbyZFKc/VAi+enY= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" + integrity sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-regex@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72" + integrity sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI= + dependencies: + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-remap-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" + integrity sha1-XsWBgnrXI/7N04HxySg5BnbkVRs= + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" + integrity sha1-v22/5Dk40XNpohPKiov3S2qQqxo= + dependencies: + babel-helper-optimise-call-expression "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + integrity sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI= + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-check-es2015-constants@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" + integrity sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-syntax-async-functions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + integrity sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU= + +babel-plugin-syntax-exponentiation-operator@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" + integrity sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4= + +babel-plugin-syntax-trailing-function-commas@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" + integrity sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM= + +babel-plugin-transform-async-to-generator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" + integrity sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E= + dependencies: + babel-helper-remap-async-to-generator "^6.24.1" + babel-plugin-syntax-async-functions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-arrow-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" + integrity sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" + integrity sha1-u8UbSflk1wy42OC5ToICRs46YUE= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoping@^6.23.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f" + integrity sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8= + dependencies: + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-plugin-transform-es2015-classes@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" + integrity sha1-WkxYpQyclGHlZLSyo7+ryXolhNs= + dependencies: + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-computed-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" + integrity sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM= + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-destructuring@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" + integrity sha1-mXux8auWf2gtKwh2/jWNYOdlxW0= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-duplicate-keys@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" + integrity sha1-c+s9MQypaePvnskcU3QabxV2Qj4= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-for-of@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" + integrity sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-function-name@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" + integrity sha1-g0yJhTvDaxrw86TF26qU/Y6sqos= + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" + integrity sha1-T1SgLWzWbPkVKAAZox0xklN3yi4= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" + integrity sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ= + dependencies: + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1: + version "6.26.2" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3" + integrity sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q== + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-types "^6.26.0" + +babel-plugin-transform-es2015-modules-systemjs@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" + integrity sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM= + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-umd@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" + integrity sha1-rJl+YoXNGO1hdq22B9YCNErThGg= + dependencies: + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-object-super@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" + integrity sha1-JM72muIcuDp/hgPa0CH1cusnj40= + dependencies: + babel-helper-replace-supers "^6.24.1" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-parameters@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" + integrity sha1-V6w1GrScrxSpfNE7CfZv3wpiXys= + dependencies: + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-shorthand-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" + integrity sha1-JPh11nIch2YbvZmkYi5R8U3jiqA= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-spread@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" + integrity sha1-1taKmfia7cRTbIGlQujdnxdG+NE= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-sticky-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" + integrity sha1-AMHNsaynERLN8M9hJsLta0V8zbw= + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-template-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" + integrity sha1-qEs0UPfp+PH2g51taH2oS7EjbY0= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-typeof-symbol@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" + integrity sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I= + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-unicode-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" + integrity sha1-04sS9C6nMj9yk4fxinxa4frrNek= + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + regexpu-core "^2.0.0" + +babel-plugin-transform-exponentiation-operator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" + integrity sha1-KrDJx/MJj6SJB3cruBP+QejeOg4= + dependencies: + babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" + babel-plugin-syntax-exponentiation-operator "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-regenerator@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" + integrity sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8= + dependencies: + regenerator-transform "^0.10.0" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + integrity sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g= + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-preset-env@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a" + integrity sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg== + dependencies: + babel-plugin-check-es2015-constants "^6.22.0" + babel-plugin-syntax-trailing-function-commas "^6.22.0" + babel-plugin-transform-async-to-generator "^6.22.0" + babel-plugin-transform-es2015-arrow-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoping "^6.23.0" + babel-plugin-transform-es2015-classes "^6.23.0" + babel-plugin-transform-es2015-computed-properties "^6.22.0" + babel-plugin-transform-es2015-destructuring "^6.23.0" + babel-plugin-transform-es2015-duplicate-keys "^6.22.0" + babel-plugin-transform-es2015-for-of "^6.23.0" + babel-plugin-transform-es2015-function-name "^6.22.0" + babel-plugin-transform-es2015-literals "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.22.0" + babel-plugin-transform-es2015-modules-commonjs "^6.23.0" + babel-plugin-transform-es2015-modules-systemjs "^6.23.0" + babel-plugin-transform-es2015-modules-umd "^6.23.0" + babel-plugin-transform-es2015-object-super "^6.22.0" + babel-plugin-transform-es2015-parameters "^6.23.0" + babel-plugin-transform-es2015-shorthand-properties "^6.22.0" + babel-plugin-transform-es2015-spread "^6.22.0" + babel-plugin-transform-es2015-sticky-regex "^6.22.0" + babel-plugin-transform-es2015-template-literals "^6.22.0" + babel-plugin-transform-es2015-typeof-symbol "^6.23.0" + babel-plugin-transform-es2015-unicode-regex "^6.22.0" + babel-plugin-transform-exponentiation-operator "^6.22.0" + babel-plugin-transform-regenerator "^6.22.0" + browserslist "^3.2.6" + invariant "^2.2.2" + semver "^5.3.0" + +babel-register@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" + integrity sha1-btAhFz4vy0htestFxgCahW9kcHE= + dependencies: + babel-core "^6.26.0" + babel-runtime "^6.26.0" + core-js "^2.5.0" + home-or-tmp "^2.0.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + source-map-support "^0.4.15" + +babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.24.1, babel-template@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI= + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.24.1, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babelify@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/babelify/-/babelify-7.3.0.tgz#aa56aede7067fd7bd549666ee16dc285087e88e5" + integrity sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU= + dependencies: + babel-core "^6.0.14" + object-assign "^4.0.0" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== + +bach@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880" + integrity sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA= + dependencies: + arr-filter "^1.1.1" + arr-flatten "^1.0.1" + arr-map "^2.0.0" + array-each "^1.0.0" + array-initial "^1.0.0" + array-last "^1.1.1" + async-done "^1.2.2" + async-settle "^1.0.0" + now-and-later "^2.0.0" + +backoff@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" + integrity sha1-9hbtqdPktmuMp/ynn2lXIsX44m8= + dependencies: + precond "0.2" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base-x@^3.0.2, base-x@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d" + integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA== + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.0.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +bignumber.js@*, bignumber.js@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.0.tgz#805880f84a329b5eac6e7cb6f8274b6d82bdf075" + integrity sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A== + +bignumber.js@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" + integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +binary-extensions@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" + integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== + +bindings@^1.2.1, bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bip39@2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/bip39/-/bip39-2.5.0.tgz#51cbd5179460504a63ea3c000db3f787ca051235" + integrity sha512-xwIx/8JKoT2+IPJpFEfXoWdYwP7UVAoUxxLNfGCfVowaJE7yg1Y5B1BVPqlUNsBq5/nGwmFkwRJ8xDW4sX8OdA== + dependencies: + create-hash "^1.1.0" + pbkdf2 "^3.0.9" + randombytes "^2.0.1" + safe-buffer "^5.0.1" + unorm "^1.3.3" + +bip66@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bip66/-/bip66-1.1.5.tgz#01fa8748785ca70955d5011217d1b3139969ca22" + integrity sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI= + dependencies: + safe-buffer "^5.0.1" + +bl@^1.0.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7" + integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww== + dependencies: + readable-stream "^2.3.5" + safe-buffer "^5.1.1" + +blakejs@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5" + integrity sha1-ad+S75U6qIylGjLfarHFShVfx6U= + +bluebird@^3.4.7, bluebird@^3.5.0: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha1-UzRK2xRhehP26N0s4okF0cC6MhU= + +bn.js@4.11.8: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.4.0, bn.js@^4.8.0: + version "4.11.9" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" + integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== + +bn.js@^5.1.1, bn.js@^5.1.2: + version "5.1.3" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b" + integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ== + +body-parser@1.19.0, body-parser@^1.16.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.0.6, browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +browserslist@^3.2.6: + version "3.2.8" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.2.8.tgz#b0005361d6471f0f5952797a76fc985f1f978fc6" + integrity sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ== + dependencies: + caniuse-lite "^1.0.30000844" + electron-to-chromium "^1.3.47" + +bs58@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +buffer-alloc-unsafe@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== + +buffer-alloc@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" + integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== + dependencies: + buffer-alloc-unsafe "^1.1.0" + buffer-fill "^1.0.0" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + +buffer-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" + integrity sha1-WWFrSYME1Var1GaWayLu2j7KX74= + +buffer-fill@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" + integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-to-arraybuffer@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" + integrity sha1-YGSkD6dutDxyOrqe+PbhIW0QURo= + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer-xor@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-2.0.2.tgz#34f7c64f04c777a1f8aac5e661273bb9dd320289" + integrity sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ== + dependencies: + safe-buffer "^5.1.1" + +buffer@^5.0.5, buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + +bufferutil@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.1.tgz#3a177e8e5819a1243fe16b63a199951a7ad8d4a7" + integrity sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA== + dependencies: + node-gyp-build "~3.7.0" + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +bytewise-core@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/bytewise-core/-/bytewise-core-1.2.3.tgz#3fb410c7e91558eb1ab22a82834577aa6bd61d42" + integrity sha1-P7QQx+kVWOsasiqCg0V3qmvWHUI= + dependencies: + typewise-core "^1.2" + +bytewise@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/bytewise/-/bytewise-1.1.0.tgz#1d13cbff717ae7158094aa881b35d081b387253e" + integrity sha1-HRPL/3F65xWAlKqIGzXQgbOHJT4= + dependencies: + bytewise-core "^1.2.2" + typewise "^1.0.3" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cacheable-request@^2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" + integrity sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0= + dependencies: + clone-response "1.0.2" + get-stream "3.0.0" + http-cache-semantics "3.8.1" + keyv "3.0.0" + lowercase-keys "1.0.0" + normalize-url "2.0.1" + responselike "1.0.2" + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +cachedown@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cachedown/-/cachedown-1.0.0.tgz#d43f036e4510696b31246d7db31ebf0f7ac32d15" + integrity sha1-1D8DbkUQaWsxJG19sx6/D3rDLRU= + dependencies: + abstract-leveldown "^2.4.1" + lru-cache "^3.2.0" + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +caniuse-lite@^1.0.30000844: + version "1.0.30001123" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001123.tgz#7b981d81382ab2c8fd062f3e6439215e8c503c22" + integrity sha512-03dJDoa4YC4332jq0rqwiM+Hw6tA5RJtrnZKvOQy7ASoIUv8CinkcmGhYpCvCjedvkBQrrKnkcELxrUSW/XwNQ== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +caw@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/caw/-/caw-2.0.1.tgz#6c3ca071fc194720883c2dc5da9b074bfc7e9e95" + integrity sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA== + dependencies: + get-proxy "^2.0.0" + isurl "^1.0.0-alpha5" + tunnel-agent "^0.6.0" + url-to-options "^1.0.1" + +chai@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" + integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.0" + type-detect "^4.0.5" + +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + +checkpoint-store@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/checkpoint-store/-/checkpoint-store-1.1.0.tgz#04e4cb516b91433893581e6d4601a78e9552ea06" + integrity sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY= + dependencies: + functional-red-black-tree "^1.0.1" + +chokidar@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" + integrity sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.2.0" + optionalDependencies: + fsevents "~2.1.1" + +chokidar@3.4.2, chokidar@^3.4.0: + version "3.4.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" + integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + +chokidar@^2.0.0: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cids@^0.7.1: + version "0.7.5" + resolved "https://registry.yarnpkg.com/cids/-/cids-0.7.5.tgz#60a08138a99bfb69b6be4ceb63bfef7a396b28b2" + integrity sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA== + dependencies: + buffer "^5.5.0" + class-is "^1.1.0" + multibase "~0.6.0" + multicodec "^1.0.0" + multihashes "~0.4.15" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +class-is@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" + integrity sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +clone-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" + integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= + +clone-response@1.0.2, clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +clone-stats@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" + integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= + +clone@2.1.2, clone@^2.0.0, clone@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +cloneable-readable@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec" + integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ== + dependencies: + inherits "^2.0.1" + process-nextick-args "^2.0.0" + readable-stream "^2.3.5" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +collection-map@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-map/-/collection-map-1.0.0.tgz#aea0f06f8d26c780c2b75494385544b2255af18c" + integrity sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw= + dependencies: + arr-map "^2.0.2" + for-own "^1.0.0" + make-iterator "^1.0.0" + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-support@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +command-exists@^1.2.8: + version "1.2.9" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" + integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== + +commander@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" + integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== + +commander@^2.8.1: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.5.1, concat-stream@^1.6.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +config-chain@^1.1.11: + version "1.1.12" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" + integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +content-disposition@0.5.3, content-disposition@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-hash@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/content-hash/-/content-hash-2.5.2.tgz#bbc2655e7c21f14fd3bfc7b7d4bfe6e454c9e211" + integrity sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw== + dependencies: + cids "^0.7.1" + multicodec "^0.5.5" + multihashes "^0.4.15" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +convert-source-map@^1.1.0, convert-source-map@^1.5.0, convert-source-map@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +cookie@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" + integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== + +cookiejar@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" + integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +copy-props@^2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/copy-props/-/copy-props-2.0.4.tgz#93bb1cadfafd31da5bb8a9d4b41f471ec3a72dfe" + integrity sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A== + dependencies: + each-props "^1.3.0" + is-plain-object "^2.0.1" + +core-js-pure@^3.0.1: + version "3.6.5" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813" + integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA== + +core-js@^2.4.0, core-js@^2.5.0: + version "2.6.11" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" + integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cors@^2.8.1: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-fetch@^2.1.0, cross-fetch@^2.1.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.3.tgz#e8a0b3c54598136e037f8650f8e823ccdfac198e" + integrity sha512-PrWWNH3yL2NYIb/7WF/5vFG3DCQiXDOVf8k3ijatbrtnwNuhMWLC7YF7uqf53tbTFDzHIUD8oITw4Bxt8ST3Nw== + dependencies: + node-fetch "2.1.2" + whatwg-fetch "2.0.4" + +cross-spawn@^6.0.0: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-browserify@3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +crypto-js@^3.1.9-1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b" + integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q== + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@4, debug@4.1.1, debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +decamelize@^1.1.1, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +decompress-response@^3.2.0, decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" + integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ== + dependencies: + file-type "^5.2.0" + is-stream "^1.1.0" + tar-stream "^1.5.2" + +decompress-tarbz2@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b" + integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A== + dependencies: + decompress-tar "^4.1.0" + file-type "^6.1.0" + is-stream "^1.1.0" + seek-bzip "^1.0.5" + unbzip2-stream "^1.0.9" + +decompress-targz@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee" + integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w== + dependencies: + decompress-tar "^4.1.1" + file-type "^5.2.0" + is-stream "^1.1.0" + +decompress-unzip@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69" + integrity sha1-3qrM39FK6vhVePczroIQ+bSEj2k= + dependencies: + file-type "^3.8.0" + get-stream "^2.2.0" + pify "^2.3.0" + yauzl "^2.4.2" + +decompress@^4.0.0, decompress@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.1.tgz#007f55cc6a62c055afa37c07eb6a4ee1b773f118" + integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ== + dependencies: + decompress-tar "^4.0.0" + decompress-tarbz2 "^4.0.0" + decompress-targz "^4.0.0" + decompress-unzip "^4.0.1" + graceful-fs "^4.1.10" + make-dir "^1.0.0" + pify "^2.3.0" + strip-dirs "^2.0.0" + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== + dependencies: + type-detect "^4.0.0" + +deep-equal@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" + +deepmerge@^2.1.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170" + integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA== + +default-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f" + integrity sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ== + dependencies: + kind-of "^5.0.2" + +default-resolution@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/default-resolution/-/default-resolution-2.0.0.tgz#bcb82baa72ad79b426a76732f1a81ad6df26d684" + integrity sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ= + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +deferred-leveldown@~1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz#3acd2e0b75d1669924bc0a4b642851131173e1eb" + integrity sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA== + dependencies: + abstract-leveldown "~2.6.0" + +deferred-leveldown@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-4.0.2.tgz#0b0570087827bf480a23494b398f04c128c19a20" + integrity sha512-5fMC8ek8alH16QiV0lTCis610D1Zt1+LA4MS4d63JgS32lrCjTFDUFz2ao09/j2I4Bqb5jL4FZYwu7Jz0XO1ww== + dependencies: + abstract-leveldown "~5.0.0" + inherits "^2.0.3" + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +defined@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-file@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" + integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg= + dependencies: + repeating "^2.0.0" + +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + +diff@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + +dotignore@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dotignore/-/dotignore-0.1.2.tgz#f942f2200d28c3a76fbdd6f0ee9f3257c8a2e905" + integrity sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw== + dependencies: + minimatch "^3.0.4" + +download@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/download/-/download-7.1.0.tgz#9059aa9d70b503ee76a132897be6dec8e5587233" + integrity sha512-xqnBTVd/E+GxJVrX5/eUJiLYjCGPwMpdL+jGhGU57BvtcA7wwhtHVbXBeUk51kOpW3S7Jn3BQbN9Q1R1Km2qDQ== + dependencies: + archive-type "^4.0.0" + caw "^2.0.1" + content-disposition "^0.5.2" + decompress "^4.2.0" + ext-name "^5.0.0" + file-type "^8.1.0" + filenamify "^2.0.0" + get-stream "^3.0.0" + got "^8.3.1" + make-dir "^1.2.0" + p-event "^2.1.0" + pify "^3.0.0" + +drbg.js@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/drbg.js/-/drbg.js-1.0.1.tgz#3e36b6c42b37043823cdbc332d58f31e2445480b" + integrity sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs= + dependencies: + browserify-aes "^1.0.6" + create-hash "^1.1.2" + create-hmac "^1.1.4" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +each-props@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/each-props/-/each-props-1.3.2.tgz#ea45a414d16dd5cfa419b1a81720d5ca06892333" + integrity sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA== + dependencies: + is-plain-object "^2.0.1" + object.defaults "^1.1.0" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.47: + version "1.3.560" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.560.tgz#6c3f61fe50324770b75705300e9f98f29312ea8d" + integrity sha512-0cEFfOA3sNXfSxo0FIClBhrLVSe/QO9LBiqmmYPm3N/IYyt41NRTa2EhvOMWAOKpjd91t/rq062yhnJzfVMKkQ== + +elliptic@6.3.3: + version "6.3.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.3.3.tgz#5482d9646d54bcb89fd7d994fc9e2e9568876e3f" + integrity sha1-VILZZG1UvLif19mU/J4ulWiHbj8= + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + inherits "^2.0.1" + +elliptic@6.5.3, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3: + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +encoding-down@5.0.4, encoding-down@~5.0.0: + version "5.0.4" + resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-5.0.4.tgz#1e477da8e9e9d0f7c8293d320044f8b2cd8e9614" + integrity sha512-8CIZLDcSKxgzT+zX8ZVfgNbu8Md2wq/iqa1Y7zyVR18QBEAc0Nmzuvj/N5ykSKpfGzjM8qxbaFntLPwnVoUhZw== + dependencies: + abstract-leveldown "^5.0.0" + inherits "^2.0.3" + level-codec "^9.0.0" + level-errors "^2.0.0" + xtend "^4.0.1" + +encoding@^0.1.11: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enquirer@^2.3.0: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + +errno@~0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== + dependencies: + prr "~1.0.1" + +error-ex@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4, es-abstract@^1.17.5: + version "1.17.6" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" + integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.0" + is-regex "^1.1.0" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + +es-get-iterator@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8" + integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ== + dependencies: + es-abstract "^1.17.4" + has-symbols "^1.0.1" + is-arguments "^1.0.4" + is-map "^2.0.1" + is-set "^2.0.1" + is-string "^1.0.5" + isarray "^2.0.5" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@^2.0.1, es6-iterator@^2.0.3, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +es6-weak-map@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eth-block-tracker@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-3.0.1.tgz#95cd5e763c7293e0b1b2790a2a39ac2ac188a5e1" + integrity sha512-WUVxWLuhMmsfenfZvFO5sbl1qFY2IqUlw/FPVmjjdElpqLsZtSG+wPe9Dz7W/sB6e80HgFKknOmKk2eNlznHug== + dependencies: + eth-query "^2.1.0" + ethereumjs-tx "^1.3.3" + ethereumjs-util "^5.1.3" + ethjs-util "^0.1.3" + json-rpc-engine "^3.6.0" + pify "^2.3.0" + tape "^4.6.3" + +eth-ens-namehash@2.0.8, eth-ens-namehash@^2.0.0: + version "2.0.8" + resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" + integrity sha1-IprEbsqG1S4MmR58sq74P/D2i88= + dependencies: + idna-uts46-hx "^2.3.1" + js-sha3 "^0.5.7" + +eth-json-rpc-infura@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/eth-json-rpc-infura/-/eth-json-rpc-infura-3.2.1.tgz#26702a821067862b72d979c016fd611502c6057f" + integrity sha512-W7zR4DZvyTn23Bxc0EWsq4XGDdD63+XPUCEhV2zQvQGavDVC4ZpFDK4k99qN7bd7/fjj37+rxmuBOBeIqCA5Mw== + dependencies: + cross-fetch "^2.1.1" + eth-json-rpc-middleware "^1.5.0" + json-rpc-engine "^3.4.0" + json-rpc-error "^2.0.0" + +eth-json-rpc-middleware@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/eth-json-rpc-middleware/-/eth-json-rpc-middleware-1.6.0.tgz#5c9d4c28f745ccb01630f0300ba945f4bef9593f" + integrity sha512-tDVCTlrUvdqHKqivYMjtFZsdD7TtpNLBCfKAcOpaVs7orBMS/A8HWro6dIzNtTZIR05FAbJ3bioFOnZpuCew9Q== + dependencies: + async "^2.5.0" + eth-query "^2.1.2" + eth-tx-summary "^3.1.2" + ethereumjs-block "^1.6.0" + ethereumjs-tx "^1.3.3" + ethereumjs-util "^5.1.2" + ethereumjs-vm "^2.1.0" + fetch-ponyfill "^4.0.0" + json-rpc-engine "^3.6.0" + json-rpc-error "^2.0.0" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + tape "^4.6.3" + +eth-lib@0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.7.tgz#2f93f17b1e23aec3759cd4a3fe20c1286a3fc1ca" + integrity sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco= + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + +eth-lib@0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8" + integrity sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + +eth-lib@^0.1.26: + version "0.1.29" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.29.tgz#0c11f5060d42da9f931eab6199084734f4dbd1d9" + integrity sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + nano-json-stream-parser "^0.1.2" + servify "^0.1.12" + ws "^3.0.0" + xhr-request-promise "^0.1.2" + +eth-query@^2.0.2, eth-query@^2.1.0, eth-query@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/eth-query/-/eth-query-2.1.2.tgz#d6741d9000106b51510c72db92d6365456a6da5e" + integrity sha1-1nQdkAAQa1FRDHLbktY2VFam2l4= + dependencies: + json-rpc-random-id "^1.0.0" + xtend "^4.0.1" + +eth-sig-util@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-2.3.0.tgz#c54a6ac8e8796f7e25f59cf436982a930e645231" + integrity sha512-ugD1AvaggvKaZDgnS19W5qOfepjGc7qHrt7TrAaL54gJw9SHvgIXJ3r2xOMW30RWJZNP+1GlTOy5oye7yXA4xA== + dependencies: + buffer "^5.2.1" + elliptic "^6.4.0" + ethereumjs-abi "0.6.5" + ethereumjs-util "^5.1.1" + tweetnacl "^1.0.0" + tweetnacl-util "^0.15.0" + +eth-sig-util@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-1.4.2.tgz#8d958202c7edbaae839707fba6f09ff327606210" + integrity sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA= + dependencies: + ethereumjs-abi "git+https://github.com/ethereumjs/ethereumjs-abi.git" + ethereumjs-util "^5.1.1" + +eth-sig-util@^2.5.2: + version "2.5.3" + resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-2.5.3.tgz#6938308b38226e0b3085435474900b03036abcbe" + integrity sha512-KpXbCKmmBUNUTGh9MRKmNkIPietfhzBqqYqysDavLseIiMUGl95k6UcPEkALAZlj41e9E6yioYXc1PC333RKqw== + dependencies: + buffer "^5.2.1" + elliptic "^6.4.0" + ethereumjs-abi "0.6.5" + ethereumjs-util "^5.1.1" + tweetnacl "^1.0.0" + tweetnacl-util "^0.15.0" + +eth-tx-summary@^3.1.2: + version "3.2.4" + resolved "https://registry.yarnpkg.com/eth-tx-summary/-/eth-tx-summary-3.2.4.tgz#e10eb95eb57cdfe549bf29f97f1e4f1db679035c" + integrity sha512-NtlDnaVZah146Rm8HMRUNMgIwG/ED4jiqk0TME9zFheMl1jOp6jL1m0NKGjJwehXQ6ZKCPr16MTr+qspKpEXNg== + dependencies: + async "^2.1.2" + clone "^2.0.0" + concat-stream "^1.5.1" + end-of-stream "^1.1.0" + eth-query "^2.0.2" + ethereumjs-block "^1.4.1" + ethereumjs-tx "^1.1.1" + ethereumjs-util "^5.0.1" + ethereumjs-vm "^2.6.0" + through2 "^2.0.3" + +ethashjs@~0.0.7: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ethashjs/-/ethashjs-0.0.8.tgz#227442f1bdee409a548fb04136e24c874f3aa6f9" + integrity sha512-/MSbf/r2/Ld8o0l15AymjOTlPqpN8Cr4ByUEA9GtR4x0yAh3TdtDzEg29zMjXCNPI7u6E5fOQdj/Cf9Tc7oVNw== + dependencies: + async "^2.1.2" + buffer-xor "^2.0.1" + ethereumjs-util "^7.0.2" + miller-rabin "^4.0.0" + +ethereum-bloom-filters@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.7.tgz#b7b80735e385dbb7f944ce6b4533e24511306060" + integrity sha512-cDcJJSJ9GMAcURiAWO3DxIEhTL/uWqlQnvgKpuYQzYPrt/izuGU+1ntQmHt0IRq6ADoSYHFnB+aCEFIldjhkMQ== + dependencies: + js-sha3 "^0.8.0" + +ethereum-common@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.2.0.tgz#13bf966131cce1eeade62a1b434249bb4cb120ca" + integrity sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA== + +ethereum-common@^0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" + integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8= + +ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereum-ens@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/ethereum-ens/-/ethereum-ens-0.8.0.tgz#6d0f79acaa61fdbc87d2821779c4e550243d4c57" + integrity sha512-a8cBTF4AWw1Q1Y37V1LSCS9pRY4Mh3f8vCg5cbXCCEJ3eno1hbI/+Ccv9SZLISYpqQhaglP3Bxb/34lS4Qf7Bg== + dependencies: + bluebird "^3.4.7" + eth-ens-namehash "^2.0.0" + js-sha3 "^0.5.7" + pako "^1.0.4" + underscore "^1.8.3" + web3 "^1.0.0-beta.34" + +ethereumjs-abi@0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.5.tgz#5a637ef16ab43473fa72a29ad90871405b3f5241" + integrity sha1-WmN+8Wq0NHP6cqKa2QhxQFs/UkE= + dependencies: + bn.js "^4.10.0" + ethereumjs-util "^4.3.0" + +ethereumjs-abi@0.6.7: + version "0.6.7" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.7.tgz#d1d1c5cdb8d910a7d97645ba9e93be5d153bba2e" + integrity sha512-EMLOA8ICO5yAaXDhjVEfYjsJIXYutY8ufTE93eEKwsVtp2usQreKwsDTJ9zvam3omYqNuffr8IONIqb2uUslGQ== + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-abi@^0.6.4, ethereumjs-abi@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +"ethereumjs-abi@git+https://github.com/ethereumjs/ethereumjs-abi.git": + version "0.6.8" + resolved "git+https://github.com/ethereumjs/ethereumjs-abi.git#1ce6a1d64235fabe2aaf827fd606def55693508f" + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-account@3.0.0, ethereumjs-account@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-3.0.0.tgz#728f060c8e0c6e87f1e987f751d3da25422570a9" + integrity sha512-WP6BdscjiiPkQfF9PVfMcwx/rDvfZTjFKY0Uwc09zSQr9JfIVH87dYIJu0gNhBhpmovV4yq295fdllS925fnBA== + dependencies: + ethereumjs-util "^6.0.0" + rlp "^2.2.1" + safe-buffer "^5.1.1" + +ethereumjs-account@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz#eeafc62de544cb07b0ee44b10f572c9c49e00a84" + integrity sha512-bgDojnXGjhMwo6eXQC0bY6UK2liSFUSMwwylOmQvZbSl/D7NXQ3+vrGO46ZeOgjGfxXmgIeVNDIiHw7fNZM4VA== + dependencies: + ethereumjs-util "^5.0.0" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-block@2.2.2, ethereumjs-block@^2.2.0, ethereumjs-block@^2.2.2, ethereumjs-block@~2.2.0, ethereumjs-block@~2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz#c7654be7e22df489fda206139ecd63e2e9c04965" + integrity sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg== + dependencies: + async "^2.0.1" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.1" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-block@^1.2.2, ethereumjs-block@^1.4.1, ethereumjs-block@^1.6.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz#78b88e6cc56de29a6b4884ee75379b6860333c3f" + integrity sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg== + dependencies: + async "^2.0.1" + ethereum-common "0.2.0" + ethereumjs-tx "^1.2.2" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-blockchain@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/ethereumjs-blockchain/-/ethereumjs-blockchain-4.0.4.tgz#30f2228dc35f6dcf94423692a6902604ae34960f" + integrity sha512-zCxaRMUOzzjvX78DTGiKjA+4h2/sF0OYL1QuPux0DHpyq8XiNoF5GYHtb++GUxVlMsMfZV7AVyzbtgcRdIcEPQ== + dependencies: + async "^2.6.1" + ethashjs "~0.0.7" + ethereumjs-block "~2.2.2" + ethereumjs-common "^1.5.0" + ethereumjs-util "^6.1.0" + flow-stoplight "^1.0.0" + level-mem "^3.0.1" + lru-cache "^5.1.1" + rlp "^2.2.2" + semaphore "^1.1.0" + +ethereumjs-common@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.5.0.tgz#d3e82fc7c47c0cef95047f431a99485abc9bb1cd" + integrity sha512-SZOjgK1356hIY7MRj3/ma5qtfr/4B5BL+G4rP/XSMYr2z1H5el4RX5GReYCKmQmYI/nSBmRnwrZ17IfHuG0viQ== + +ethereumjs-common@^1.1.0, ethereumjs-common@^1.3.2, ethereumjs-common@^1.5.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz#2065dbe9214e850f2e955a80e650cb6999066979" + integrity sha512-hTfZjwGX52GS2jcVO6E2sx4YuFnf0Fhp5ylo4pEPhEffNln7vS59Hr5sLnp3/QCazFLluuBZ+FZ6J5HTp0EqCA== + +ethereumjs-tx@2.1.2, ethereumjs-tx@^2.1.1, ethereumjs-tx@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz#5dfe7688bf177b45c9a23f86cf9104d47ea35fed" + integrity sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw== + dependencies: + ethereumjs-common "^1.5.0" + ethereumjs-util "^6.0.0" + +ethereumjs-tx@^1.1.1, ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.3: + version "1.3.7" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz#88323a2d875b10549b8347e09f4862b546f3d89a" + integrity sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA== + dependencies: + ethereum-common "^0.0.18" + ethereumjs-util "^5.0.0" + +ethereumjs-util@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz#e9c51e5549e8ebd757a339cc00f5380507e799c8" + integrity sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q== + dependencies: + bn.js "^4.11.0" + create-hash "^1.1.2" + ethjs-util "0.1.6" + keccak "^1.0.2" + rlp "^2.0.0" + safe-buffer "^5.1.1" + secp256k1 "^3.0.1" + +ethereumjs-util@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.0.tgz#23ec79b2488a7d041242f01e25f24e5ad0357960" + integrity sha512-vb0XN9J2QGdZGIEKG2vXM+kUdEivUfU6Wmi5y0cg+LRhDYKnXIZ/Lz7XjFbHRR9VIKq2lVGLzGBkA++y2nOdOQ== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + ethjs-util "0.1.6" + keccak "^2.0.0" + rlp "^2.2.3" + secp256k1 "^3.0.1" + +ethereumjs-util@^4.3.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-4.5.1.tgz#f4bf9b3b515a484e3cc8781d61d9d980f7c83bd0" + integrity sha512-WrckOZ7uBnei4+AKimpuF1B3Fv25OmoRgmYCpGsP7u8PFxXAmAgiJSYT2kRWnt6fVIlKaQlZvuwXp7PIrmn3/w== + dependencies: + bn.js "^4.8.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + rlp "^2.0.0" + +ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5, ethereumjs-util@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz#a833f0e5fca7e5b361384dc76301a721f537bf65" + integrity sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ== + dependencies: + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "^0.1.3" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0, ethereumjs-util@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + +ethereumjs-util@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.4.tgz#f4b2022a91416bf421b35b0d5b81c21e8abd8b7f" + integrity sha512-isldtbCn9fdnhBPxedMNbFkNWVZ8ZdQvKRDSrdflame/AycAPKMer+vEpndpBxYIB3qxN6bd3Gh1YCQW9LDkCQ== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.4" + +ethereumjs-vm@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-4.2.0.tgz#e885e861424e373dbc556278f7259ff3fca5edab" + integrity sha512-X6qqZbsY33p5FTuZqCnQ4+lo957iUJMM6Mpa6bL4UW0dxM6WmDSHuI4j/zOp1E2TDKImBGCJA9QPfc08PaNubA== + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + core-js-pure "^3.0.1" + ethereumjs-account "^3.0.0" + ethereumjs-block "^2.2.2" + ethereumjs-blockchain "^4.0.3" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.2" + ethereumjs-util "^6.2.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "^2.3.2" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + util.promisify "^1.0.0" + +ethereumjs-vm@^2.1.0, ethereumjs-vm@^2.3.4, ethereumjs-vm@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz#76243ed8de031b408793ac33907fb3407fe400c6" + integrity sha512-r/XIUik/ynGbxS3y+mvGnbOKnuLo40V5Mj1J25+HEO63aWYREIqvWeRO/hnROlMBE5WoniQmPmhiaN0ctiHaXw== + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + ethereumjs-account "^2.0.3" + ethereumjs-block "~2.2.0" + ethereumjs-common "^1.1.0" + ethereumjs-util "^6.0.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "^2.3.2" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + +ethereumjs-wallet@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/ethereumjs-wallet/-/ethereumjs-wallet-0.6.3.tgz#b0eae6f327637c2aeb9ccb9047b982ac542e6ab1" + integrity sha512-qiXPiZOsStem+Dj/CQHbn5qex+FVkuPmGH7SvSnA9F3tdRDt8dLMyvIj3+U05QzVZNPYh4HXEdnzoYI4dZkr9w== + dependencies: + aes-js "^3.1.1" + bs58check "^2.1.2" + ethereumjs-util "^6.0.0" + hdkey "^1.1.0" + randombytes "^2.0.6" + safe-buffer "^5.1.2" + scrypt.js "^0.3.0" + utf8 "^3.0.0" + uuid "^3.3.2" + +ethers@4.0.0-beta.3: + version "4.0.0-beta.3" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.0-beta.3.tgz#15bef14e57e94ecbeb7f9b39dd0a4bd435bc9066" + integrity sha512-YYPogooSknTwvHg3+Mv71gM/3Wcrx+ZpCzarBj3mqs9njjRkrOo2/eufzhHloOCo3JSoNI4TQJJ6yU5ABm3Uog== + dependencies: + "@types/node" "^10.3.2" + aes-js "3.0.0" + bn.js "^4.4.0" + elliptic "6.3.3" + hash.js "1.1.3" + js-sha3 "0.5.7" + scrypt-js "2.0.3" + setimmediate "1.0.4" + uuid "2.0.1" + xmlhttprequest "1.8.0" + +ethers@^4.0.0-beta.1, ethers@^4.0.32: + version "4.0.48" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.48.tgz#330c65b8133e112b0613156e57e92d9009d8fbbe" + integrity sha512-sZD5K8H28dOrcidzx9f8KYh8083n5BexIO3+SbE4jK83L85FxtpXZBCQdXb8gkg+7sBqomcLhhkU7UHL+F7I2g== + dependencies: + aes-js "3.0.0" + bn.js "^4.4.0" + elliptic "6.5.3" + hash.js "1.1.3" + js-sha3 "0.5.7" + scrypt-js "2.0.4" + setimmediate "1.0.4" + uuid "2.0.1" + xmlhttprequest "1.8.0" + +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk= + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + +ethjs-util@0.1.6, ethjs-util@^0.1.3: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +eventemitter3@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" + integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== + +eventemitter3@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" + integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== + +events@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" + integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +exorcist@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/exorcist/-/exorcist-1.0.1.tgz#79316e3c4885845490f7bb405c0e5b5db1167c52" + integrity sha1-eTFuPEiFhFSQ97tAXA5bXbEWfFI= + dependencies: + is-stream "~1.1.0" + minimist "0.0.5" + mkdirp "~0.5.1" + mold-source-map "~0.4.0" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expand-tilde@^2.0.0, expand-tilde@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" + integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= + dependencies: + homedir-polyfill "^1.0.1" + +express@^4.14.0: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext-list@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37" + integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA== + dependencies: + mime-db "^1.28.0" + +ext-name@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ext-name/-/ext-name-5.0.0.tgz#70781981d183ee15d13993c8822045c506c8f0a6" + integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== + dependencies: + ext-list "^2.0.0" + sort-keys-length "^1.0.0" + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@^3.0.0, extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fake-merkle-patricia-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz#4b8c3acfb520afadf9860b1f14cd8ce3402cddd3" + integrity sha1-S4w6z7Ugr635hgsfFM2M40As3dM= + dependencies: + checkpoint-store "^1.1.0" + +fancy-log@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7" + integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== + dependencies: + ansi-gray "^0.1.1" + color-support "^1.1.3" + parse-node-version "^1.0.0" + time-stamp "^1.0.0" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz#e6a754cc8f15e58987aa9cbd27af66fd6f4e5af9" + integrity sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk= + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + +fetch-ponyfill@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893" + integrity sha1-rjzl9zLGReq4fkroeTQUcJsjmJM= + dependencies: + node-fetch "~1.7.1" + +file-type@^3.8.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" + integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek= + +file-type@^4.2.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-4.4.0.tgz#1b600e5fca1fbdc6e80c0a70c71c8dba5f7906c5" + integrity sha1-G2AOX8ofvcboDApwxxyNul95BsU= + +file-type@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6" + integrity sha1-LdvqfHP/42No365J3DOMBYwritY= + +file-type@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919" + integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg== + +file-type@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-8.1.0.tgz#244f3b7ef641bbe0cca196c7276e4b332399f68c" + integrity sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ== + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +filename-reserved-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229" + integrity sha1-q/c9+rc10EVECr/qLZHzieu/oik= + +filenamify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-2.1.0.tgz#88faf495fb1b47abfd612300002a16228c677ee9" + integrity sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA== + dependencies: + filename-reserved-regex "^2.0.0" + strip-outer "^1.0.0" + trim-repeated "^1.0.0" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-up@3.0.0, find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +findup-sync@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" + integrity sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw= + dependencies: + detect-file "^1.0.0" + is-glob "^3.1.0" + micromatch "^3.0.4" + resolve-dir "^1.0.1" + +findup-sync@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" + integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== + dependencies: + detect-file "^1.0.0" + is-glob "^4.0.0" + micromatch "^3.0.4" + resolve-dir "^1.0.1" + +fined@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fined/-/fined-1.2.0.tgz#d00beccf1aa2b475d16d423b0238b713a2c4a37b" + integrity sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng== + dependencies: + expand-tilde "^2.0.2" + is-plain-object "^2.0.3" + object.defaults "^1.1.0" + object.pick "^1.2.0" + parse-filepath "^1.0.1" + +flagged-respawn@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.1.tgz#e7de6f1279ddd9ca9aac8a5971d618606b3aab41" + integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q== + +flat@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" + integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== + dependencies: + is-buffer "~2.0.3" + +flow-stoplight@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b" + integrity sha1-SiksW8/4s5+mzAyxqFPYbyfu/3s= + +flush-write-stream@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== + dependencies: + inherits "^2.0.3" + readable-stream "^2.3.6" + +for-each@~0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +for-in@^1.0.1, for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +for-own@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" + integrity sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs= + dependencies: + for-in "^1.0.1" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + +fp-ts@1.19.3: + version "1.19.3" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.3.tgz#261a60d1088fbff01f91256f91d21d0caaaaa96f" + integrity sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg== + +fp-ts@^1.0.0: + version "1.19.5" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.5.tgz#3da865e585dfa1fdfd51785417357ac50afc520a" + integrity sha512-wDNqTimnzs8QqpldiId9OavWK2NptormjXnRJTQecNjzwfyp6P/8s/zG8e4h3ja3oqkKaY72UlTjQYt/1yXf9A== + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +from2@^2.1.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + integrity sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A= + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + +fs-extra@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs-mkdirp-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz#0b7815fc3201c6a69e14db98ce098c16935259eb" + integrity sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes= + dependencies: + graceful-fs "^4.1.11" + through2 "^2.0.3" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^1.2.7: + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +fsevents@~2.1.1, fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + +function-bind@^1.1.1, function-bind@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1, functional-red-black-tree@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +ganache-cli@^6.1.0: + version "6.10.1" + resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.10.1.tgz#6e083a92dba204d649c43d8823152bb9e2219807" + integrity sha512-3lpBxILtJBxkNVo+U8ad2qbkzB6nZ53gXhXH6NiS0Pauqur86rGbhXz6fNASjBosZm9iJ8Nr71fJd331QODFkQ== + dependencies: + ethereumjs-util "6.1.0" + source-map-support "0.5.12" + yargs "13.2.4" + optionalDependencies: + scrypt "6.0.3" + +ganache-core@^2.7.0: + version "2.11.2" + resolved "https://registry.yarnpkg.com/ganache-core/-/ganache-core-2.11.2.tgz#821a8e7beaa65b32e408ccae2e2ec49194c4e378" + integrity sha512-yZSMdR2xtqG2IdApeB6OywtMxwcHMp6Y5TUBwPyKe5/GripP8xnEpSKBluhxoyqEotg+Z2S8mjIXJyAm+NnMGw== + dependencies: + abstract-leveldown "3.0.0" + async "2.6.2" + bip39 "2.5.0" + cachedown "1.0.0" + clone "2.1.2" + debug "3.2.6" + encoding-down "5.0.4" + eth-sig-util "2.3.0" + ethereumjs-abi "0.6.7" + ethereumjs-account "3.0.0" + ethereumjs-block "2.2.2" + ethereumjs-common "1.5.0" + ethereumjs-tx "2.1.2" + ethereumjs-util "6.2.0" + ethereumjs-vm "4.2.0" + heap "0.2.6" + level-sublevel "6.6.4" + levelup "3.1.1" + lodash "4.17.14" + lru-cache "5.1.1" + merkle-patricia-tree "2.3.2" + seedrandom "3.0.1" + source-map-support "0.5.12" + tmp "0.1.0" + web3-provider-engine "14.2.1" + websocket "1.0.29" + optionalDependencies: + ethereumjs-wallet "0.6.3" + web3 "1.2.4" + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + +get-proxy@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/get-proxy/-/get-proxy-2.1.0.tgz#349f2b4d91d44c4d4d4e9cba2ad90143fac5ef93" + integrity sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw== + dependencies: + npm-conf "^1.1.0" + +get-stream@3.0.0, get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= + +get-stream@^2.2.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" + integrity sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4= + dependencies: + object-assign "^4.0.1" + pinkie-promise "^2.0.0" + +get-stream@^4.0.0, get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + +glob-stream@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4" + integrity sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ= + dependencies: + extend "^3.0.0" + glob "^7.1.1" + glob-parent "^3.1.0" + is-negated-glob "^1.0.0" + ordered-read-streams "^1.0.0" + pumpify "^1.3.5" + readable-stream "^2.1.5" + remove-trailing-separator "^1.0.1" + to-absolute-glob "^2.0.0" + unique-stream "^2.0.2" + +glob-watcher@^5.0.3: + version "5.0.5" + resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-5.0.5.tgz#aa6bce648332924d9a8489be41e3e5c52d4186dc" + integrity sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw== + dependencies: + anymatch "^2.0.0" + async-done "^1.2.0" + chokidar "^2.0.0" + is-negated-glob "^1.0.0" + just-debounce "^1.0.0" + normalize-path "^3.0.0" + object.defaults "^1.1.0" + +glob@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@7.1.6, glob@^7.1.1, glob@^7.1.3, glob@~7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-dirs@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" + integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU= + dependencies: + ini "^1.3.4" + +global-modules@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" + integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== + dependencies: + global-prefix "^1.0.1" + is-windows "^1.0.1" + resolve-dir "^1.0.0" + +global-prefix@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" + integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= + dependencies: + expand-tilde "^2.0.2" + homedir-polyfill "^1.0.1" + ini "^1.3.4" + is-windows "^1.0.1" + which "^1.2.14" + +global@~4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" + integrity sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8= + dependencies: + min-document "^2.19.0" + process "~0.5.1" + +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== + +glogg@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.2.tgz#2d7dd702beda22eb3bffadf880696da6d846313f" + integrity sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA== + dependencies: + sparkles "^1.0.0" + +got@9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +got@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" + integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== + dependencies: + decompress-response "^3.2.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-plain-obj "^1.1.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^1.1.1" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + url-parse-lax "^1.0.0" + url-to-options "^1.0.1" + +got@^8.3.1: + version "8.3.2" + resolved "https://registry.yarnpkg.com/got/-/got-8.3.2.tgz#1d23f64390e97f776cac52e5b936e5f514d2e937" + integrity sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw== + dependencies: + "@sindresorhus/is" "^0.7.0" + cacheable-request "^2.1.1" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + into-stream "^3.1.0" + is-retry-allowed "^1.1.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + mimic-response "^1.0.0" + p-cancelable "^0.4.0" + p-timeout "^2.0.1" + pify "^3.0.0" + safe-buffer "^5.1.1" + timed-out "^4.0.1" + url-parse-lax "^3.0.0" + url-to-options "^1.0.1" + +graceful-fs@^4.0.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +gulp-cli@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-2.3.0.tgz#ec0d380e29e52aa45e47977f0d32e18fd161122f" + integrity sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A== + dependencies: + ansi-colors "^1.0.1" + archy "^1.0.0" + array-sort "^1.0.0" + color-support "^1.1.3" + concat-stream "^1.6.0" + copy-props "^2.0.1" + fancy-log "^1.3.2" + gulplog "^1.0.0" + interpret "^1.4.0" + isobject "^3.0.1" + liftoff "^3.1.0" + matchdep "^2.0.0" + mute-stdout "^1.0.0" + pretty-hrtime "^1.0.0" + replace-homedir "^1.0.0" + semver-greatest-satisfied-range "^1.1.0" + v8flags "^3.2.0" + yargs "^7.1.0" + +gulp@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/gulp/-/gulp-4.0.2.tgz#543651070fd0f6ab0a0650c6a3e6ff5a7cb09caa" + integrity sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA== + dependencies: + glob-watcher "^5.0.3" + gulp-cli "^2.2.0" + undertaker "^1.2.1" + vinyl-fs "^3.0.0" + +gulplog@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" + integrity sha1-4oxNRdBey77YGDY86PnFkmIp/+U= + dependencies: + glogg "^1.0.0" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbol-support-x@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" + integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== + +has-symbols@^1.0.0, has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== + dependencies: + has-symbol-support-x "^1.4.1" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.3, has@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.0" + +hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hdkey@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-1.1.2.tgz#c60f9cf6f90fbf24a8a52ea06893f36a0108cd3e" + integrity sha512-PTQ4VKu0oRnCrYfLp04iQZ7T2Cxz0UsEXYauk2j8eh6PJXCpbXuCFhOmtIFtbET0i3PMWmHN9J11gU8LEgUljQ== + dependencies: + bs58check "^2.1.2" + safe-buffer "^5.1.1" + secp256k1 "^3.0.1" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +heap@0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.6.tgz#087e1f10b046932fc8594dd9e6d378afc9d1e5ac" + integrity sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw= + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + integrity sha1-42w/LSyufXRqhX440Y1fMqeILbg= + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +homedir-polyfill@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" + integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== + dependencies: + parse-passwd "^1.0.0" + +hosted-git-info@^2.1.4: + version "2.8.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" + integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== + +http-cache-semantics@3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" + integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@1.7.3, http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-https@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" + integrity sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs= + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01" + integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +idna-uts46-hx@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" + integrity sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA== + dependencies: + punycode "2.1.0" + +ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + +immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" + integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== + +immediate@~3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" + integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ini@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +interpret@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + +into-stream@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6" + integrity sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY= + dependencies: + from2 "^2.1.1" + p-is-promise "^1.1.0" + +invariant@^2.2.2: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= + +invert-kv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== + +io-ts@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2" + integrity sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g== + dependencies: + fp-ts "^1.0.0" + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-absolute@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" + integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== + dependencies: + is-relative "^1.0.0" + is-windows "^1.0.1" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arguments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" + integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-buffer@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" + integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb" + integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw== + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + +is-fn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fn/-/is-fn-1.0.0.tgz#9543d5de7bcf5b08a22ec8a20bae6e286d510d8c" + integrity sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw= + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-function@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" + integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= + +is-installed-globally@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.2.0.tgz#8cde07ade508458b51f14bcda315ffaf4898de30" + integrity sha512-g3TzWCnR/eO4Q3abCwgFjOFw7uVOfxG4m8hMr/39Jcf2YvE5mHrFKqpyuraWV4zwx9XhjnVO4nY0ZI4llzl0Pg== + dependencies: + global-dirs "^0.1.1" + is-path-inside "^2.1.0" + +is-map@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" + integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw== + +is-natural-number@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" + integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg= + +is-negated-glob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" + integrity sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI= + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" + integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= + +is-path-inside@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" + integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== + dependencies: + path-is-inside "^1.0.2" + +is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-regex@^1.0.4, is-regex@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + dependencies: + has-symbols "^1.0.1" + +is-regex@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" + integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== + dependencies: + has "^1.0.3" + +is-relative@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" + integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== + dependencies: + is-unc-path "^1.0.0" + +is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" + integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== + +is-set@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" + integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA== + +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0, is-stream@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-string@^1.0.4, is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-unc-path@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" + integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== + dependencies: + unc-path-regex "^0.1.2" + +is-utf8@^0.2.0, is-utf8@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +is-valid-glob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa" + integrity sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao= + +is-windows@^1.0.1, is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + +iterate-iterator@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/iterate-iterator/-/iterate-iterator-1.0.1.tgz#1693a768c1ddd79c969051459453f082fe82e9f6" + integrity sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw== + +iterate-value@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/iterate-value/-/iterate-value-1.0.2.tgz#935115bd37d006a52046535ebc8d07e9c9337f57" + integrity sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ== + dependencies: + es-get-iterator "^1.0.2" + iterate-iterator "^1.0.1" + +js-sha3@0.5.7, js-sha3@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc= + +js-sha3@0.8.0, js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= + +js-yaml@3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-rpc-engine@^3.4.0, json-rpc-engine@^3.6.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.8.0.tgz#9d4ff447241792e1d0a232f6ef927302bb0c62a9" + integrity sha512-6QNcvm2gFuuK4TKU1uwfH0Qd/cOSb9c1lls0gbnIhciktIUQJwz6NQNAW4B1KiGPenv7IKu97V222Yo1bNhGuA== + dependencies: + async "^2.0.1" + babel-preset-env "^1.7.0" + babelify "^7.3.0" + json-rpc-error "^2.0.0" + promise-to-callback "^1.0.0" + safe-event-emitter "^1.0.1" + +json-rpc-error@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/json-rpc-error/-/json-rpc-error-2.0.0.tgz#a7af9c202838b5e905c7250e547f1aff77258a02" + integrity sha1-p6+cICg4tekFxyUOVH8a/3cligI= + dependencies: + inherits "^2.0.1" + +json-rpc-random-id@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz#ba49d96aded1444dbb8da3d203748acbbcdec8c8" + integrity sha1-uknZat7RRE27jaPSA3SKy7zeyMg= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= + +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +just-debounce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.0.0.tgz#87fccfaeffc0b68cd19d55f6722943f929ea35ea" + integrity sha1-h/zPrv/AtozRnVX2cilD+SnqNeo= + +keccak@^1.0.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-1.4.0.tgz#572f8a6dbee8e7b3aa421550f9e6408ca2186f80" + integrity sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw== + dependencies: + bindings "^1.2.1" + inherits "^2.0.3" + nan "^2.2.1" + safe-buffer "^5.1.0" + +keccak@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-2.1.0.tgz#734ea53f2edcfd0f42cdb8d5f4c358fef052752b" + integrity sha512-m1wbJRTo+gWbctZWay9i26v5fFnYkOn7D5PCxJ3fZUGUEb49dE1Pm4BREUYCt/aoO6di7jeoGmhvqN9Nzylm3Q== + dependencies: + bindings "^1.5.0" + inherits "^2.0.4" + nan "^2.14.0" + safe-buffer "^5.2.0" + +keccak@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.1.tgz#ae30a0e94dbe43414f741375cff6d64c8bea0bff" + integrity sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +keyv@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" + integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA== + dependencies: + json-buffer "3.0.0" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0, kind-of@^5.0.2: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= + optionalDependencies: + graceful-fs "^4.1.9" + +last-run@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/last-run/-/last-run-1.1.1.tgz#45b96942c17b1c79c772198259ba943bebf8ca5b" + integrity sha1-RblpQsF7HHnHchmCWbqUO+v4yls= + dependencies: + default-resolution "^2.0.0" + es6-weak-map "^2.0.1" + +lazystream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" + integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= + dependencies: + readable-stream "^2.0.5" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= + dependencies: + invert-kv "^1.0.0" + +lcid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== + dependencies: + invert-kv "^2.0.0" + +lead@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lead/-/lead-1.0.0.tgz#6f14f99a37be3a9dd784f5495690e5903466ee42" + integrity sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI= + dependencies: + flush-write-stream "^1.0.2" + +level-codec@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.2.tgz#fd60df8c64786a80d44e63423096ffead63d8cbc" + integrity sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ== + dependencies: + buffer "^5.6.0" + +level-codec@~7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-7.0.1.tgz#341f22f907ce0f16763f24bddd681e395a0fb8a7" + integrity sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ== + +level-errors@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.1.2.tgz#4399c2f3d3ab87d0625f7e3676e2d807deff404d" + integrity sha512-Sw/IJwWbPKF5Ai4Wz60B52yj0zYeqzObLh8k1Tk88jVmD51cJSKWSYpRyhVIvFzZdvsPqlH5wfhp/yxdsaQH4w== + dependencies: + errno "~0.1.1" + +level-errors@^2.0.0, level-errors@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-2.0.1.tgz#2132a677bf4e679ce029f517c2f17432800c05c8" + integrity sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw== + dependencies: + errno "~0.1.1" + +level-errors@~1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.0.5.tgz#83dbfb12f0b8a2516bdc9a31c4876038e227b859" + integrity sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig== + dependencies: + errno "~0.1.1" + +level-iterator-stream@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-2.0.3.tgz#ccfff7c046dcf47955ae9a86f46dfa06a31688b4" + integrity sha512-I6Heg70nfF+e5Y3/qfthJFexhRw/Gi3bIymCoXAlijZdAcLaPuWSJs3KXyTYf23ID6g0o2QF62Yh+grOXY3Rig== + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.5" + xtend "^4.0.0" + +level-iterator-stream@~1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz#e43b78b1a8143e6fa97a4f485eb8ea530352f2ed" + integrity sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0= + dependencies: + inherits "^2.0.1" + level-errors "^1.0.3" + readable-stream "^1.0.33" + xtend "^4.0.0" + +level-iterator-stream@~3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-3.0.1.tgz#2c98a4f8820d87cdacab3132506815419077c730" + integrity sha512-nEIQvxEED9yRThxvOrq8Aqziy4EGzrxSZK+QzEFAVuJvQ8glfyZ96GB6BoI4sBbLfjMXm2w4vu3Tkcm9obcY0g== + dependencies: + inherits "^2.0.1" + readable-stream "^2.3.6" + xtend "^4.0.0" + +level-mem@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/level-mem/-/level-mem-3.0.1.tgz#7ce8cf256eac40f716eb6489654726247f5a89e5" + integrity sha512-LbtfK9+3Ug1UmvvhR2DqLqXiPW1OJ5jEh0a3m9ZgAipiwpSxGj/qaVVy54RG5vAQN1nCuXqjvprCuKSCxcJHBg== + dependencies: + level-packager "~4.0.0" + memdown "~3.0.0" + +level-packager@~4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-4.0.1.tgz#7e7d3016af005be0869bc5fa8de93d2a7f56ffe6" + integrity sha512-svCRKfYLn9/4CoFfi+d8krOtrp6RoX8+xm0Na5cgXMqSyRru0AnDYdLl+YI8u1FyS6gGZ94ILLZDE5dh2but3Q== + dependencies: + encoding-down "~5.0.0" + levelup "^3.0.0" + +level-post@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/level-post/-/level-post-1.0.7.tgz#19ccca9441a7cc527879a0635000f06d5e8f27d0" + integrity sha512-PWYqG4Q00asOrLhX7BejSajByB4EmG2GaKHfj3h5UmmZ2duciXLPGYWIjBzLECFWUGOZWlm5B20h/n3Gs3HKew== + dependencies: + ltgt "^2.1.2" + +level-sublevel@6.6.4: + version "6.6.4" + resolved "https://registry.yarnpkg.com/level-sublevel/-/level-sublevel-6.6.4.tgz#f7844ae893919cd9d69ae19d7159499afd5352ba" + integrity sha512-pcCrTUOiO48+Kp6F1+UAzF/OtWqLcQVTVF39HLdZ3RO8XBoXt+XVPKZO1vVr1aUoxHZA9OtD2e1v7G+3S5KFDA== + dependencies: + bytewise "~1.1.0" + level-codec "^9.0.0" + level-errors "^2.0.0" + level-iterator-stream "^2.0.3" + ltgt "~2.1.1" + pull-defer "^0.2.2" + pull-level "^2.0.3" + pull-stream "^3.6.8" + typewiselite "~1.0.0" + xtend "~4.0.0" + +level-ws@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" + integrity sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos= + dependencies: + readable-stream "~1.0.15" + xtend "~2.1.1" + +level-ws@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-1.0.0.tgz#19a22d2d4ac57b18cc7c6ecc4bd23d899d8f603b" + integrity sha512-RXEfCmkd6WWFlArh3X8ONvQPm8jNpfA0s/36M4QzLqrLEIt1iJE9WBHLZ5vZJK6haMjJPJGJCQWfjMNnRcq/9Q== + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.8" + xtend "^4.0.1" + +levelup@3.1.1, levelup@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-3.1.1.tgz#c2c0b3be2b4dc316647c53b42e2f559e232d2189" + integrity sha512-9N10xRkUU4dShSRRFTBdNaBxofz+PGaIZO962ckboJZiNmLuhVT6FZ6ZKAsICKfUBO76ySaYU6fJWX/jnj3Lcg== + dependencies: + deferred-leveldown "~4.0.0" + level-errors "~2.0.0" + level-iterator-stream "~3.0.0" + xtend "~4.0.0" + +levelup@^1.2.1: + version "1.3.9" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-1.3.9.tgz#2dbcae845b2bb2b6bea84df334c475533bbd82ab" + integrity sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ== + dependencies: + deferred-leveldown "~1.2.1" + level-codec "~7.0.0" + level-errors "~1.0.3" + level-iterator-stream "~1.3.0" + prr "~1.0.1" + semver "~5.4.1" + xtend "~4.0.0" + +liftoff@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-3.1.0.tgz#c9ba6081f908670607ee79062d700df062c52ed3" + integrity sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog== + dependencies: + extend "^3.0.0" + findup-sync "^3.0.0" + fined "^1.0.1" + flagged-respawn "^1.0.0" + is-plain-object "^2.0.4" + object.map "^1.0.0" + rechoir "^0.6.2" + resolve "^1.1.7" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash@4.17.14: + version "4.17.14" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba" + integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw== + +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + +log-symbols@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" + integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== + dependencies: + chalk "^2.4.2" + +log-symbols@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== + dependencies: + chalk "^4.0.0" + +looper@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/looper/-/looper-2.0.0.tgz#66cd0c774af3d4fedac53794f742db56da8f09ec" + integrity sha1-Zs0Md0rz1P7axTeU90LbVtqPCew= + +looper@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/looper/-/looper-3.0.0.tgz#2efa54c3b1cbaba9b94aee2e5914b0be57fbb749" + integrity sha1-LvpUw7HLq6m5Su4uWRSwvlf7t0k= + +loose-envify@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lowercase-keys@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" + integrity sha1-TjNms55/VFfjXxMkvfb4jQv8cwY= + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@5.1.1, lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-3.2.0.tgz#71789b3b7f5399bec8565dda38aa30d2a097efee" + integrity sha1-cXibO39Tmb7IVl3aOKow0qCX7+4= + dependencies: + pseudomap "^1.0.1" + +lru_map@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" + integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0= + +ltgt@^2.1.2, ltgt@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= + +ltgt@~2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.1.3.tgz#10851a06d9964b971178441c23c9e52698eece34" + integrity sha1-EIUaBtmWS5cReEQcI8nlJpjuzjQ= + +make-dir@^1.0.0, make-dir@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== + dependencies: + pify "^3.0.0" + +make-iterator@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" + integrity sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw== + dependencies: + kind-of "^6.0.2" + +map-age-cleaner@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + +map-cache@^0.2.0, map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +matchdep@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e" + integrity sha1-xvNINKDY28OzfCfui7yyfHd1WC4= + dependencies: + findup-sync "^2.0.0" + micromatch "^3.0.4" + resolve "^1.4.0" + stack-trace "0.0.10" + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +mem@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" + integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== + dependencies: + map-age-cleaner "^0.1.1" + mimic-fn "^2.0.0" + p-is-promise "^2.0.0" + +memdown@^1.0.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-1.4.1.tgz#b4e4e192174664ffbae41361aa500f3119efe215" + integrity sha1-tOThkhdGZP+65BNhqlAPMRnv4hU= + dependencies: + abstract-leveldown "~2.7.1" + functional-red-black-tree "^1.0.1" + immediate "^3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.1.1" + +memdown@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-3.0.0.tgz#93aca055d743b20efc37492e9e399784f2958309" + integrity sha512-tbV02LfZMWLcHcq4tw++NuqMO+FZX8tNJEiD2aNRm48ZZusVg5N8NART+dmBkepJVye986oixErf7jfXboMGMA== + dependencies: + abstract-leveldown "~5.0.0" + functional-red-black-tree "~1.0.1" + immediate "~3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.1.1" + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merkle-patricia-tree@2.3.2, merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz#982ca1b5a0fde00eed2f6aeed1f9152860b8208a" + integrity sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g== + dependencies: + async "^1.4.2" + ethereumjs-util "^5.0.0" + level-ws "0.0.0" + levelup "^1.2.1" + memdown "^1.0.0" + readable-stream "^2.0.0" + rlp "^2.0.0" + semaphore ">=1.0.1" + +merkle-patricia-tree@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-3.0.0.tgz#448d85415565df72febc33ca362b8b614f5a58f8" + integrity sha512-soRaMuNf/ILmw3KWbybaCjhx86EYeBbD8ph0edQCTed0JN/rxDt1EBN52Ajre3VyGo+91f8+/rfPIRQnnGMqmQ== + dependencies: + async "^2.6.1" + ethereumjs-util "^5.2.0" + level-mem "^3.0.1" + level-ws "^1.0.0" + readable-stream "^3.0.6" + rlp "^2.0.0" + semaphore ">=1.0.1" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.44.0, mime-db@^1.28.0: + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + +mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + dependencies: + dom-walk "^0.1.0" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.5.tgz#d7aa327bcecf518f9106ac6b8f003fa3bcea8566" + integrity sha1-16oye87PUY+RBqxrjwA/o7zqhWY= + +minimist@^1.2.5, minimist@~1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp-promise@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + integrity sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE= + dependencies: + mkdirp "*" + +mkdirp@*: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@0.5.5, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mocha@8.1.2: + version "8.1.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.1.2.tgz#d67fad13300e4f5cd48135a935ea566f96caf827" + integrity sha512-I8FRAcuACNMLQn3lS4qeWLxXqLvGf6r2CaLstDpZmMUUSmvW6Cnm1AuHxgbc7ctZVRcfwspCRbDHymPsi3dkJw== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.4.2" + debug "4.1.1" + diff "4.0.2" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.1.6" + growl "1.10.5" + he "1.2.0" + js-yaml "3.14.0" + log-symbols "4.0.0" + minimatch "3.0.4" + ms "2.1.2" + object.assign "4.1.0" + promise.allsettled "1.0.2" + serialize-javascript "4.0.0" + strip-json-comments "3.0.1" + supports-color "7.1.0" + which "2.0.2" + wide-align "1.1.3" + workerpool "6.0.0" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.1" + +mocha@^7.1.2: + version "7.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.2.0.tgz#01cc227b00d875ab1eed03a75106689cfed5a604" + integrity sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ== + dependencies: + ansi-colors "3.2.3" + browser-stdout "1.3.1" + chokidar "3.3.0" + debug "3.2.6" + diff "3.5.0" + escape-string-regexp "1.0.5" + find-up "3.0.0" + glob "7.1.3" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "3.0.0" + minimatch "3.0.4" + mkdirp "0.5.5" + ms "2.1.1" + node-environment-flags "1.0.6" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.0" + +mock-fs@^4.1.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.13.0.tgz#31c02263673ec3789f90eb7b6963676aa407a598" + integrity sha512-DD0vOdofJdoaRNtnWcrXe6RQbpHkPPmtqGq14uRX0F8ZKJ5nv89CVTYl/BZdppDxBDaV0hl75htg3abpEWlPZA== + +mold-source-map@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/mold-source-map/-/mold-source-map-0.4.0.tgz#cf67e0b31c47ab9badb5c9c25651862127bb8317" + integrity sha1-z2fgsxxHq5uttcnCVlGGISe7gxc= + dependencies: + convert-source-map "^1.1.0" + through "~2.2.7" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@2.1.2, ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +multibase@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.7.0.tgz#1adfc1c50abe05eefeb5091ac0c2728d6b84581b" + integrity sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multibase@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.6.1.tgz#b76df6298536cc17b9f6a6db53ec88f85f8cc12b" + integrity sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multicodec@^0.5.5: + version "0.5.7" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-0.5.7.tgz#1fb3f9dd866a10a55d226e194abba2dcc1ee9ffd" + integrity sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA== + dependencies: + varint "^5.0.0" + +multicodec@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-1.0.4.tgz#46ac064657c40380c28367c90304d8ed175a714f" + integrity sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg== + dependencies: + buffer "^5.6.0" + varint "^5.0.0" + +multihashes@^0.4.15, multihashes@~0.4.15: + version "0.4.21" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-0.4.21.tgz#dc02d525579f334a7909ade8a122dabb58ccfcb5" + integrity sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw== + dependencies: + buffer "^5.5.0" + multibase "^0.7.0" + varint "^5.0.0" + +mute-stdout@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.1.tgz#acb0300eb4de23a7ddeec014e3e96044b3472331" + integrity sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg== + +nan@^2.0.8, nan@^2.11.0, nan@^2.12.1, nan@^2.14.0, nan@^2.2.1: + version "2.14.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" + integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== + +nano-json-stream-parser@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" + integrity sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18= + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-environment-flags@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" + integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw== + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + +node-fetch@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5" + integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U= + +node-fetch@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" + integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + +node-fetch@~1.7.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +node-gyp-build@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" + integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== + +node-gyp-build@~3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.7.0.tgz#daa77a4f547b9aed3e2aac779eaf151afd60ec8d" + integrity sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w== + +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-url@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" + integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== + dependencies: + prepend-http "^2.0.0" + query-string "^5.0.1" + sort-keys "^2.0.0" + +normalize-url@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + +now-and-later@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-2.0.1.tgz#8e579c8685764a7cc02cb680380e94f43ccb1f7c" + integrity sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ== + dependencies: + once "^1.3.2" + +npm-conf@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" + integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== + dependencies: + config-chain "^1.1.11" + pify "^3.0.0" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA= + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4, object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" + integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== + +object-inspect@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" + integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== + +object-is@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6" + integrity sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-keys@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" + integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@4.1.0, object.assign@^4.0.4, object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.defaults@^1.0.0, object.defaults@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" + integrity sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8= + dependencies: + array-each "^1.0.1" + array-slice "^1.0.0" + for-own "^1.0.0" + isobject "^3.0.0" + +object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" + integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +object.map@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" + integrity sha1-z4Plncj8wK1fQlDh94s7gb2AHTc= + dependencies: + for-own "^1.0.0" + make-iterator "^1.0.0" + +object.pick@^1.2.0, object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +object.reduce@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.reduce/-/object.reduce-1.0.1.tgz#6fe348f2ac7fa0f95ca621226599096825bb03ad" + integrity sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60= + dependencies: + for-own "^1.0.0" + make-iterator "^1.0.0" + +oboe@2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.4.tgz#20c88cdb0c15371bb04119257d4fdd34b0aa49f6" + integrity sha1-IMiM2wwVNxuwQRklfU/dNLCqSfY= + dependencies: + http-https "^1.0.0" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +ordered-read-streams@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" + integrity sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4= + dependencies: + readable-stream "^2.0.1" + +original-require@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/original-require/-/original-require-1.0.1.tgz#0f130471584cd33511c5ec38c8d59213f9ac5e20" + integrity sha1-DxMEcVhM0zURxew4yNWSE/msXiA= + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= + dependencies: + lcid "^1.0.0" + +os-locale@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" + integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== + dependencies: + execa "^1.0.0" + lcid "^2.0.0" + mem "^4.0.0" + +os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== + +p-cancelable@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0" + integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ== + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= + +p-event@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/p-event/-/p-event-2.3.1.tgz#596279ef169ab2c3e0cae88c1cfbb08079993ef6" + integrity sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA== + dependencies: + p-timeout "^2.0.1" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-is-promise@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" + integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4= + +p-is-promise@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe" + integrity sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg== + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-timeout@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" + integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= + dependencies: + p-finally "^1.0.0" + +p-timeout@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" + integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== + dependencies: + p-finally "^1.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@^1.0.4: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-filepath@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" + integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= + dependencies: + is-absolute "^1.0.0" + map-cache "^0.2.0" + path-root "^0.1.1" + +parse-headers@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.3.tgz#5e8e7512383d140ba02f0c7aa9f49b4399c92515" + integrity sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA== + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-node-version@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" + integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== + +parse-passwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-root-regex@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" + integrity sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= + +path-root@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" + integrity sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= + dependencies: + path-root-regex "^0.1.0" + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +pathval@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" + integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= + +pbkdf2@^3.0.17, pbkdf2@^3.0.3, pbkdf2@^3.0.9: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94" + integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + +pify@^2.0.0, pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +precond@0.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" + integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +pretty-hrtime@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" + integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= + +private@^0.1.6, private@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + +process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@~0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" + integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8= + +promise-to-callback@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/promise-to-callback/-/promise-to-callback-1.0.0.tgz#5d2a749010bfb67d963598fcd3960746a68feef7" + integrity sha1-XSp0kBC/tn2WNZj805YHRqaP7vc= + dependencies: + is-fn "^1.0.0" + set-immediate-shim "^1.0.1" + +promise.allsettled@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/promise.allsettled/-/promise.allsettled-1.0.2.tgz#d66f78fbb600e83e863d893e98b3d4376a9c47c9" + integrity sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg== + dependencies: + array.prototype.map "^1.0.1" + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + iterate-value "^1.0.0" + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= + +proxy-addr@~2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +pseudomap@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pull-cat@^1.1.9: + version "1.1.11" + resolved "https://registry.yarnpkg.com/pull-cat/-/pull-cat-1.1.11.tgz#b642dd1255da376a706b6db4fa962f5fdb74c31b" + integrity sha1-tkLdElXaN2pwa220+pYvX9t0wxs= + +pull-defer@^0.2.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/pull-defer/-/pull-defer-0.2.3.tgz#4ee09c6d9e227bede9938db80391c3dac489d113" + integrity sha512-/An3KE7mVjZCqNhZsr22k1Tx8MACnUnHZZNPSJ0S62td8JtYr/AiRG42Vz7Syu31SoTLUzVIe61jtT/pNdjVYA== + +pull-level@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pull-level/-/pull-level-2.0.4.tgz#4822e61757c10bdcc7cf4a03af04c92734c9afac" + integrity sha512-fW6pljDeUThpq5KXwKbRG3X7Ogk3vc75d5OQU/TvXXui65ykm+Bn+fiktg+MOx2jJ85cd+sheufPL+rw9QSVZg== + dependencies: + level-post "^1.0.7" + pull-cat "^1.1.9" + pull-live "^1.0.1" + pull-pushable "^2.0.0" + pull-stream "^3.4.0" + pull-window "^2.1.4" + stream-to-pull-stream "^1.7.1" + +pull-live@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/pull-live/-/pull-live-1.0.1.tgz#a4ecee01e330155e9124bbbcf4761f21b38f51f5" + integrity sha1-pOzuAeMwFV6RJLu89HYfIbOPUfU= + dependencies: + pull-cat "^1.1.9" + pull-stream "^3.4.0" + +pull-pushable@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/pull-pushable/-/pull-pushable-2.2.0.tgz#5f2f3aed47ad86919f01b12a2e99d6f1bd776581" + integrity sha1-Xy867UethpGfAbEqLpnW8b13ZYE= + +pull-stream@^3.2.3, pull-stream@^3.4.0, pull-stream@^3.6.8: + version "3.6.14" + resolved "https://registry.yarnpkg.com/pull-stream/-/pull-stream-3.6.14.tgz#529dbd5b86131f4a5ed636fdf7f6af00781357ee" + integrity sha512-KIqdvpqHHaTUA2mCYcLG1ibEbu/LCKoJZsBWyv9lSYtPkJPBq8m3Hxa103xHi6D2thj5YXa0TqK3L3GUkwgnew== + +pull-window@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/pull-window/-/pull-window-2.1.4.tgz#fc3b86feebd1920c7ae297691e23f705f88552f0" + integrity sha1-/DuG/uvRkgx64pdpHiP3BfiFUvA= + dependencies: + looper "^2.0.0" + +pump@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.5: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" + integrity sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@^6.7.0: + version "6.9.4" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" + integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.0.6, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +randomhex@0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/randomhex/-/randomhex-0.1.5.tgz#baceef982329091400f2a2912c6cd02f1094f585" + integrity sha1-us7vmCMpCRQA8qKRLGzQLxCU9YU= + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +raw-body@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" + integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== + dependencies: + bytes "3.1.0" + http-errors "1.7.3" + iconv-lite "0.4.24" + unpipe "1.0.0" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +readable-stream@^1.0.33: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.8, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@~1.0.15: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +readdirp@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" + integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ== + dependencies: + picomatch "^2.0.4" + +readdirp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" + integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= + dependencies: + resolve "^1.1.6" + +regenerate@^1.2.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.1.tgz#cad92ad8e6b591773485fbe05a485caf4f457e6f" + integrity sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A== + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + +regenerator-transform@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" + integrity sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q== + dependencies: + babel-runtime "^6.18.0" + babel-types "^6.19.0" + private "^0.1.6" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexp.prototype.flags@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" + integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +regexpu-core@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" + integrity sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA= + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc= + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw= + dependencies: + jsesc "~0.5.0" + +remove-bom-buffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53" + integrity sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ== + dependencies: + is-buffer "^1.1.5" + is-utf8 "^0.2.1" + +remove-bom-stream@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz#05f1a593f16e42e1fb90ebf59de8e569525f9523" + integrity sha1-BfGlk/FuQuH7kOv1nejlaVJflSM= + dependencies: + remove-bom-buffer "^3.0.0" + safe-buffer "^5.1.0" + through2 "^2.0.3" + +remove-trailing-separator@^1.0.1, remove-trailing-separator@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + +replace-ext@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" + integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== + +replace-homedir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/replace-homedir/-/replace-homedir-1.0.0.tgz#e87f6d513b928dde808260c12be7fec6ff6e798c" + integrity sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw= + dependencies: + homedir-polyfill "^1.0.1" + is-absolute "^1.0.0" + remove-trailing-separator "^1.1.0" + +request@^2.79.0, request@^2.85.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-from-string@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-dir@^1.0.0, resolve-dir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" + integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= + dependencies: + expand-tilde "^2.0.0" + global-modules "^1.0.0" + +resolve-options@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-1.1.0.tgz#32bb9e39c06d67338dc9378c0d6d6074566ad131" + integrity sha1-MrueOcBtZzONyTeMDW1gdFZq0TE= + dependencies: + value-or-function "^3.0.0" + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.4.0, resolve@~1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + +responselike@1.0.2, responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +resumer@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759" + integrity sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k= + dependencies: + through "~2.3.4" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +rimraf@^2.2.8, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.0.0, rlp@^2.2.1, rlp@^2.2.2, rlp@^2.2.3, rlp@^2.2.4: + version "2.2.6" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.6.tgz#c80ba6266ac7a483ef1e69e8e2f056656de2fb2c" + integrity sha512-HAfAmL6SDYNWPUOJNrM500x4Thn4PZsEy5pijPh40U9WfNk0z15hUYzO9xVIMAdIHdFtD8CBDHd75Td1g36Mjg== + dependencies: + bn.js "^4.11.1" + +rustbn.js@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" + integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-event-emitter@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" + integrity sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg== + dependencies: + events "^3.0.0" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +scrypt-js@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.3.tgz#bb0040be03043da9a012a2cea9fc9f852cfc87d4" + integrity sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q= + +scrypt-js@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" + integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw== + +scrypt-js@^3.0.0, scrypt-js@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + +scrypt.js@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/scrypt.js/-/scrypt.js-0.3.0.tgz#6c62d61728ad533c8c376a2e5e3e86d41a95c4c0" + integrity sha512-42LTc1nyFsyv/o0gcHtDztrn+aqpkaCNt5Qh7ATBZfhEZU7IC/0oT/qbBH+uRNoAPvs2fwiOId68FDEoSRA8/A== + dependencies: + scryptsy "^1.2.1" + optionalDependencies: + scrypt "^6.0.2" + +scrypt@6.0.3, scrypt@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/scrypt/-/scrypt-6.0.3.tgz#04e014a5682b53fa50c2d5cce167d719c06d870d" + integrity sha1-BOAUpWgrU/pQwtXM4WfXGcBthw0= + dependencies: + nan "^2.0.8" + +scryptsy@2.1.0, scryptsy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-2.1.0.tgz#8d1e8d0c025b58fdd25b6fa9a0dc905ee8faa790" + integrity sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w== + +scryptsy@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-1.2.1.tgz#a3225fa4b2524f802700761e2855bdf3b2d92163" + integrity sha1-oyJfpLJST4AnAHYeKFW987LZIWM= + dependencies: + pbkdf2 "^3.0.3" + +secp256k1@^3.0.1: + version "3.8.0" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.8.0.tgz#28f59f4b01dbee9575f56a47034b7d2e3b3b352d" + integrity sha512-k5ke5avRZbtl9Tqx/SA7CbY3NF6Ro+Sj9cZxezFzuBlLDmyqPiL8hJJ+EmzD8Ig4LUDByHJ3/iPOVoRixs/hmw== + dependencies: + bindings "^1.5.0" + bip66 "^1.1.5" + bn.js "^4.11.8" + create-hash "^1.2.0" + drbg.js "^1.0.1" + elliptic "^6.5.2" + nan "^2.14.0" + safe-buffer "^5.1.2" + +secp256k1@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" + integrity sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg== + dependencies: + elliptic "^6.5.2" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +seedrandom@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.1.tgz#eb3dde015bcf55df05a233514e5df44ef9dce083" + integrity sha512-1/02Y/rUeU1CJBAGLebiC5Lbo5FnB22gQbIFFYTLkwvp1xdABZJH1sn4ZT1MzXmPpzv+Rf/Lu2NcsLJiK4rcDg== + +seek-bzip@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4" + integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ== + dependencies: + commander "^2.8.1" + +semaphore@>=1.0.1, semaphore@^1.0.3, semaphore@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" + integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA== + +semver-greatest-satisfied-range@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz#13e8c2658ab9691cb0cd71093240280d36f77a5b" + integrity sha1-E+jCZYq5aRywzXEJMkAoDTb3els= + dependencies: + sver-compat "^1.5.0" + +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.7.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.2.0.tgz#4d813d9590aaf8a9192693d6c85b9344de5901db" + integrity sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A== + +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@~5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serialize-javascript@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +servify@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" + integrity sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw== + dependencies: + body-parser "^1.16.0" + cors "^2.8.1" + express "^4.14.0" + request "^2.79.0" + xhr "^2.3.3" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.4.tgz#20e81de622d4a02588ce0c8da8973cbcf1d3138f" + integrity sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48= + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +signal-exit@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^2.7.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" + integrity sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw== + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +solc@0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.6.8.tgz#accf03634554938e166ba9b9853d17ca5c728131" + integrity sha512-7URBAisWVjO7dwWNpEkQ5dpRSpSF4Wm0aD5EB82D5BQKh+q7jhOxhgkG4K5gax/geM0kPZUAxnaLcgl2ZXBgMQ== + dependencies: + command-exists "^1.2.8" + commander "3.0.2" + fs-extra "^0.30.0" + js-sha3 "0.8.0" + memorystream "^0.3.1" + require-from-string "^2.0.0" + semver "^5.5.0" + tmp "0.0.33" + +sort-keys-length@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188" + integrity sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg= + dependencies: + sort-keys "^1.0.0" + +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= + dependencies: + is-plain-obj "^1.0.0" + +sort-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= + dependencies: + is-plain-obj "^1.0.0" + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@0.5.12: + version "0.5.12" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" + integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== + dependencies: + source-map "^0.5.6" + +source-map-support@^0.5.13, source-map-support@^0.5.16, source-map-support@^0.5.19: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@^0.5.6, source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sparkles@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" + integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw== + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" + integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stack-trace@0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +stream-exhaust@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stream-exhaust/-/stream-exhaust-1.0.2.tgz#acdac8da59ef2bc1e17a2c0ccf6c320d120e555d" + integrity sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw== + +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +stream-to-pull-stream@^1.7.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/stream-to-pull-stream/-/stream-to-pull-stream-1.7.3.tgz#4161aa2d2eb9964de60bfa1af7feaf917e874ece" + integrity sha512-6sNyqJpr5dIOQdgNy/xcDWwDuzAsAwVzhzrWlAPAQ7Lkjx/rv0wgvxEyKwTq6FmNd5rjTrELt/CLmaSw7crMGg== + dependencies: + looper "^3.0.0" + pull-stream "^3.2.3" + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + +string-width@^1.0.1, string-width@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string.prototype.trim@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz#141233dff32c82bfad80684d7e5f0869ee0fb782" + integrity sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + +string.prototype.trimend@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" + integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.trimstart@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" + integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-dirs@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" + integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g== + dependencies: + is-natural-number "^4.0.1" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha1-DF8VX+8RUTczd96du1iNoFUA428= + dependencies: + is-hex-prefixed "1.0.0" + +strip-json-comments@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +strip-json-comments@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" + integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== + +strip-outer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" + integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg== + dependencies: + escape-string-regexp "^1.0.2" + +supports-color@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" + integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== + dependencies: + has-flag "^3.0.0" + +supports-color@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +sver-compat@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/sver-compat/-/sver-compat-1.5.0.tgz#3cf87dfeb4d07b4a3f14827bc186b3fd0c645cd8" + integrity sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg= + dependencies: + es6-iterator "^2.0.1" + es6-symbol "^3.1.1" + +swarm-js@0.1.39: + version "0.1.39" + resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.39.tgz#79becb07f291d4b2a178c50fee7aa6e10342c0e8" + integrity sha512-QLMqL2rzF6n5s50BptyD6Oi0R1aWlJC5Y17SRIVXRj6OR1DRIPM7nepvrxxkjA1zNzFz6mUOMjfeqeDaWB7OOg== + dependencies: + bluebird "^3.5.0" + buffer "^5.0.5" + decompress "^4.0.0" + eth-lib "^0.1.26" + fs-extra "^4.0.2" + got "^7.1.0" + mime-types "^2.1.16" + mkdirp-promise "^5.0.1" + mock-fs "^4.1.0" + setimmediate "^1.0.5" + tar "^4.0.2" + xhr-request-promise "^0.1.2" + +swarm-js@^0.1.40: + version "0.1.40" + resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.40.tgz#b1bc7b6dcc76061f6c772203e004c11997e06b99" + integrity sha512-yqiOCEoA4/IShXkY3WKwP5PvZhmoOOD8clsKA7EEcRILMkTEYHCQ21HDCAcVpmIxZq4LyZvWeRJ6quIyHk1caA== + dependencies: + bluebird "^3.5.0" + buffer "^5.0.5" + eth-lib "^0.1.26" + fs-extra "^4.0.2" + got "^7.1.0" + mime-types "^2.1.16" + mkdirp-promise "^5.0.1" + mock-fs "^4.1.0" + setimmediate "^1.0.5" + tar "^4.0.2" + xhr-request "^1.0.1" + +tape@^4.6.3: + version "4.13.3" + resolved "https://registry.yarnpkg.com/tape/-/tape-4.13.3.tgz#51b3d91c83668c7a45b1a594b607dee0a0b46278" + integrity sha512-0/Y20PwRIUkQcTCSi4AASs+OANZZwqPKaipGCEwp10dQMipVvSZwUUCi01Y/OklIGyHKFhIcjock+DKnBfLAFw== + dependencies: + deep-equal "~1.1.1" + defined "~1.0.0" + dotignore "~0.1.2" + for-each "~0.3.3" + function-bind "~1.1.1" + glob "~7.1.6" + has "~1.0.3" + inherits "~2.0.4" + is-regex "~1.0.5" + minimist "~1.2.5" + object-inspect "~1.7.0" + resolve "~1.17.0" + resumer "~0.0.0" + string.prototype.trim "~1.2.1" + through "~2.3.8" + +tar-stream@^1.5.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" + integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== + dependencies: + bl "^1.0.0" + buffer-alloc "^1.2.0" + end-of-stream "^1.0.0" + fs-constants "^1.0.0" + readable-stream "^2.3.0" + to-buffer "^1.1.1" + xtend "^4.0.0" + +tar@^4.0.2: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + +through2-filter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254" + integrity sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA== + dependencies: + through2 "~2.0.0" + xtend "~4.0.0" + +through2@^2.0.0, through2@^2.0.3, through2@~2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through@^2.3.8, through@~2.3.4, through@~2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +through@~2.2.7: + version "2.2.7" + resolved "https://registry.yarnpkg.com/through/-/through-2.2.7.tgz#6e8e21200191d4eb6a99f6f010df46aa1c6eb2bd" + integrity sha1-bo4hIAGR1OtqmfbwEN9Gqhxusr0= + +time-stamp@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" + integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= + +timed-out@^4.0.0, timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= + +tmp@0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tmp@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" + integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== + dependencies: + rimraf "^2.6.3" + +to-absolute-glob@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1865f43d9e74b0822db9f145b78cff7d0f7c849b" + integrity sha1-GGX0PZ50sIItufFFt4z/fQ98hJs= + dependencies: + is-absolute "^1.0.0" + is-negated-glob "^1.0.0" + +to-buffer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" + integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +to-through@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-through/-/to-through-2.0.0.tgz#fc92adaba072647bc0b67d6b03664aa195093af6" + integrity sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY= + dependencies: + through2 "^2.0.3" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +trim-repeated@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" + integrity sha1-42RqLqTokTEr9+rObPsFOAvAHCE= + dependencies: + escape-string-regexp "^1.0.2" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= + +truffle@^5.1.42: + version "5.1.42" + resolved "https://registry.yarnpkg.com/truffle/-/truffle-5.1.42.tgz#ef8a73807121c3cd11039315b29609ea34e76b64" + integrity sha512-kGNGU9YNasCjumaC67HaqEzAHeMMjN+q4WpBI54s2zoEvgbkLm6yNyL9arp7iy3niAZhG0OJleTRq0PmuCOpNw== + dependencies: + app-module-path "^2.2.0" + mocha "8.1.2" + original-require "1.0.1" + +truffle@^5.1.43: + version "5.1.43" + resolved "https://registry.yarnpkg.com/truffle/-/truffle-5.1.43.tgz#544e7b0955b6728a00761a86555c1eb259313266" + integrity sha512-KXda/70RAG9TBdQta8JEwVQmL9r/AZzU++5aZkrF4/nDosund8SV1yM9CcDTib4xLWuMaB15YyOC5r163QdLAw== + dependencies: + app-module-path "^2.2.0" + mocha "8.1.2" + original-require "1.0.1" + +ts-essentials@^2.0.7: + version "2.0.12" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-2.0.12.tgz#c9303f3d74f75fa7528c3d49b80e089ab09d8745" + integrity sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w== + +ts-interface-checker@^0.1.9: + version "0.1.13" + resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" + integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== + +tslib@^1.9.3: + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== + +tsort@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tsort/-/tsort-0.0.1.tgz#e2280f5e817f8bf4275657fd0f9aebd44f5a2786" + integrity sha1-4igPXoF/i/QnVlf9D5rr1E9aJ4Y= + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl-util@^0.15.0: + version "0.15.1" + resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" + integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +tweetnacl@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.1.0.tgz#9bdc22c648cf8cf86dd23d32336a41cfb6475e3f" + integrity sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +typewise-core@^1.2, typewise-core@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/typewise-core/-/typewise-core-1.2.0.tgz#97eb91805c7f55d2f941748fa50d315d991ef195" + integrity sha1-l+uRgFx/VdL5QXSPpQ0xXZke8ZU= + +typewise@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typewise/-/typewise-1.0.3.tgz#1067936540af97937cc5dcf9922486e9fa284651" + integrity sha1-EGeTZUCvl5N8xdz5kiSG6fooRlE= + dependencies: + typewise-core "^1.2.0" + +typewiselite@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typewiselite/-/typewiselite-1.0.0.tgz#c8882fa1bb1092c06005a97f34ef5c8508e3664e" + integrity sha1-yIgvobsQksBgBal/NO9chQjjZk4= + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== + +unbzip2-stream@^1.0.9: + version "1.4.3" + resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" + integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== + dependencies: + buffer "^5.2.1" + through "^2.3.8" + +unc-path-regex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" + integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= + +underscore@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" + integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== + +underscore@^1.8.3: + version "1.11.0" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.11.0.tgz#dd7c23a195db34267186044649870ff1bab5929e" + integrity sha512-xY96SsN3NA461qIRKZ/+qox37YXPtSBswMGfiNptr+wrt6ds4HaMw23TP612fEyGekRE6LNRiLYr/aqbHXNedw== + +undertaker-registry@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/undertaker-registry/-/undertaker-registry-1.0.1.tgz#5e4bda308e4a8a2ae584f9b9a4359a499825cc50" + integrity sha1-XkvaMI5KiirlhPm5pDWaSZglzFA= + +undertaker@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/undertaker/-/undertaker-1.3.0.tgz#363a6e541f27954d5791d6fa3c1d321666f86d18" + integrity sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg== + dependencies: + arr-flatten "^1.0.1" + arr-map "^2.0.0" + bach "^1.0.0" + collection-map "^1.0.0" + es6-weak-map "^2.0.1" + fast-levenshtein "^1.0.0" + last-run "^1.1.0" + object.defaults "^1.0.0" + object.reduce "^1.0.0" + undertaker-registry "^1.0.0" + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +unique-stream@^2.0.2: + version "2.3.1" + resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac" + integrity sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A== + dependencies: + json-stable-stringify-without-jsonify "^1.0.1" + through2-filter "^3.0.0" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unorm@^1.3.3: + version "1.6.0" + resolved "https://registry.yarnpkg.com/unorm/-/unorm-1.6.0.tgz#029b289661fba714f1a9af439eb51d9b16c205af" + integrity sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +uri-js@^4.2.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= + dependencies: + prepend-http "^1.0.1" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url-set-query@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" + integrity sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk= + +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +utf-8-validate@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.2.tgz#63cfbccd85dc1f2b66cf7a1d0eebc08ed056bfb3" + integrity sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw== + dependencies: + node-gyp-build "~3.7.0" + +utf8@3.0.0, utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util.promisify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" + integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.2" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.0" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac" + integrity sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w= + +uuid@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +v8flags@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz#b243e3b4dfd731fa774e7492128109a0fe66d656" + integrity sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg== + dependencies: + homedir-polyfill "^1.0.1" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +value-or-function@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" + integrity sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM= + +varint@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.0.tgz#d826b89f7490732fabc0c0ed693ed475dcb29ebf" + integrity sha1-2Ca4n3SQcy+rwMDtaT7Uddyynr8= + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vinyl-fs@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7" + integrity sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng== + dependencies: + fs-mkdirp-stream "^1.0.0" + glob-stream "^6.1.0" + graceful-fs "^4.0.0" + is-valid-glob "^1.0.0" + lazystream "^1.0.0" + lead "^1.0.0" + object.assign "^4.0.4" + pumpify "^1.3.5" + readable-stream "^2.3.3" + remove-bom-buffer "^3.0.0" + remove-bom-stream "^1.2.0" + resolve-options "^1.1.0" + through2 "^2.0.0" + to-through "^2.0.0" + value-or-function "^3.0.0" + vinyl "^2.0.0" + vinyl-sourcemap "^1.1.0" + +vinyl-sourcemap@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz#92a800593a38703a8cdb11d8b300ad4be63b3e16" + integrity sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY= + dependencies: + append-buffer "^1.0.2" + convert-source-map "^1.5.0" + graceful-fs "^4.1.6" + normalize-path "^2.1.1" + now-and-later "^2.0.0" + remove-bom-buffer "^3.0.0" + vinyl "^2.0.0" + +vinyl@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.0.tgz#d85b07da96e458d25b2ffe19fece9f2caa13ed86" + integrity sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg== + dependencies: + clone "^2.1.1" + clone-buffer "^1.0.0" + clone-stats "^1.0.0" + cloneable-readable "^1.0.0" + remove-trailing-separator "^1.0.1" + replace-ext "^1.0.0" + +web3-bzz@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.2.1.tgz#c3bd1e8f0c02a13cd6d4e3c3e9e1713f144f6f0d" + integrity sha512-LdOO44TuYbGIPfL4ilkuS89GQovxUpmLz6C1UC7VYVVRILeZS740FVB3j9V4P4FHUk1RenaDfKhcntqgVCHtjw== + dependencies: + got "9.6.0" + swarm-js "0.1.39" + underscore "1.9.1" + +web3-bzz@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.2.11.tgz#41bc19a77444bd5365744596d778b811880f707f" + integrity sha512-XGpWUEElGypBjeFyUhTkiPXFbDVD6Nr/S5jznE3t8cWUA0FxRf1n3n/NuIZeb0H9RkN2Ctd/jNma/k8XGa3YKg== + dependencies: + "@types/node" "^12.12.6" + got "9.6.0" + swarm-js "^0.1.40" + underscore "1.9.1" + +web3-bzz@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.2.4.tgz#a4adb7a8cba3d260de649bdb1f14ed359bfb3821" + integrity sha512-MqhAo/+0iQSMBtt3/QI1rU83uvF08sYq8r25+OUZ+4VtihnYsmkkca+rdU0QbRyrXY2/yGIpI46PFdh0khD53A== + dependencies: + "@types/node" "^10.12.18" + got "9.6.0" + swarm-js "0.1.39" + underscore "1.9.1" + +web3-core-helpers@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.2.1.tgz#f5f32d71c60a4a3bd14786118e633ce7ca6d5d0d" + integrity sha512-Gx3sTEajD5r96bJgfuW377PZVFmXIH4TdqDhgGwd2lZQCcMi+DA4TgxJNJGxn0R3aUVzyyE76j4LBrh412mXrw== + dependencies: + underscore "1.9.1" + web3-eth-iban "1.2.1" + web3-utils "1.2.1" + +web3-core-helpers@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.2.11.tgz#84c681ed0b942c0203f3b324a245a127e8c67a99" + integrity sha512-PEPoAoZd5ME7UfbnCZBdzIerpe74GEvlwT4AjOmHeCVZoIFk7EqvOZDejJHt+feJA6kMVTdd0xzRNN295UhC1A== + dependencies: + underscore "1.9.1" + web3-eth-iban "1.2.11" + web3-utils "1.2.11" + +web3-core-helpers@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.2.4.tgz#ffd425861f4d66b3f38df032afdb39ea0971fc0f" + integrity sha512-U7wbsK8IbZvF3B7S+QMSNP0tni/6VipnJkB0tZVEpHEIV2WWeBHYmZDnULWcsS/x/jn9yKhJlXIxWGsEAMkjiw== + dependencies: + underscore "1.9.1" + web3-eth-iban "1.2.4" + web3-utils "1.2.4" + +web3-core-method@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.2.1.tgz#9df1bafa2cd8be9d9937e01c6a47fc768d15d90a" + integrity sha512-Ghg2WS23qi6Xj8Od3VCzaImLHseEA7/usvnOItluiIc5cKs00WYWsNy2YRStzU9a2+z8lwQywPYp0nTzR/QXdQ== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.1" + web3-core-promievent "1.2.1" + web3-core-subscriptions "1.2.1" + web3-utils "1.2.1" + +web3-core-method@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.2.11.tgz#f880137d1507a0124912bf052534f168b8d8fbb6" + integrity sha512-ff0q76Cde94HAxLDZ6DbdmKniYCQVtvuaYh+rtOUMB6kssa5FX0q3vPmixi7NPooFnbKmmZCM6NvXg4IreTPIw== + dependencies: + "@ethersproject/transactions" "^5.0.0-beta.135" + underscore "1.9.1" + web3-core-helpers "1.2.11" + web3-core-promievent "1.2.11" + web3-core-subscriptions "1.2.11" + web3-utils "1.2.11" + +web3-core-method@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.2.4.tgz#a0fbc50b8ff5fd214021435cc2c6d1e115807aed" + integrity sha512-8p9kpL7di2qOVPWgcM08kb+yKom0rxRCMv6m/K+H+yLSxev9TgMbCgMSbPWAHlyiF3SJHw7APFKahK5Z+8XT5A== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.4" + web3-core-promievent "1.2.4" + web3-core-subscriptions "1.2.4" + web3-utils "1.2.4" + +web3-core-promievent@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.2.1.tgz#003e8a3eb82fb27b6164a6d5b9cad04acf733838" + integrity sha512-IVUqgpIKoeOYblwpex4Hye6npM0aMR+kU49VP06secPeN0rHMyhGF0ZGveWBrGvf8WDPI7jhqPBFIC6Jf3Q3zw== + dependencies: + any-promise "1.3.0" + eventemitter3 "3.1.2" + +web3-core-promievent@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.2.11.tgz#51fe97ca0ddec2f99bf8c3306a7a8e4b094ea3cf" + integrity sha512-il4McoDa/Ox9Agh4kyfQ8Ak/9ABYpnF8poBLL33R/EnxLsJOGQG2nZhkJa3I067hocrPSjEdlPt/0bHXsln4qA== + dependencies: + eventemitter3 "4.0.4" + +web3-core-promievent@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.2.4.tgz#75e5c0f2940028722cdd21ba503ebd65272df6cb" + integrity sha512-gEUlm27DewUsfUgC3T8AxkKi8Ecx+e+ZCaunB7X4Qk3i9F4C+5PSMGguolrShZ7Zb6717k79Y86f3A00O0VAZw== + dependencies: + any-promise "1.3.0" + eventemitter3 "3.1.2" + +web3-core-requestmanager@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.2.1.tgz#fa2e2206c3d738db38db7c8fe9c107006f5c6e3d" + integrity sha512-xfknTC69RfYmLKC+83Jz73IC3/sS2ZLhGtX33D4Q5nQ8yc39ElyAolxr9sJQS8kihOcM6u4J+8gyGMqsLcpIBg== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.1" + web3-providers-http "1.2.1" + web3-providers-ipc "1.2.1" + web3-providers-ws "1.2.1" + +web3-core-requestmanager@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.2.11.tgz#fe6eb603fbaee18530293a91f8cf26d8ae28c45a" + integrity sha512-oFhBtLfOiIbmfl6T6gYjjj9igOvtyxJ+fjS+byRxiwFJyJ5BQOz4/9/17gWR1Cq74paTlI7vDGxYfuvfE/mKvA== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.11" + web3-providers-http "1.2.11" + web3-providers-ipc "1.2.11" + web3-providers-ws "1.2.11" + +web3-core-requestmanager@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.2.4.tgz#0a7020a23fb91c6913c611dfd3d8c398d1e4b4a8" + integrity sha512-eZJDjyNTDtmSmzd3S488nR/SMJtNnn/GuwxnMh3AzYCqG3ZMfOylqTad2eYJPvc2PM5/Gj1wAMQcRpwOjjLuPg== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.4" + web3-providers-http "1.2.4" + web3-providers-ipc "1.2.4" + web3-providers-ws "1.2.4" + +web3-core-subscriptions@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.2.1.tgz#8c2368a839d4eec1c01a4b5650bbeb82d0e4a099" + integrity sha512-nmOwe3NsB8V8UFsY1r+sW6KjdOS68h8nuh7NzlWxBQT/19QSUGiERRTaZXWu5BYvo1EoZRMxCKyCQpSSXLc08g== + dependencies: + eventemitter3 "3.1.2" + underscore "1.9.1" + web3-core-helpers "1.2.1" + +web3-core-subscriptions@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.2.11.tgz#beca908fbfcb050c16f45f3f0f4c205e8505accd" + integrity sha512-qEF/OVqkCvQ7MPs1JylIZCZkin0aKK9lDxpAtQ1F8niEDGFqn7DT8E/vzbIa0GsOjL2fZjDhWJsaW+BSoAW1gg== + dependencies: + eventemitter3 "4.0.4" + underscore "1.9.1" + web3-core-helpers "1.2.11" + +web3-core-subscriptions@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.2.4.tgz#0dc095b5cfd82baa527a39796e3515a846b21b99" + integrity sha512-3D607J2M8ymY9V+/WZq4MLlBulwCkwEjjC2U+cXqgVO1rCyVqbxZNCmHyNYHjDDCxSEbks9Ju5xqJxDSxnyXEw== + dependencies: + eventemitter3 "3.1.2" + underscore "1.9.1" + web3-core-helpers "1.2.4" + +web3-core@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.2.1.tgz#7278b58fb6495065e73a77efbbce781a7fddf1a9" + integrity sha512-5ODwIqgl8oIg/0+Ai4jsLxkKFWJYE0uLuE1yUKHNVCL4zL6n3rFjRMpKPokd6id6nJCNgeA64KdWQ4XfpnjdMg== + dependencies: + web3-core-helpers "1.2.1" + web3-core-method "1.2.1" + web3-core-requestmanager "1.2.1" + web3-utils "1.2.1" + +web3-core@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.2.11.tgz#1043cacc1becb80638453cc5b2a14be9050288a7" + integrity sha512-CN7MEYOY5ryo5iVleIWRE3a3cZqVaLlIbIzDPsvQRUfzYnvzZQRZBm9Mq+ttDi2STOOzc1MKylspz/o3yq/LjQ== + dependencies: + "@types/bn.js" "^4.11.5" + "@types/node" "^12.12.6" + bignumber.js "^9.0.0" + web3-core-helpers "1.2.11" + web3-core-method "1.2.11" + web3-core-requestmanager "1.2.11" + web3-utils "1.2.11" + +web3-core@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.2.4.tgz#2df13b978dcfc59c2abaa887d27f88f21ad9a9d6" + integrity sha512-CHc27sMuET2cs1IKrkz7xzmTdMfZpYswe7f0HcuyneTwS1yTlTnHyqjAaTy0ZygAb/x4iaVox+Gvr4oSAqSI+A== + dependencies: + "@types/bignumber.js" "^5.0.0" + "@types/bn.js" "^4.11.4" + "@types/node" "^12.6.1" + web3-core-helpers "1.2.4" + web3-core-method "1.2.4" + web3-core-requestmanager "1.2.4" + web3-utils "1.2.4" + +web3-eth-abi@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.1.tgz#9b915b1c9ebf82f70cca631147035d5419064689" + integrity sha512-jI/KhU2a/DQPZXHjo2GW0myEljzfiKOn+h1qxK1+Y9OQfTcBMxrQJyH5AP89O6l6NZ1QvNdq99ThAxBFoy5L+g== + dependencies: + ethers "4.0.0-beta.3" + underscore "1.9.1" + web3-utils "1.2.1" + +web3-eth-abi@1.2.11, web3-eth-abi@^1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.11.tgz#a887494e5d447c2926d557a3834edd66e17af9b0" + integrity sha512-PkRYc0+MjuLSgg03QVWqWlQivJqRwKItKtEpRUaxUAeLE7i/uU39gmzm2keHGcQXo3POXAbOnMqkDvOep89Crg== + dependencies: + "@ethersproject/abi" "5.0.0-beta.153" + underscore "1.9.1" + web3-utils "1.2.11" + +web3-eth-abi@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.4.tgz#5b73e5ef70b03999227066d5d1310b168845e2b8" + integrity sha512-8eLIY4xZKoU3DSVu1pORluAw9Ru0/v4CGdw5so31nn+7fR8zgHMgwbFe0aOqWQ5VU42PzMMXeIJwt4AEi2buFg== + dependencies: + ethers "4.0.0-beta.3" + underscore "1.9.1" + web3-utils "1.2.4" + +web3-eth-abi@1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.5.tgz#7ffddd3a3e7bacd66a2186e5e388310786b9b548" + integrity sha512-Tz6AjGTlgZVpv01h2YgotoXoQAQgWacx82Zh72ZlZ4iBCs4SoiYvq6tfbW9pquylK2Egm23bELsrSSENz0204w== + dependencies: + ethers "4.0.0-beta.3" + underscore "1.9.1" + web3-utils "1.2.5" + +web3-eth-accounts@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.2.1.tgz#2741a8ef337a7219d57959ac8bd118b9d68d63cf" + integrity sha512-26I4qq42STQ8IeKUyur3MdQ1NzrzCqPsmzqpux0j6X/XBD7EjZ+Cs0lhGNkSKH5dI3V8CJasnQ5T1mNKeWB7nQ== + dependencies: + any-promise "1.3.0" + crypto-browserify "3.12.0" + eth-lib "0.2.7" + scryptsy "2.1.0" + semver "6.2.0" + underscore "1.9.1" + uuid "3.3.2" + web3-core "1.2.1" + web3-core-helpers "1.2.1" + web3-core-method "1.2.1" + web3-utils "1.2.1" + +web3-eth-accounts@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.2.11.tgz#a9e3044da442d31903a7ce035a86d8fa33f90520" + integrity sha512-6FwPqEpCfKIh3nSSGeo3uBm2iFSnFJDfwL3oS9pyegRBXNsGRVpgiW63yhNzL0796StsvjHWwQnQHsZNxWAkGw== + dependencies: + crypto-browserify "3.12.0" + eth-lib "0.2.8" + ethereumjs-common "^1.3.2" + ethereumjs-tx "^2.1.1" + scrypt-js "^3.0.1" + underscore "1.9.1" + uuid "3.3.2" + web3-core "1.2.11" + web3-core-helpers "1.2.11" + web3-core-method "1.2.11" + web3-utils "1.2.11" + +web3-eth-accounts@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.2.4.tgz#ada6edc49542354328a85cafab067acd7f88c288" + integrity sha512-04LzT/UtWmRFmi4hHRewP5Zz43fWhuHiK5XimP86sUQodk/ByOkXQ3RoXyGXFMNoRxdcAeRNxSfA2DpIBc9xUw== + dependencies: + "@web3-js/scrypt-shim" "^0.1.0" + any-promise "1.3.0" + crypto-browserify "3.12.0" + eth-lib "0.2.7" + ethereumjs-common "^1.3.2" + ethereumjs-tx "^2.1.1" + underscore "1.9.1" + uuid "3.3.2" + web3-core "1.2.4" + web3-core-helpers "1.2.4" + web3-core-method "1.2.4" + web3-utils "1.2.4" + +web3-eth-contract@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.2.1.tgz#3542424f3d341386fd9ff65e78060b85ac0ea8c4" + integrity sha512-kYFESbQ3boC9bl2rYVghj7O8UKMiuKaiMkxvRH5cEDHil8V7MGEGZNH0slSdoyeftZVlaWSMqkRP/chfnKND0g== + dependencies: + underscore "1.9.1" + web3-core "1.2.1" + web3-core-helpers "1.2.1" + web3-core-method "1.2.1" + web3-core-promievent "1.2.1" + web3-core-subscriptions "1.2.1" + web3-eth-abi "1.2.1" + web3-utils "1.2.1" + +web3-eth-contract@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.2.11.tgz#917065902bc27ce89da9a1da26e62ef663663b90" + integrity sha512-MzYuI/Rq2o6gn7vCGcnQgco63isPNK5lMAan2E51AJLknjSLnOxwNY3gM8BcKoy4Z+v5Dv00a03Xuk78JowFow== + dependencies: + "@types/bn.js" "^4.11.5" + underscore "1.9.1" + web3-core "1.2.11" + web3-core-helpers "1.2.11" + web3-core-method "1.2.11" + web3-core-promievent "1.2.11" + web3-core-subscriptions "1.2.11" + web3-eth-abi "1.2.11" + web3-utils "1.2.11" + +web3-eth-contract@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.2.4.tgz#68ef7cc633232779b0a2c506a810fbe903575886" + integrity sha512-b/9zC0qjVetEYnzRA1oZ8gF1OSSUkwSYi5LGr4GeckLkzXP7osEnp9lkO/AQcE4GpG+l+STnKPnASXJGZPgBRQ== + dependencies: + "@types/bn.js" "^4.11.4" + underscore "1.9.1" + web3-core "1.2.4" + web3-core-helpers "1.2.4" + web3-core-method "1.2.4" + web3-core-promievent "1.2.4" + web3-core-subscriptions "1.2.4" + web3-eth-abi "1.2.4" + web3-utils "1.2.4" + +web3-eth-ens@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.2.1.tgz#a0e52eee68c42a8b9865ceb04e5fb022c2d971d5" + integrity sha512-lhP1kFhqZr2nnbu3CGIFFrAnNxk2veXpOXBY48Tub37RtobDyHijHgrj+xTh+mFiPokyrapVjpFsbGa+Xzye4Q== + dependencies: + eth-ens-namehash "2.0.8" + underscore "1.9.1" + web3-core "1.2.1" + web3-core-helpers "1.2.1" + web3-core-promievent "1.2.1" + web3-eth-abi "1.2.1" + web3-eth-contract "1.2.1" + web3-utils "1.2.1" + +web3-eth-ens@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.2.11.tgz#26d4d7f16d6cbcfff918e39832b939edc3162532" + integrity sha512-dbW7dXP6HqT1EAPvnniZVnmw6TmQEKF6/1KgAxbo8iBBYrVTMDGFQUUnZ+C4VETGrwwaqtX4L9d/FrQhZ6SUiA== + dependencies: + content-hash "^2.5.2" + eth-ens-namehash "2.0.8" + underscore "1.9.1" + web3-core "1.2.11" + web3-core-helpers "1.2.11" + web3-core-promievent "1.2.11" + web3-eth-abi "1.2.11" + web3-eth-contract "1.2.11" + web3-utils "1.2.11" + +web3-eth-ens@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.2.4.tgz#b95b3aa99fb1e35c802b9e02a44c3046a3fa065e" + integrity sha512-g8+JxnZlhdsCzCS38Zm6R/ngXhXzvc3h7bXlxgKU4coTzLLoMpgOAEz71GxyIJinWTFbLXk/WjNY0dazi9NwVw== + dependencies: + eth-ens-namehash "2.0.8" + underscore "1.9.1" + web3-core "1.2.4" + web3-core-helpers "1.2.4" + web3-core-promievent "1.2.4" + web3-eth-abi "1.2.4" + web3-eth-contract "1.2.4" + web3-utils "1.2.4" + +web3-eth-iban@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.2.1.tgz#2c3801718946bea24e9296993a975c80b5acf880" + integrity sha512-9gkr4QPl1jCU+wkgmZ8EwODVO3ovVj6d6JKMos52ggdT2YCmlfvFVF6wlGLwi0VvNa/p+0BjJzaqxnnG/JewjQ== + dependencies: + bn.js "4.11.8" + web3-utils "1.2.1" + +web3-eth-iban@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.2.11.tgz#f5f73298305bc7392e2f188bf38a7362b42144ef" + integrity sha512-ozuVlZ5jwFC2hJY4+fH9pIcuH1xP0HEFhtWsR69u9uDIANHLPQQtWYmdj7xQ3p2YT4bQLq/axKhZi7EZVetmxQ== + dependencies: + bn.js "^4.11.9" + web3-utils "1.2.11" + +web3-eth-iban@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.2.4.tgz#8e0550fd3fd8e47a39357d87fe27dee9483ee476" + integrity sha512-D9HIyctru/FLRpXakRwmwdjb5bWU2O6UE/3AXvRm6DCOf2e+7Ve11qQrPtaubHfpdW3KWjDKvlxV9iaFv/oTMQ== + dependencies: + bn.js "4.11.8" + web3-utils "1.2.4" + +web3-eth-personal@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.2.1.tgz#244e9911b7b482dc17c02f23a061a627c6e47faf" + integrity sha512-RNDVSiaSoY4aIp8+Hc7z+X72H7lMb3fmAChuSBADoEc7DsJrY/d0R5qQDK9g9t2BO8oxgLrLNyBP/9ub2Hc6Bg== + dependencies: + web3-core "1.2.1" + web3-core-helpers "1.2.1" + web3-core-method "1.2.1" + web3-net "1.2.1" + web3-utils "1.2.1" + +web3-eth-personal@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.2.11.tgz#a38b3942a1d87a62070ce0622a941553c3d5aa70" + integrity sha512-42IzUtKq9iHZ8K9VN0vAI50iSU9tOA1V7XU2BhF/tb7We2iKBVdkley2fg26TxlOcKNEHm7o6HRtiiFsVK4Ifw== + dependencies: + "@types/node" "^12.12.6" + web3-core "1.2.11" + web3-core-helpers "1.2.11" + web3-core-method "1.2.11" + web3-net "1.2.11" + web3-utils "1.2.11" + +web3-eth-personal@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.2.4.tgz#3224cca6851c96347d9799b12c1b67b2a6eb232b" + integrity sha512-5Russ7ZECwHaZXcN3DLuLS7390Vzgrzepl4D87SD6Sn1DHsCZtvfdPIYwoTmKNp69LG3mORl7U23Ga5YxqkICw== + dependencies: + "@types/node" "^12.6.1" + web3-core "1.2.4" + web3-core-helpers "1.2.4" + web3-core-method "1.2.4" + web3-net "1.2.4" + web3-utils "1.2.4" + +web3-eth@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.2.1.tgz#b9989e2557c73a9e8ffdc107c6dafbe72c79c1b0" + integrity sha512-/2xly4Yry5FW1i+uygPjhfvgUP/MS/Dk+PDqmzp5M88tS86A+j8BzKc23GrlA8sgGs0645cpZK/999LpEF5UdA== + dependencies: + underscore "1.9.1" + web3-core "1.2.1" + web3-core-helpers "1.2.1" + web3-core-method "1.2.1" + web3-core-subscriptions "1.2.1" + web3-eth-abi "1.2.1" + web3-eth-accounts "1.2.1" + web3-eth-contract "1.2.1" + web3-eth-ens "1.2.1" + web3-eth-iban "1.2.1" + web3-eth-personal "1.2.1" + web3-net "1.2.1" + web3-utils "1.2.1" + +web3-eth@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.2.11.tgz#4c81fcb6285b8caf544058fba3ae802968fdc793" + integrity sha512-REvxW1wJ58AgHPcXPJOL49d1K/dPmuw4LjPLBPStOVkQjzDTVmJEIsiLwn2YeuNDd4pfakBwT8L3bz1G1/wVsQ== + dependencies: + underscore "1.9.1" + web3-core "1.2.11" + web3-core-helpers "1.2.11" + web3-core-method "1.2.11" + web3-core-subscriptions "1.2.11" + web3-eth-abi "1.2.11" + web3-eth-accounts "1.2.11" + web3-eth-contract "1.2.11" + web3-eth-ens "1.2.11" + web3-eth-iban "1.2.11" + web3-eth-personal "1.2.11" + web3-net "1.2.11" + web3-utils "1.2.11" + +web3-eth@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.2.4.tgz#24c3b1f1ac79351bbfb808b2ab5c585fa57cdd00" + integrity sha512-+j+kbfmZsbc3+KJpvHM16j1xRFHe2jBAniMo1BHKc3lho6A8Sn9Buyut6odubguX2AxoRArCdIDCkT9hjUERpA== + dependencies: + underscore "1.9.1" + web3-core "1.2.4" + web3-core-helpers "1.2.4" + web3-core-method "1.2.4" + web3-core-subscriptions "1.2.4" + web3-eth-abi "1.2.4" + web3-eth-accounts "1.2.4" + web3-eth-contract "1.2.4" + web3-eth-ens "1.2.4" + web3-eth-iban "1.2.4" + web3-eth-personal "1.2.4" + web3-net "1.2.4" + web3-utils "1.2.4" + +web3-net@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.2.1.tgz#edd249503315dd5ab4fa00220f6509d95bb7ab10" + integrity sha512-Yt1Bs7WgnLESPe0rri/ZoPWzSy55ovioaP35w1KZydrNtQ5Yq4WcrAdhBzcOW7vAkIwrsLQsvA+hrOCy7mNauw== + dependencies: + web3-core "1.2.1" + web3-core-method "1.2.1" + web3-utils "1.2.1" + +web3-net@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.2.11.tgz#eda68ef25e5cdb64c96c39085cdb74669aabbe1b" + integrity sha512-sjrSDj0pTfZouR5BSTItCuZ5K/oZPVdVciPQ6981PPPIwJJkCMeVjD7I4zO3qDPCnBjBSbWvVnLdwqUBPtHxyg== + dependencies: + web3-core "1.2.11" + web3-core-method "1.2.11" + web3-utils "1.2.11" + +web3-net@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.2.4.tgz#1d246406d3aaffbf39c030e4e98bce0ca5f25458" + integrity sha512-wKOsqhyXWPSYTGbp7ofVvni17yfRptpqoUdp3SC8RAhDmGkX6irsiT9pON79m6b3HUHfLoBilFQyt/fTUZOf7A== + dependencies: + web3-core "1.2.4" + web3-core-method "1.2.4" + web3-utils "1.2.4" + +web3-provider-engine@14.2.1: + version "14.2.1" + resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-14.2.1.tgz#ef351578797bf170e08d529cb5b02f8751329b95" + integrity sha512-iSv31h2qXkr9vrL6UZDm4leZMc32SjWJFGOp/D92JXfcEboCqraZyuExDkpxKw8ziTufXieNM7LSXNHzszYdJw== + dependencies: + async "^2.5.0" + backoff "^2.5.0" + clone "^2.0.0" + cross-fetch "^2.1.0" + eth-block-tracker "^3.0.0" + eth-json-rpc-infura "^3.1.0" + eth-sig-util "^1.4.2" + ethereumjs-block "^1.2.2" + ethereumjs-tx "^1.2.0" + ethereumjs-util "^5.1.5" + ethereumjs-vm "^2.3.4" + json-rpc-error "^2.0.0" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + readable-stream "^2.2.9" + request "^2.85.0" + semaphore "^1.0.3" + ws "^5.1.1" + xhr "^2.2.0" + xtend "^4.0.1" + +web3-providers-http@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.2.1.tgz#c93ea003a42e7b894556f7e19dd3540f947f5013" + integrity sha512-BDtVUVolT9b3CAzeGVA/np1hhn7RPUZ6YYGB/sYky+GjeO311Yoq8SRDUSezU92x8yImSC2B+SMReGhd1zL+bQ== + dependencies: + web3-core-helpers "1.2.1" + xhr2-cookies "1.1.0" + +web3-providers-http@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.2.11.tgz#1cd03442c61670572d40e4dcdf1faff8bd91e7c6" + integrity sha512-psh4hYGb1+ijWywfwpB2cvvOIMISlR44F/rJtYkRmQ5jMvG4FOCPlQJPiHQZo+2cc3HbktvvSJzIhkWQJdmvrA== + dependencies: + web3-core-helpers "1.2.11" + xhr2-cookies "1.1.0" + +web3-providers-http@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.2.4.tgz#514fcad71ae77832c2c15574296282fbbc5f4a67" + integrity sha512-dzVCkRrR/cqlIrcrWNiPt9gyt0AZTE0J+MfAu9rR6CyIgtnm1wFUVVGaxYRxuTGQRO4Dlo49gtoGwaGcyxqiTw== + dependencies: + web3-core-helpers "1.2.4" + xhr2-cookies "1.1.0" + +web3-providers-ipc@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.2.1.tgz#017bfc687a8fc5398df2241eb98f135e3edd672c" + integrity sha512-oPEuOCwxVx8L4CPD0TUdnlOUZwGBSRKScCz/Ws2YHdr9Ium+whm+0NLmOZjkjQp5wovQbyBzNa6zJz1noFRvFA== + dependencies: + oboe "2.1.4" + underscore "1.9.1" + web3-core-helpers "1.2.1" + +web3-providers-ipc@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.2.11.tgz#d16d6c9be1be6e0b4f4536c4acc16b0f4f27ef21" + integrity sha512-yhc7Y/k8hBV/KlELxynWjJDzmgDEDjIjBzXK+e0rHBsYEhdCNdIH5Psa456c+l0qTEU2YzycF8VAjYpWfPnBpQ== + dependencies: + oboe "2.1.4" + underscore "1.9.1" + web3-core-helpers "1.2.11" + +web3-providers-ipc@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.2.4.tgz#9d6659f8d44943fb369b739f48df09092be459bd" + integrity sha512-8J3Dguffin51gckTaNrO3oMBo7g+j0UNk6hXmdmQMMNEtrYqw4ctT6t06YOf9GgtOMjSAc1YEh3LPrvgIsR7og== + dependencies: + oboe "2.1.4" + underscore "1.9.1" + web3-core-helpers "1.2.4" + +web3-providers-ws@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.2.1.tgz#2d941eaf3d5a8caa3214eff8dc16d96252b842cb" + integrity sha512-oqsQXzu+ejJACVHy864WwIyw+oB21nw/pI65/sD95Zi98+/HQzFfNcIFneF1NC4bVF3VNX4YHTNq2I2o97LAiA== + dependencies: + underscore "1.9.1" + web3-core-helpers "1.2.1" + websocket "github:web3-js/WebSocket-Node#polyfill/globalThis" + +web3-providers-ws@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.2.11.tgz#a1dfd6d9778d840561d9ec13dd453046451a96bb" + integrity sha512-ZxnjIY1Er8Ty+cE4migzr43zA/+72AF1myzsLaU5eVgdsfV7Jqx7Dix1hbevNZDKFlSoEyq/3j/jYalh3So1Zg== + dependencies: + eventemitter3 "4.0.4" + underscore "1.9.1" + web3-core-helpers "1.2.11" + websocket "^1.0.31" + +web3-providers-ws@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.2.4.tgz#099ee271ee03f6ea4f5df9cfe969e83f4ce0e36f" + integrity sha512-F/vQpDzeK+++oeeNROl1IVTufFCwCR2hpWe5yRXN0ApLwHqXrMI7UwQNdJ9iyibcWjJf/ECbauEEQ8CHgE+MYQ== + dependencies: + "@web3-js/websocket" "^1.0.29" + underscore "1.9.1" + web3-core-helpers "1.2.4" + +web3-shh@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.2.1.tgz#4460e3c1e07faf73ddec24ccd00da46f89152b0c" + integrity sha512-/3Cl04nza5kuFn25bV3FJWa0s3Vafr5BlT933h26xovQ6HIIz61LmvNQlvX1AhFL+SNJOTcQmK1SM59vcyC8bA== + dependencies: + web3-core "1.2.1" + web3-core-method "1.2.1" + web3-core-subscriptions "1.2.1" + web3-net "1.2.1" + +web3-shh@1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.2.11.tgz#f5d086f9621c9a47e98d438010385b5f059fd88f" + integrity sha512-B3OrO3oG1L+bv3E1sTwCx66injW1A8hhwpknDUbV+sw3fehFazA06z9SGXUefuFI1kVs4q2vRi0n4oCcI4dZDg== + dependencies: + web3-core "1.2.11" + web3-core-method "1.2.11" + web3-core-subscriptions "1.2.11" + web3-net "1.2.11" + +web3-shh@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.2.4.tgz#5c8ff5ab624a3b14f08af0d24d2b16c10e9f70dd" + integrity sha512-z+9SCw0dE+69Z/Hv8809XDbLj7lTfEv9Sgu8eKEIdGntZf4v7ewj5rzN5bZZSz8aCvfK7Y6ovz1PBAu4QzS4IQ== + dependencies: + web3-core "1.2.4" + web3-core-method "1.2.4" + web3-core-subscriptions "1.2.4" + web3-net "1.2.4" + +web3-utils@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.1.tgz#21466e38291551de0ab34558de21512ac4274534" + integrity sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA== + dependencies: + bn.js "4.11.8" + eth-lib "0.2.7" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randomhex "0.1.5" + underscore "1.9.1" + utf8 "3.0.0" + +web3-utils@1.2.11, web3-utils@^1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.11.tgz#af1942aead3fb166ae851a985bed8ef2c2d95a82" + integrity sha512-3Tq09izhD+ThqHEaWYX4VOT7dNPdZiO+c/1QMA0s5X2lDFKK/xHJb7cyTRRVzN2LvlHbR7baS1tmQhSua51TcQ== + dependencies: + bn.js "^4.11.9" + eth-lib "0.2.8" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + underscore "1.9.1" + utf8 "3.0.0" + +web3-utils@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.4.tgz#96832a39a66b05bf8862a5b0bdad2799d709d951" + integrity sha512-+S86Ip+jqfIPQWvw2N/xBQq5JNqCO0dyvukGdJm8fEWHZbckT4WxSpHbx+9KLEWY4H4x9pUwnoRkK87pYyHfgQ== + dependencies: + bn.js "4.11.8" + eth-lib "0.2.7" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + underscore "1.9.1" + utf8 "3.0.0" + +web3-utils@1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.5.tgz#7691f981ce11dc919e123edbde159dce061a5a53" + integrity sha512-U0tNfB4Hep5ouzvNZ+Hr8I8kIftiHiDhwg+Eoh2Nvr5lLOPEH14B2exkRSARLXGY9xl2p3ykJWBCKoG1oCadug== + dependencies: + bn.js "4.11.8" + eth-lib "0.2.7" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + underscore "1.9.1" + utf8 "3.0.0" + +web3@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.1.tgz#5d8158bcca47838ab8c2b784a2dee4c3ceb4179b" + integrity sha512-nNMzeCK0agb5i/oTWNdQ1aGtwYfXzHottFP2Dz0oGIzavPMGSKyVlr8ibVb1yK5sJBjrWVnTdGaOC2zKDFuFRw== + dependencies: + web3-bzz "1.2.1" + web3-core "1.2.1" + web3-eth "1.2.1" + web3-eth-personal "1.2.1" + web3-net "1.2.1" + web3-shh "1.2.1" + web3-utils "1.2.1" + +web3@1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.4.tgz#6e7ab799eefc9b4648c2dab63003f704a1d5e7d9" + integrity sha512-xPXGe+w0x0t88Wj+s/dmAdASr3O9wmA9mpZRtixGZxmBexAF0MjfqYM+MS4tVl5s11hMTN3AZb8cDD4VLfC57A== + dependencies: + "@types/node" "^12.6.1" + web3-bzz "1.2.4" + web3-core "1.2.4" + web3-eth "1.2.4" + web3-eth-personal "1.2.4" + web3-net "1.2.4" + web3-shh "1.2.4" + web3-utils "1.2.4" + +web3@^1.0.0-beta.34, web3@^1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.11.tgz#50f458b2e8b11aa37302071c170ed61cff332975" + integrity sha512-mjQ8HeU41G6hgOYm1pmeH0mRAeNKJGnJEUzDMoerkpw7QUQT4exVREgF1MYPvL/z6vAshOXei25LE/t/Bxl8yQ== + dependencies: + web3-bzz "1.2.11" + web3-core "1.2.11" + web3-eth "1.2.11" + web3-eth-personal "1.2.11" + web3-net "1.2.11" + web3-shh "1.2.11" + web3-utils "1.2.11" + +websocket@1.0.29: + version "1.0.29" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.29.tgz#3f83e49d3279657c58b02a22d90749c806101b98" + integrity sha512-WhU8jKXC8sTh6ocLSqpZRlOKMNYGwUvjA5+XcIgIk/G3JCaDfkZUr0zA19sVSxJ0TEvm0i5IBzr54RZC4vzW7g== + dependencies: + debug "^2.2.0" + gulp "^4.0.2" + nan "^2.11.0" + typedarray-to-buffer "^3.1.5" + yaeti "^0.0.6" + +websocket@^1.0.31: + version "1.0.32" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.32.tgz#1f16ddab3a21a2d929dec1687ab21cfdc6d3dbb1" + integrity sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.50" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +"websocket@github:web3-js/WebSocket-Node#polyfill/globalThis": + version "1.0.29" + resolved "https://codeload.github.com/web3-js/WebSocket-Node/tar.gz/ef5ea2f41daf4a2113b80c9223df884b4d56c400" + dependencies: + debug "^2.2.0" + es5-ext "^0.10.50" + nan "^2.14.0" + typedarray-to-buffer "^3.1.5" + yaeti "^0.0.6" + +whatwg-fetch@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@1.3.1, which@^1.2.14, which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +workerpool@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.0.tgz#85aad67fa1a2c8ef9386a1b43539900f61d03d58" + integrity sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA== + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +ws@^3.0.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +ws@^5.1.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + +ws@^7.2.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" + integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== + +xhr-request-promise@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz#2d5f4b16d8c6c893be97f1a62b0ed4cf3ca5f96c" + integrity sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg== + dependencies: + xhr-request "^1.1.0" + +xhr-request@^1.0.1, xhr-request@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" + integrity sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA== + dependencies: + buffer-to-arraybuffer "^0.0.5" + object-assign "^4.1.1" + query-string "^5.0.1" + simple-get "^2.7.0" + timed-out "^4.0.1" + url-set-query "^1.0.0" + xhr "^2.0.4" + +xhr2-cookies@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48" + integrity sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg= + dependencies: + cookiejar "^2.1.1" + +xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3: + version "2.5.0" + resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.5.0.tgz#bed8d1676d5ca36108667692b74b316c496e49dd" + integrity sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ== + dependencies: + global "~4.3.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + +xmlhttprequest@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= + +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +xtend@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" + integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= + dependencies: + object-keys "~0.4.0" + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= + +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yargs-parser@13.1.2, yargs-parser@^13.1.0, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@5.0.0-security.0: + version "5.0.0-security.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz#4ff7271d25f90ac15643b86076a2ab499ec9ee24" + integrity sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ== + dependencies: + camelcase "^3.0.0" + object.assign "^4.1.0" + +yargs-parser@^15.0.1: + version "15.0.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.1.tgz#54786af40b820dcb2fb8025b11b4d659d76323b3" + integrity sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-unparser@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" + integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== + dependencies: + flat "^4.1.0" + lodash "^4.17.15" + yargs "^13.3.0" + +yargs-unparser@1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.1.tgz#bd4b0ee05b4c94d058929c32cb09e3fce71d3c5f" + integrity sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA== + dependencies: + camelcase "^5.3.1" + decamelize "^1.2.0" + flat "^4.1.0" + is-plain-obj "^1.1.0" + yargs "^14.2.3" + +yargs@13.2.4: + version "13.2.4" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" + integrity sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + os-locale "^3.1.0" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.0" + +yargs@13.3.2, yargs@^13.3.0: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + +yargs@^14.2.3: + version "14.2.3" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414" + integrity sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg== + dependencies: + cliui "^5.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^15.0.1" + +yargs@^7.1.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.1.tgz#67f0ef52e228d4ee0d6311acede8850f53464df6" + integrity sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g== + dependencies: + camelcase "^3.0.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + y18n "^3.2.1" + yargs-parser "5.0.0-security.0" + +yauzl@^2.4.2: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" diff --git a/tests/personal_test.go b/tests/personal_test.go new file mode 100644 index 0000000000..94a881ebce --- /dev/null +++ b/tests/personal_test.go @@ -0,0 +1,127 @@ +package tests + +import ( + "encoding/json" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + + "github.com/stretchr/testify/require" +) + +func TestPersonal_ListAccounts(t *testing.T) { + rpcRes := call(t, "personal_listAccounts", []string{}) + + var res []hexutil.Bytes + err := json.Unmarshal(rpcRes.Result, &res) + require.NoError(t, err) + require.Equal(t, 1, len(res)) +} + +func TestPersonal_NewAccount(t *testing.T) { + rpcRes := call(t, "personal_newAccount", []string{"password"}) + var addr common.Address + err := json.Unmarshal(rpcRes.Result, &addr) + require.NoError(t, err) + + rpcRes = call(t, "personal_listAccounts", []string{}) + var res []hexutil.Bytes + err = json.Unmarshal(rpcRes.Result, &res) + require.NoError(t, err) + require.Equal(t, 2, len(res)) +} + +func TestPersonal_Sign(t *testing.T) { + rpcRes := call(t, "personal_sign", []interface{}{hexutil.Bytes{0x88}, hexutil.Bytes(from), ""}) + + var res hexutil.Bytes + err := json.Unmarshal(rpcRes.Result, &res) + require.NoError(t, err) + require.Equal(t, 65, len(res)) + // TODO: check that signature is same as with geth, requires importing a key +} + +func TestPersonal_ImportRawKey(t *testing.T) { + privkey, err := ethcrypto.GenerateKey() + require.NoError(t, err) + + // parse priv key to hex + hexPriv := common.Bytes2Hex(ethcrypto.FromECDSA(privkey)) + rpcRes := call(t, "personal_importRawKey", []string{hexPriv, "password"}) + + var res hexutil.Bytes + err = json.Unmarshal(rpcRes.Result, &res) + require.NoError(t, err) + + addr := ethcrypto.PubkeyToAddress(privkey.PublicKey) + resAddr := common.BytesToAddress(res) + + require.Equal(t, addr.String(), resAddr.String()) +} + +func TestPersonal_EcRecover(t *testing.T) { + data := hexutil.Bytes{0x88} + rpcRes := call(t, "personal_sign", []interface{}{data, hexutil.Bytes(from), ""}) + + var res hexutil.Bytes + err := json.Unmarshal(rpcRes.Result, &res) + require.NoError(t, err) + require.Equal(t, 65, len(res)) + + rpcRes = call(t, "personal_ecRecover", []interface{}{data, res}) + var ecrecoverRes common.Address + err = json.Unmarshal(rpcRes.Result, &ecrecoverRes) + require.NoError(t, err) + require.Equal(t, from, ecrecoverRes[:]) +} + +func TestPersonal_UnlockAccount(t *testing.T) { + pswd := "nootwashere" + rpcRes := call(t, "personal_newAccount", []string{pswd}) + var addr common.Address + err := json.Unmarshal(rpcRes.Result, &addr) + require.NoError(t, err) + + // try to sign, should be locked + _, err = callWithError("personal_sign", []interface{}{hexutil.Bytes{0x88}, addr, ""}) + require.Error(t, err) + + rpcRes = call(t, "personal_unlockAccount", []interface{}{addr, ""}) + var unlocked bool + err = json.Unmarshal(rpcRes.Result, &unlocked) + require.NoError(t, err) + require.True(t, unlocked) + + // try to sign, should work now + rpcRes = call(t, "personal_sign", []interface{}{hexutil.Bytes{0x88}, addr, pswd}) + var res hexutil.Bytes + err = json.Unmarshal(rpcRes.Result, &res) + require.NoError(t, err) + require.Equal(t, 65, len(res)) +} + +func TestPersonal_LockAccount(t *testing.T) { + pswd := "nootwashere" + rpcRes := call(t, "personal_newAccount", []string{pswd}) + var addr common.Address + err := json.Unmarshal(rpcRes.Result, &addr) + require.NoError(t, err) + + rpcRes = call(t, "personal_unlockAccount", []interface{}{addr, ""}) + var unlocked bool + err = json.Unmarshal(rpcRes.Result, &unlocked) + require.NoError(t, err) + require.True(t, unlocked) + + rpcRes = call(t, "personal_lockAccount", []interface{}{addr}) + var locked bool + err = json.Unmarshal(rpcRes.Result, &locked) + require.NoError(t, err) + require.True(t, locked) + + // try to sign, should be locked + _, err = callWithError("personal_sign", []interface{}{hexutil.Bytes{0x88}, addr, ""}) + require.Error(t, err) +} diff --git a/tests/rpc_test.go b/tests/rpc_test.go new file mode 100644 index 0000000000..28bf81d619 --- /dev/null +++ b/tests/rpc_test.go @@ -0,0 +1,853 @@ +// This is a test utility for Ethermint's Web3 JSON-RPC services. +// +// To run these tests please first ensure you have the ethermintd running +// and have started the RPC service with `ethermintcli rest-server`. +// +// You can configure the desired HOST and MODE as well +package tests + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "fmt" + "math/big" + "net/http" + "os" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + + ethcmn "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" + + "github.com/cosmos/ethermint/rpc" + "github.com/cosmos/ethermint/version" + "github.com/cosmos/ethermint/x/evm/types" +) + +const ( + addrA = "0xc94770007dda54cF92009BFF0dE90c06F603a09f" + addrAStoreKey = 0 +) + +var ( + MODE = os.Getenv("MODE") + HOST = os.Getenv("HOST") + + zeroString = "0x0" + from = []byte{} +) + +type Request struct { + Version string `json:"jsonrpc"` + Method string `json:"method"` + Params interface{} `json:"params"` + ID int `json:"id"` +} + +type RPCError struct { + Code int `json:"code"` + Message string `json:"message"` + Data interface{} `json:"data,omitempty"` +} + +type Response struct { + Error *RPCError `json:"error"` + ID int `json:"id"` + Result json.RawMessage `json:"result,omitempty"` +} + +func TestMain(m *testing.M) { + if MODE != "rpc" { + _, _ = fmt.Fprintln(os.Stdout, "Skipping RPC test") + return + } + + if HOST == "" { + HOST = "http://localhost:8545" + } + + var err error + from, err = getAddress() + if err != nil { + fmt.Printf("failed to get account: %s\n", err) + os.Exit(1) + } + + // Start all tests + code := m.Run() + os.Exit(code) +} + +func getAddress() ([]byte, error) { + rpcRes, err := callWithError("eth_accounts", []string{}) + if err != nil { + return nil, err + } + + var res []hexutil.Bytes + err = json.Unmarshal(rpcRes.Result, &res) + if err != nil { + return nil, err + } + + return res[0], nil +} + +func createRequest(method string, params interface{}) Request { + return Request{ + Version: "2.0", + Method: method, + Params: params, + ID: 1, + } +} + +func call(t *testing.T, method string, params interface{}) *Response { + req, err := json.Marshal(createRequest(method, params)) + require.NoError(t, err) + + var rpcRes *Response + time.Sleep(1 * time.Second) + /* #nosec */ + res, err := http.Post(HOST, "application/json", bytes.NewBuffer(req)) + require.NoError(t, err) + + decoder := json.NewDecoder(res.Body) + rpcRes = new(Response) + err = decoder.Decode(&rpcRes) + require.NoError(t, err) + + err = res.Body.Close() + require.NoError(t, err) + require.Nil(t, rpcRes.Error) + + return rpcRes +} + +func callWithError(method string, params interface{}) (*Response, error) { + req, err := json.Marshal(createRequest(method, params)) + if err != nil { + return nil, err + } + + var rpcRes *Response + time.Sleep(1 * time.Second) + /* #nosec */ + res, err := http.Post(HOST, "application/json", bytes.NewBuffer(req)) + if err != nil { + return nil, err + } + + decoder := json.NewDecoder(res.Body) + rpcRes = new(Response) + err = decoder.Decode(&rpcRes) + if err != nil { + return nil, err + } + + err = res.Body.Close() + if err != nil { + return nil, err + } + + if rpcRes.Error != nil { + return nil, fmt.Errorf(rpcRes.Error.Message) + } + + return rpcRes, nil +} + +// turns a 0x prefixed hex string to a big.Int +func hexToBigInt(t *testing.T, in string) *big.Int { + s := in[2:] + b, err := hex.DecodeString(s) + require.NoError(t, err) + return big.NewInt(0).SetBytes(b) +} + +func TestBlockBloom(t *testing.T) { + hash := deployTestContractWithFunction(t) + receipt := waitForReceipt(t, hash) + + number := receipt["blockNumber"].(string) + param := []interface{}{number, false} + rpcRes := call(t, "eth_getBlockByNumber", param) + + block := make(map[string]interface{}) + err := json.Unmarshal(rpcRes.Result, &block) + require.NoError(t, err) + + lb := hexToBigInt(t, block["logsBloom"].(string)) + require.NotEqual(t, big.NewInt(0), lb) + require.Equal(t, hash.String(), block["transactions"].([]interface{})[0]) +} + +func TestEth_GetLogs_NoLogs(t *testing.T) { + param := make([]map[string][]string, 1) + param[0] = make(map[string][]string) + param[0]["topics"] = []string{} + call(t, "eth_getLogs", param) +} + +func TestEth_GetLogs_Topics_AB(t *testing.T) { + // TODO: this test passes on when run on its own, but fails when run with the other tests + if testing.Short() { + t.Skip("skipping TestEth_GetLogs_Topics_AB") + } + + rpcRes := call(t, "eth_blockNumber", []string{}) + + var res hexutil.Uint64 + err := res.UnmarshalJSON(rpcRes.Result) + require.NoError(t, err) + + param := make([]map[string]interface{}, 1) + param[0] = make(map[string]interface{}) + param[0]["topics"] = []string{helloTopic, worldTopic} + param[0]["fromBlock"] = res.String() + + hash := deployTestContractWithFunction(t) + waitForReceipt(t, hash) + + rpcRes = call(t, "eth_getLogs", param) + + var logs []*ethtypes.Log + err = json.Unmarshal(rpcRes.Result, &logs) + require.NoError(t, err) + + require.Equal(t, 1, len(logs)) +} + +func TestEth_GetTransactionCount(t *testing.T) { + // TODO: this test passes on when run on its own, but fails when run with the other tests + if testing.Short() { + t.Skip("skipping TestEth_GetTransactionCount") + } + + prev := getNonce(t) + sendTestTransaction(t) + post := getNonce(t) + require.Equal(t, prev, post-1) +} + +func TestEth_GetTransactionLogs(t *testing.T) { + // TODO: this test passes on when run on its own, but fails when run with the other tests + if testing.Short() { + t.Skip("skipping TestEth_GetTransactionLogs") + } + + hash, _ := deployTestContract(t) + + param := []string{hash.String()} + rpcRes := call(t, "eth_getTransactionLogs", param) + + logs := new([]*ethtypes.Log) + err := json.Unmarshal(rpcRes.Result, logs) + require.NoError(t, err) + require.Equal(t, 1, len(*logs)) +} + +func TestEth_protocolVersion(t *testing.T) { + expectedRes := hexutil.Uint(version.ProtocolVersion) + + rpcRes := call(t, "eth_protocolVersion", []string{}) + + var res hexutil.Uint + err := res.UnmarshalJSON(rpcRes.Result) + require.NoError(t, err) + + t.Logf("Got protocol version: %s\n", res.String()) + require.Equal(t, expectedRes, res, "expected: %s got: %s\n", expectedRes.String(), rpcRes.Result) +} + +func TestEth_chainId(t *testing.T) { + rpcRes := call(t, "eth_chainId", []string{}) + + var res hexutil.Uint + err := res.UnmarshalJSON(rpcRes.Result) + require.NoError(t, err) + require.NotEqual(t, "0x0", res.String()) +} + +func TestEth_blockNumber(t *testing.T) { + rpcRes := call(t, "eth_blockNumber", []string{}) + + var res hexutil.Uint64 + err := res.UnmarshalJSON(rpcRes.Result) + require.NoError(t, err) + + t.Logf("Got block number: %s\n", res.String()) +} + +func TestEth_coinbase(t *testing.T) { + zeroAddress := hexutil.Bytes(ethcmn.Address{}.Bytes()) + rpcRes := call(t, "eth_coinbase", []string{}) + + var res hexutil.Bytes + err := res.UnmarshalJSON(rpcRes.Result) + require.NoError(t, err) + + t.Logf("Got coinbase block proposer: %s\n", res.String()) + require.NotEqual(t, zeroAddress.String(), res.String(), "expected: not %s got: %s\n", zeroAddress.String(), res.String()) +} + +func TestEth_GetBalance(t *testing.T) { + rpcRes := call(t, "eth_getBalance", []string{addrA, zeroString}) + + var res hexutil.Big + err := res.UnmarshalJSON(rpcRes.Result) + require.NoError(t, err) + + t.Logf("Got balance %s for %s\n", res.String(), addrA) + + // 0 if x == y; where x is res, y is 0 + if res.ToInt().Cmp(big.NewInt(0)) != 0 { + t.Errorf("expected balance: %d, got: %s", 0, res.String()) + } +} + +func TestEth_GetStorageAt(t *testing.T) { + expectedRes := hexutil.Bytes{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + rpcRes := call(t, "eth_getStorageAt", []string{addrA, fmt.Sprint(addrAStoreKey), zeroString}) + + var storage hexutil.Bytes + err := storage.UnmarshalJSON(rpcRes.Result) + require.NoError(t, err) + + t.Logf("Got value [%X] for %s with key %X\n", storage, addrA, addrAStoreKey) + + require.True(t, bytes.Equal(storage, expectedRes), "expected: %d (%d bytes) got: %d (%d bytes)", expectedRes, len(expectedRes), storage, len(storage)) +} + +func TestEth_GetProof(t *testing.T) { + params := make([]interface{}, 3) + params[0] = addrA + params[1] = []string{fmt.Sprint(addrAStoreKey)} + params[2] = "latest" + rpcRes := call(t, "eth_getProof", params) + require.NotNil(t, rpcRes) + + var accRes rpc.AccountResult + err := json.Unmarshal(rpcRes.Result, &accRes) + require.NoError(t, err) + require.NotEmpty(t, accRes.AccountProof) + require.NotEmpty(t, accRes.StorageProof) + + t.Logf("Got AccountResult %s", rpcRes.Result) +} + +func TestEth_GetCode(t *testing.T) { + expectedRes := hexutil.Bytes{} + rpcRes := call(t, "eth_getCode", []string{addrA, zeroString}) + + var code hexutil.Bytes + err := code.UnmarshalJSON(rpcRes.Result) + + require.NoError(t, err) + + t.Logf("Got code [%X] for %s\n", code, addrA) + require.True(t, bytes.Equal(expectedRes, code), "expected: %X got: %X", expectedRes, code) +} + +func TestEth_SendTransaction_Transfer(t *testing.T) { + param := make([]map[string]string, 1) + param[0] = make(map[string]string) + param[0]["from"] = "0x" + fmt.Sprintf("%x", from) + param[0]["to"] = "0x0000000000000000000000000000000012341234" + param[0]["value"] = "0x16345785d8a0000" + param[0]["gasLimit"] = "0x5208" + param[0]["gasPrice"] = "0x55ae82600" + + rpcRes := call(t, "eth_sendTransaction", param) + + var hash hexutil.Bytes + err := json.Unmarshal(rpcRes.Result, &hash) + require.NoError(t, err) + + receipt := waitForReceipt(t, hash) + require.NotNil(t, receipt) + require.Equal(t, "0x1", receipt["status"].(string)) +} + +func TestEth_SendTransaction_ContractDeploy(t *testing.T) { + param := make([]map[string]string, 1) + param[0] = make(map[string]string) + param[0]["from"] = "0x" + fmt.Sprintf("%x", from) + param[0]["data"] = "0x6080604052348015600f57600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a2603580604b6000396000f3fe6080604052600080fdfea165627a7a723058206cab665f0f557620554bb45adf266708d2bd349b8a4314bdff205ee8440e3c240029" + + rpcRes := call(t, "eth_sendTransaction", param) + + var hash hexutil.Bytes + err := json.Unmarshal(rpcRes.Result, &hash) + require.NoError(t, err) +} + +func TestEth_NewFilter(t *testing.T) { + param := make([]map[string][]string, 1) + param[0] = make(map[string][]string) + param[0]["topics"] = []string{"0x0000000000000000000000000000000000000000000000000000000012341234"} + rpcRes := call(t, "eth_newFilter", param) + + var ID string + err := json.Unmarshal(rpcRes.Result, &ID) + require.NoError(t, err) +} + +func TestEth_NewBlockFilter(t *testing.T) { + rpcRes := call(t, "eth_newBlockFilter", []string{}) + + var ID string + err := json.Unmarshal(rpcRes.Result, &ID) + require.NoError(t, err) +} + +func TestEth_GetFilterChanges_BlockFilter(t *testing.T) { + rpcRes := call(t, "eth_newBlockFilter", []string{}) + + var ID string + err := json.Unmarshal(rpcRes.Result, &ID) + require.NoError(t, err) + + time.Sleep(5 * time.Second) + + changesRes := call(t, "eth_getFilterChanges", []string{ID}) + var hashes []ethcmn.Hash + err = json.Unmarshal(changesRes.Result, &hashes) + require.NoError(t, err) + require.GreaterOrEqual(t, len(hashes), 1) +} + +func TestEth_GetFilterChanges_NoLogs(t *testing.T) { + param := make([]map[string][]string, 1) + param[0] = make(map[string][]string) + param[0]["topics"] = []string{} + rpcRes := call(t, "eth_newFilter", param) + + var ID string + err := json.Unmarshal(rpcRes.Result, &ID) + require.NoError(t, err) + + changesRes := call(t, "eth_getFilterChanges", []string{ID}) + + var logs []*ethtypes.Log + err = json.Unmarshal(changesRes.Result, &logs) + require.NoError(t, err) +} + +func TestEth_GetFilterChanges_WrongID(t *testing.T) { + req, err := json.Marshal(createRequest("eth_getFilterChanges", []string{"0x1122334400000077"})) + require.NoError(t, err) + + var rpcRes *Response + time.Sleep(1 * time.Second) + /* #nosec */ + res, err := http.Post(HOST, "application/json", bytes.NewBuffer(req)) + require.NoError(t, err) + + decoder := json.NewDecoder(res.Body) + rpcRes = new(Response) + err = decoder.Decode(&rpcRes) + require.NoError(t, err) + + err = res.Body.Close() + require.NoError(t, err) + require.NotNil(t, "invalid filter ID", rpcRes.Error.Message) +} + +// sendTestTransaction sends a dummy transaction +func sendTestTransaction(t *testing.T) hexutil.Bytes { + param := make([]map[string]string, 1) + param[0] = make(map[string]string) + param[0]["from"] = "0x" + fmt.Sprintf("%x", from) + param[0]["to"] = "0x1122334455667788990011223344556677889900" + param[0]["value"] = "0x1" + rpcRes := call(t, "eth_sendTransaction", param) + + var hash hexutil.Bytes + err := json.Unmarshal(rpcRes.Result, &hash) + require.NoError(t, err) + return hash +} + +func TestEth_GetTransactionReceipt(t *testing.T) { + hash := sendTestTransaction(t) + + time.Sleep(time.Second * 5) + + param := []string{hash.String()} + rpcRes := call(t, "eth_getTransactionReceipt", param) + require.Nil(t, rpcRes.Error) + + receipt := make(map[string]interface{}) + err := json.Unmarshal(rpcRes.Result, &receipt) + require.NoError(t, err) + require.NotEmpty(t, receipt) + require.Equal(t, "0x1", receipt["status"].(string)) + require.Equal(t, []interface{}{}, receipt["logs"].([]interface{})) +} + +// deployTestContract deploys a contract that emits an event in the constructor +func deployTestContract(t *testing.T) (hexutil.Bytes, map[string]interface{}) { + param := make([]map[string]string, 1) + param[0] = make(map[string]string) + param[0]["from"] = "0x" + fmt.Sprintf("%x", from) + param[0]["data"] = "0x6080604052348015600f57600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a2603580604b6000396000f3fe6080604052600080fdfea165627a7a723058206cab665f0f557620554bb45adf266708d2bd349b8a4314bdff205ee8440e3c240029" + param[0]["gas"] = "0x200000" + + rpcRes := call(t, "eth_sendTransaction", param) + + var hash hexutil.Bytes + err := json.Unmarshal(rpcRes.Result, &hash) + require.NoError(t, err) + + receipt := waitForReceipt(t, hash) + require.NotNil(t, receipt, "transaction failed") + require.Equal(t, "0x1", receipt["status"].(string)) + + return hash, receipt +} + +func TestEth_GetTransactionReceipt_ContractDeployment(t *testing.T) { + hash, _ := deployTestContract(t) + + time.Sleep(time.Second * 5) + + param := []string{hash.String()} + rpcRes := call(t, "eth_getTransactionReceipt", param) + + receipt := make(map[string]interface{}) + err := json.Unmarshal(rpcRes.Result, &receipt) + require.NoError(t, err) + require.Equal(t, "0x1", receipt["status"].(string)) + + require.NotEqual(t, ethcmn.Address{}.String(), receipt["contractAddress"].(string)) + require.NotNil(t, receipt["logs"]) + +} + +func getTransactionReceipt(t *testing.T, hash hexutil.Bytes) map[string]interface{} { + param := []string{hash.String()} + rpcRes := call(t, "eth_getTransactionReceipt", param) + + receipt := make(map[string]interface{}) + err := json.Unmarshal(rpcRes.Result, &receipt) + require.NoError(t, err) + + return receipt +} + +func waitForReceipt(t *testing.T, hash hexutil.Bytes) map[string]interface{} { + for i := 0; i < 12; i++ { + receipt := getTransactionReceipt(t, hash) + if receipt != nil { + return receipt + } + + time.Sleep(time.Second) + } + + return nil +} + +func TestEth_GetFilterChanges_NoTopics(t *testing.T) { + rpcRes := call(t, "eth_blockNumber", []string{}) + + var res hexutil.Uint64 + err := res.UnmarshalJSON(rpcRes.Result) + require.NoError(t, err) + + param := make([]map[string]interface{}, 1) + param[0] = make(map[string]interface{}) + param[0]["topics"] = []string{} + param[0]["fromBlock"] = res.String() + + // instantiate new filter + rpcRes = call(t, "eth_newFilter", param) + require.Nil(t, rpcRes.Error) + var ID string + err = json.Unmarshal(rpcRes.Result, &ID) + require.NoError(t, err) + + // deploy contract, emitting some event + deployTestContract(t) + + // get filter changes + changesRes := call(t, "eth_getFilterChanges", []string{ID}) + + var logs []*ethtypes.Log + err = json.Unmarshal(changesRes.Result, &logs) + require.NoError(t, err) + require.Equal(t, 1, len(logs)) +} + +func TestEth_GetFilterChanges_Addresses(t *testing.T) { + t.Skip() + // TODO: need transaction receipts to determine contract deployment address +} + +func TestEth_GetFilterChanges_BlockHash(t *testing.T) { + t.Skip() + // TODO: need transaction receipts to determine tx block +} + +// hash of Hello event +var helloTopic = "0x775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd738898" + +// world parameter in Hello event +var worldTopic = "0x0000000000000000000000000000000000000000000000000000000000000011" + +func deployTestContractWithFunction(t *testing.T) hexutil.Bytes { + // pragma solidity ^0.5.1; + + // contract Test { + // event Hello(uint256 indexed world); + // event TestEvent(uint256 indexed a, uint256 indexed b); + + // uint256 myStorage; + + // constructor() public { + // emit Hello(17); + // } + + // function test(uint256 a, uint256 b) public { + // myStorage = a; + // emit TestEvent(a, b); + // } + // } + + bytecode := "0x608060405234801561001057600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a260d08061004d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063eb8ac92114602d575b600080fd5b606060048036036040811015604157600080fd5b8101908080359060200190929190803590602001909291905050506062565b005b8160008190555080827ff3ca124a697ba07e8c5e80bebcfcc48991fc16a63170e8a9206e30508960d00360405160405180910390a3505056fea265627a7a723158201d94d2187aaf3a6790527b615fcc40970febf0385fa6d72a2344848ebd0df3e964736f6c63430005110032" + + param := make([]map[string]string, 1) + param[0] = make(map[string]string) + param[0]["from"] = "0x" + fmt.Sprintf("%x", from) + param[0]["data"] = bytecode + param[0]["gas"] = "0x200000" + + rpcRes := call(t, "eth_sendTransaction", param) + + var hash hexutil.Bytes + err := json.Unmarshal(rpcRes.Result, &hash) + require.NoError(t, err) + + receipt := waitForReceipt(t, hash) + require.NotNil(t, receipt, "transaction failed") + require.Equal(t, "0x1", receipt["status"].(string)) + + return hash +} + +// Tests topics case where there are topics in first two positions +func TestEth_GetFilterChanges_Topics_AB(t *testing.T) { + time.Sleep(time.Second) + + rpcRes := call(t, "eth_blockNumber", []string{}) + + var res hexutil.Uint64 + err := res.UnmarshalJSON(rpcRes.Result) + require.NoError(t, err) + + param := make([]map[string]interface{}, 1) + param[0] = make(map[string]interface{}) + param[0]["topics"] = []string{helloTopic, worldTopic} + param[0]["fromBlock"] = res.String() + + // instantiate new filter + rpcRes = call(t, "eth_newFilter", param) + var ID string + err = json.Unmarshal(rpcRes.Result, &ID) + require.NoError(t, err, string(rpcRes.Result)) + + deployTestContractWithFunction(t) + + // get filter changes + changesRes := call(t, "eth_getFilterChanges", []string{ID}) + + var logs []*ethtypes.Log + err = json.Unmarshal(changesRes.Result, &logs) + require.NoError(t, err) + + require.Equal(t, 1, len(logs)) +} + +func TestEth_GetFilterChanges_Topics_XB(t *testing.T) { + rpcRes := call(t, "eth_blockNumber", []string{}) + + var res hexutil.Uint64 + err := res.UnmarshalJSON(rpcRes.Result) + require.NoError(t, err) + + param := make([]map[string]interface{}, 1) + param[0] = make(map[string]interface{}) + param[0]["topics"] = []interface{}{nil, worldTopic} + param[0]["fromBlock"] = res.String() + + // instantiate new filter + rpcRes = call(t, "eth_newFilter", param) + var ID string + err = json.Unmarshal(rpcRes.Result, &ID) + require.NoError(t, err) + + deployTestContractWithFunction(t) + + // get filter changes + changesRes := call(t, "eth_getFilterChanges", []string{ID}) + + var logs []*ethtypes.Log + err = json.Unmarshal(changesRes.Result, &logs) + require.NoError(t, err) + + require.Equal(t, 1, len(logs)) +} + +func TestEth_GetFilterChanges_Topics_XXC(t *testing.T) { + t.Skip() + // TODO: call test function, need tx receipts to determine contract address +} + +func TestEth_PendingTransactionFilter(t *testing.T) { + rpcRes := call(t, "eth_newPendingTransactionFilter", []string{}) + + var ID string + err := json.Unmarshal(rpcRes.Result, &ID) + require.NoError(t, err) + + for i := 0; i < 5; i++ { + deployTestContractWithFunction(t) + } + + time.Sleep(10 * time.Second) + + // get filter changes + changesRes := call(t, "eth_getFilterChanges", []string{ID}) + require.NotNil(t, changesRes) + + var txs []*hexutil.Bytes + err = json.Unmarshal(changesRes.Result, &txs) + require.NoError(t, err, string(changesRes.Result)) + + require.True(t, len(txs) >= 2, "could not get any txs", "changesRes.Result", string(changesRes.Result)) +} + +func getNonce(t *testing.T) hexutil.Uint64 { + param := []interface{}{hexutil.Bytes(from), "latest"} + rpcRes := call(t, "eth_getTransactionCount", param) + + var nonce hexutil.Uint64 + err := json.Unmarshal(rpcRes.Result, &nonce) + require.NoError(t, err) + return nonce +} + +func TestEth_EstimateGas(t *testing.T) { + param := make([]map[string]string, 1) + param[0] = make(map[string]string) + param[0]["from"] = "0x" + fmt.Sprintf("%x", from) + param[0]["to"] = "0x1122334455667788990011223344556677889900" + param[0]["value"] = "0x1" + rpcRes := call(t, "eth_estimateGas", param) + require.NotNil(t, rpcRes) + require.NotEmpty(t, rpcRes.Result) + + var gas string + err := json.Unmarshal(rpcRes.Result, &gas) + require.NoError(t, err, string(rpcRes.Result)) + + require.Equal(t, "0xf552", gas) +} + +func TestEth_EstimateGas_ContractDeployment(t *testing.T) { + bytecode := "0x608060405234801561001057600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a260d08061004d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063eb8ac92114602d575b600080fd5b606060048036036040811015604157600080fd5b8101908080359060200190929190803590602001909291905050506062565b005b8160008190555080827ff3ca124a697ba07e8c5e80bebcfcc48991fc16a63170e8a9206e30508960d00360405160405180910390a3505056fea265627a7a723158201d94d2187aaf3a6790527b615fcc40970febf0385fa6d72a2344848ebd0df3e964736f6c63430005110032" + + param := make([]map[string]string, 1) + param[0] = make(map[string]string) + param[0]["from"] = "0x" + fmt.Sprintf("%x", from) + param[0]["data"] = bytecode + + rpcRes := call(t, "eth_estimateGas", param) + require.NotNil(t, rpcRes) + require.NotEmpty(t, rpcRes.Result) + + var gas hexutil.Uint64 + err := json.Unmarshal(rpcRes.Result, &gas) + require.NoError(t, err, string(rpcRes.Result)) + + require.Equal(t, "0x1c2c4", gas.String()) +} + +func TestEth_ExportAccount(t *testing.T) { + param := []string{} + param = append(param, "0x1122334455667788990011223344556677889901") + param = append(param, "latest") + rpcRes := call(t, "eth_exportAccount", param) + + var res string + err := json.Unmarshal(rpcRes.Result, &res) + require.NoError(t, err) + + var account types.GenesisAccount + err = json.Unmarshal([]byte(res), &account) + require.NoError(t, err) + + require.Equal(t, "0x1122334455667788990011223344556677889901", account.Address.Hex()) + require.Equal(t, big.NewInt(0), account.Balance) + require.Equal(t, hexutil.Bytes(nil), account.Code) + require.Equal(t, types.Storage(nil), account.Storage) +} + +func TestEth_ExportAccount_WithStorage(t *testing.T) { + hash := deployTestContractWithFunction(t) + receipt := waitForReceipt(t, hash) + addr := receipt["contractAddress"].(string) + + // call function to set storage + calldata := "0xeb8ac92100000000000000000000000000000000000000000000000000000000000000630000000000000000000000000000000000000000000000000000000000000000" + + param := make([]map[string]string, 1) + param[0] = make(map[string]string) + param[0]["from"] = "0x" + fmt.Sprintf("%x", from) + param[0]["to"] = addr + param[0]["data"] = calldata + rpcRes := call(t, "eth_sendTransaction", param) + + var txhash hexutil.Bytes + err := json.Unmarshal(rpcRes.Result, &txhash) + require.NoError(t, err) + waitForReceipt(t, txhash) + + // get exported account + eap := []string{} + eap = append(eap, addr) + eap = append(eap, "latest") + rpcRes = call(t, "eth_exportAccount", eap) + + var res string + err = json.Unmarshal(rpcRes.Result, &res) + require.NoError(t, err) + + var account types.GenesisAccount + err = json.Unmarshal([]byte(res), &account) + require.NoError(t, err) + + // deployed bytecode + bytecode := "0x6080604052348015600f57600080fd5b506004361060285760003560e01c8063eb8ac92114602d575b600080fd5b606060048036036040811015604157600080fd5b8101908080359060200190929190803590602001909291905050506062565b005b8160008190555080827ff3ca124a697ba07e8c5e80bebcfcc48991fc16a63170e8a9206e30508960d00360405160405180910390a3505056fea265627a7a723158201d94d2187aaf3a6790527b615fcc40970febf0385fa6d72a2344848ebd0df3e964736f6c63430005110032" + require.Equal(t, addr, strings.ToLower(account.Address.Hex())) + require.Equal(t, big.NewInt(0), account.Balance) + require.Equal(t, bytecode, account.Code.String()) + require.NotEqual(t, types.Storage(nil), account.Storage) +} + +func TestEth_GetBlockByNumber(t *testing.T) { + param := []interface{}{"0x1", false} + rpcRes := call(t, "eth_getBlockByNumber", param) + + block := make(map[string]interface{}) + err := json.Unmarshal(rpcRes.Result, &block) + require.NoError(t, err) + require.Equal(t, "0x0", block["extraData"].(string)) + require.Equal(t, []interface{}{}, block["uncles"].([]interface{})) +} diff --git a/types/account.go b/types/account.go index 70193e45f2..ca05f8173b 100644 --- a/types/account.go +++ b/types/account.go @@ -1,83 +1,211 @@ package types import ( + "bytes" + "encoding/json" "fmt" + "gopkg.in/yaml.v2" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/exported" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ethcmn "github.com/ethereum/go-ethereum/common" + ethcrypto "github.com/ethereum/go-ethereum/crypto" ) -var _ auth.Account = (*Account)(nil) +var _ exported.Account = (*EthAccount)(nil) +var _ exported.GenesisAccount = (*EthAccount)(nil) -const ( - // DenomDefault defines the single coin type/denomination supported in - // Ethermint. - DenomDefault = "Photon" -) +func init() { + authtypes.RegisterAccountTypeCodec(&EthAccount{}, EthAccountName) +} // ---------------------------------------------------------------------------- // Main Ethermint account // ---------------------------------------------------------------------------- -// BaseAccount implements the auth.Account interface and embeds an -// auth.BaseAccount type. It is compatible with the auth.AccountMapper. -type Account struct { - *auth.BaseAccount - - // merkle root of the storage trie - // - // TODO: good chance we may not need this - Root ethcmn.Hash +// EthAccount implements the auth.Account interface and embeds an +// auth.BaseAccount type. It is compatible with the auth.AccountKeeper. +type EthAccount struct { + *authtypes.BaseAccount `json:"base_account" yaml:"base_account"` + CodeHash []byte `json:"code_hash" yaml:"code_hash"` +} - CodeHash []byte +// ProtoAccount defines the prototype function for BaseAccount used for an +// AccountKeeper. +func ProtoAccount() exported.Account { + return &EthAccount{ + BaseAccount: &auth.BaseAccount{}, + CodeHash: ethcrypto.Keccak256(nil), + } } -// ProtoBaseAccount defines the prototype function for BaseAccount used for an -// account mapper. -func ProtoBaseAccount() auth.Account { - return &Account{BaseAccount: &auth.BaseAccount{}} +// EthAddress returns the account address ethereum format. +func (acc EthAccount) EthAddress() ethcmn.Address { + return ethcmn.BytesToAddress(acc.Address.Bytes()) } +// TODO: remove on SDK v0.40 + // Balance returns the balance of an account. -func (acc Account) Balance() sdk.Int { - return acc.GetCoins().AmountOf(DenomDefault) +func (acc EthAccount) Balance(denom string) sdk.Int { + return acc.GetCoins().AmountOf(denom) +} + +// SetBalance sets an account's balance of the given coin denomination. +// +// CONTRACT: assumes the denomination is valid. +func (acc *EthAccount) SetBalance(denom string, amt sdk.Int) { + coins := acc.GetCoins() + diff := amt.Sub(coins.AmountOf(denom)) + switch { + case diff.IsPositive(): + // Increase coins to amount + coins = coins.Add(sdk.NewCoin(denom, diff)) + case diff.IsNegative(): + // Decrease coins to amount + coins = coins.Sub(sdk.NewCoins(sdk.NewCoin(denom, diff.Neg()))) + default: + return + } + + if err := acc.SetCoins(coins); err != nil { + panic(fmt.Errorf("could not set %s coins for address %s: %w", denom, acc.EthAddress().String(), err)) + } } -// SetBalance sets an account's balance. -func (acc Account) SetBalance(amt sdk.Int) { - acc.SetCoins(sdk.Coins{sdk.NewCoin(DenomDefault, amt)}) +type ethermintAccountPretty struct { + Address sdk.AccAddress `json:"address" yaml:"address"` + EthAddress string `json:"eth_address" yaml:"eth_address"` + Coins sdk.Coins `json:"coins" yaml:"coins"` + PubKey string `json:"public_key" yaml:"public_key"` + AccountNumber uint64 `json:"account_number" yaml:"account_number"` + Sequence uint64 `json:"sequence" yaml:"sequence"` + CodeHash string `json:"code_hash" yaml:"code_hash"` } -// ---------------------------------------------------------------------------- -// Code & Storage -// ---------------------------------------------------------------------------- +// MarshalYAML returns the YAML representation of an account. +func (acc EthAccount) MarshalYAML() (interface{}, error) { + alias := ethermintAccountPretty{ + Address: acc.Address, + EthAddress: acc.EthAddress().String(), + Coins: acc.Coins, + AccountNumber: acc.AccountNumber, + Sequence: acc.Sequence, + CodeHash: ethcmn.Bytes2Hex(acc.CodeHash), + } -// Account code and storage type aliases. -type ( - Code []byte - Storage map[ethcmn.Hash]ethcmn.Hash -) + var err error + + if acc.PubKey != nil { + alias.PubKey, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, acc.PubKey) + if err != nil { + return nil, err + } + } -func (c Code) String() string { - return string(c) + bz, err := yaml.Marshal(alias) + if err != nil { + return nil, err + } + + return string(bz), err } -func (c Storage) String() (str string) { - for key, value := range c { - str += fmt.Sprintf("%X : %X\n", key, value) +// MarshalJSON returns the JSON representation of an EthAccount. +func (acc EthAccount) MarshalJSON() ([]byte, error) { + var ethAddress = "" + + if acc.BaseAccount != nil && acc.Address != nil { + ethAddress = acc.EthAddress().String() + } + + alias := ethermintAccountPretty{ + Address: acc.Address, + EthAddress: ethAddress, + Coins: acc.Coins, + AccountNumber: acc.AccountNumber, + Sequence: acc.Sequence, + CodeHash: ethcmn.Bytes2Hex(acc.CodeHash), } - return + var err error + + if acc.PubKey != nil { + alias.PubKey, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, acc.PubKey) + if err != nil { + return nil, err + } + } + + return json.Marshal(alias) } -// Copy returns a copy of storage. -func (c Storage) Copy() Storage { - cpy := make(Storage) - for key, value := range c { - cpy[key] = value +// UnmarshalJSON unmarshals raw JSON bytes into an EthAccount. +func (acc *EthAccount) UnmarshalJSON(bz []byte) error { + var ( + alias ethermintAccountPretty + err error + ) + + if err := json.Unmarshal(bz, &alias); err != nil { + return err } - return cpy + switch { + case !alias.Address.Empty() && alias.EthAddress != "": + // Both addresses provided. Verify correctness + ethAddress := ethcmn.HexToAddress(alias.EthAddress) + ethAddressFromAccAddress := ethcmn.BytesToAddress(alias.Address.Bytes()) + + if !bytes.Equal(ethAddress.Bytes(), alias.Address.Bytes()) { + err = sdkerrors.Wrapf( + sdkerrors.ErrInvalidAddress, + "expected %s, got %s", + ethAddressFromAccAddress.String(), ethAddress.String(), + ) + } + + case !alias.Address.Empty() && alias.EthAddress == "": + // unmarshal sdk.AccAddress only. Do nothing here + case alias.Address.Empty() && alias.EthAddress != "": + // retrieve sdk.AccAddress from ethereum address + ethAddress := ethcmn.HexToAddress(alias.EthAddress) + alias.Address = sdk.AccAddress(ethAddress.Bytes()) + case alias.Address.Empty() && alias.EthAddress == "": + err = sdkerrors.Wrapf( + sdkerrors.ErrInvalidAddress, + "account must contain address in Ethereum Hex or Cosmos Bech32 format", + ) + } + + if err != nil { + return err + } + + acc.BaseAccount = &authtypes.BaseAccount{ + Coins: alias.Coins, + Address: alias.Address, + AccountNumber: alias.AccountNumber, + Sequence: alias.Sequence, + } + acc.CodeHash = ethcmn.Hex2Bytes(alias.CodeHash) + + if alias.PubKey != "" { + acc.BaseAccount.PubKey, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) + if err != nil { + return err + } + } + return nil +} + +// String implements the fmt.Stringer interface +func (acc EthAccount) String() string { + out, _ := yaml.Marshal(acc) + return string(out) } diff --git a/types/account_test.go b/types/account_test.go new file mode 100644 index 0000000000..ff28bcb39e --- /dev/null +++ b/types/account_test.go @@ -0,0 +1,178 @@ +package types_test + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + + tmamino "github.com/tendermint/tendermint/crypto/encoding/amino" + "github.com/tendermint/tendermint/crypto/secp256k1" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + + "github.com/cosmos/ethermint/crypto" + "github.com/cosmos/ethermint/types" +) + +func init() { + tmamino.RegisterKeyType(crypto.PubKeySecp256k1{}, crypto.PubKeyAminoName) + tmamino.RegisterKeyType(crypto.PrivKeySecp256k1{}, crypto.PrivKeyAminoName) +} + +type AccountTestSuite struct { + suite.Suite + + account *types.EthAccount +} + +func (suite *AccountTestSuite) SetupTest() { + pubkey := secp256k1.GenPrivKey().PubKey() + addr := sdk.AccAddress(pubkey.Address()) + balance := sdk.NewCoins(types.NewPhotonCoin(sdk.OneInt())) + baseAcc := auth.NewBaseAccount(addr, balance, pubkey, 10, 50) + suite.account = &types.EthAccount{ + BaseAccount: baseAcc, + CodeHash: []byte{1, 2}, + } +} + +func TestAccountTestSuite(t *testing.T) { + suite.Run(t, new(AccountTestSuite)) +} + +func (suite *AccountTestSuite) TestEthAccount_Balance() { + + testCases := []struct { + name string + denom string + initialCoins sdk.Coins + amount sdk.Int + }{ + {"positive diff", types.AttoPhoton, sdk.Coins{}, sdk.OneInt()}, + {"zero diff, same coin", types.AttoPhoton, sdk.NewCoins(types.NewPhotonCoin(sdk.ZeroInt())), sdk.ZeroInt()}, + {"zero diff, other coin", sdk.DefaultBondDenom, sdk.NewCoins(types.NewPhotonCoin(sdk.ZeroInt())), sdk.ZeroInt()}, + {"negative diff", types.AttoPhoton, sdk.NewCoins(types.NewPhotonCoin(sdk.NewInt(10))), sdk.NewInt(1)}, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.SetupTest() // reset values + suite.account.SetCoins(tc.initialCoins) + + suite.account.SetBalance(tc.denom, tc.amount) + suite.Require().Equal(tc.amount, suite.account.Balance(tc.denom)) + }) + } + +} + +func (suite *AccountTestSuite) TestEthermintAccountJSON() { + bz, err := json.Marshal(suite.account) + suite.Require().NoError(err) + + bz1, err := suite.account.MarshalJSON() + suite.Require().NoError(err) + suite.Require().Equal(string(bz1), string(bz)) + + var a types.EthAccount + suite.Require().NoError(a.UnmarshalJSON(bz)) + suite.Require().Equal(suite.account.String(), a.String()) + suite.Require().Equal(suite.account.PubKey, a.PubKey) +} + +func (suite *AccountTestSuite) TestEthermintPubKeyJSON() { + privkey, err := crypto.GenerateKey() + suite.Require().NoError(err) + bz := privkey.PubKey().Bytes() + + pubk, err := tmamino.PubKeyFromBytes(bz) + suite.Require().NoError(err) + suite.Require().Equal(pubk, privkey.PubKey()) +} + +func (suite *AccountTestSuite) TestSecpPubKeyJSON() { + pubkey := secp256k1.GenPrivKey().PubKey() + bz := pubkey.Bytes() + + pubk, err := tmamino.PubKeyFromBytes(bz) + suite.Require().NoError(err) + suite.Require().Equal(pubk, pubkey) +} + +func (suite *AccountTestSuite) TestEthermintAccount_String() { + config := sdk.GetConfig() + types.SetBech32Prefixes(config) + + bech32pubkey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, suite.account.PubKey) + suite.Require().NoError(err) + + accountStr := fmt.Sprintf(`| + address: %s + eth_address: %s + coins: + - denom: aphoton + amount: "1" + public_key: %s + account_number: 10 + sequence: 50 + code_hash: "0102" +`, suite.account.Address, suite.account.EthAddress().String(), bech32pubkey) + + suite.Require().Equal(accountStr, suite.account.String()) + + i, err := suite.account.MarshalYAML() + suite.Require().NoError(err) + + var ok bool + accountStr, ok = i.(string) + suite.Require().True(ok) + suite.Require().Contains(accountStr, suite.account.Address.String()) + suite.Require().Contains(accountStr, bech32pubkey) +} + +func (suite *AccountTestSuite) TestEthermintAccount_MarshalJSON() { + bz, err := suite.account.MarshalJSON() + suite.Require().NoError(err) + suite.Require().Contains(string(bz), suite.account.EthAddress().String()) + + res := new(types.EthAccount) + err = res.UnmarshalJSON(bz) + suite.Require().NoError(err) + suite.Require().Equal(suite.account, res) + + bech32pubkey, err := sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, suite.account.PubKey) + suite.Require().NoError(err) + + // test that the sdk.AccAddress is populated from the hex address + jsonAcc := fmt.Sprintf( + `{"address":"","eth_address":"%s","coins":[{"denom":"aphoton","amount":"1"}],"public_key":"%s","account_number":10,"sequence":50,"code_hash":"0102"}`, + suite.account.EthAddress().String(), bech32pubkey, + ) + + res = new(types.EthAccount) + err = res.UnmarshalJSON([]byte(jsonAcc)) + suite.Require().NoError(err) + suite.Require().Equal(suite.account.Address.String(), res.Address.String()) + + jsonAcc = fmt.Sprintf( + `{"address":"","eth_address":"","coins":[{"denom":"aphoton","amount":"1"}],"public_key":"%s","account_number":10,"sequence":50,"code_hash":"0102"}`, + bech32pubkey, + ) + + res = new(types.EthAccount) + err = res.UnmarshalJSON([]byte(jsonAcc)) + suite.Require().Error(err, "should fail if both address are empty") + + // test that the sdk.AccAddress is populated from the hex address + jsonAcc = fmt.Sprintf( + `{"address": "%s","eth_address":"0x0000000000000000000000000000000000000000","coins":[{"denom":"aphoton","amount":"1"}],"public_key":"%s","account_number":10,"sequence":50,"code_hash":"0102"}`, + suite.account.Address.String(), bech32pubkey, + ) + + res = new(types.EthAccount) + err = res.UnmarshalJSON([]byte(jsonAcc)) + suite.Require().Error(err, "should fail if addresses mismatch") +} diff --git a/types/chain_id.go b/types/chain_id.go new file mode 100644 index 0000000000..7bc316b12f --- /dev/null +++ b/types/chain_id.go @@ -0,0 +1,48 @@ +package types + +import ( + "fmt" + "math/big" + "regexp" + "strings" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +var ( + regexChainID = `[a-z]*` + regexSeparator = `-{1}` + regexEpoch = `[1-9][0-9]*` + ethermintChainID = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, regexChainID, regexSeparator, regexEpoch)) +) + +// IsValidChainID returns false if the given chain identifier is incorrectly formatted. +func IsValidChainID(chainID string) bool { + if len(chainID) > 48 { + return false + } + + return ethermintChainID.MatchString(chainID) +} + +// ParseChainID parses a string chain identifier's epoch to an Ethereum-compatible +// chain-id in *big.Int format. The function returns an error if the chain-id has an invalid format +func ParseChainID(chainID string) (*big.Int, error) { + chainID = strings.TrimSpace(chainID) + if len(chainID) > 48 { + return nil, sdkerrors.Wrapf(ErrInvalidChainID, "chain-id '%s' cannot exceed 48 chars", chainID) + } + + matches := ethermintChainID.FindStringSubmatch(chainID) + if matches == nil || len(matches) != 3 || matches[1] == "" { + return nil, sdkerrors.Wrap(ErrInvalidChainID, chainID) + } + + // verify that the chain-id entered is a base 10 integer + chainIDInt, ok := new(big.Int).SetString(matches[2], 10) + if !ok { + return nil, sdkerrors.Wrapf(ErrInvalidChainID, "epoch %s must be base-10 integer format", matches[2]) + } + + return chainIDInt, nil +} diff --git a/types/chain_id_test.go b/types/chain_id_test.go new file mode 100644 index 0000000000..63a6b90250 --- /dev/null +++ b/types/chain_id_test.go @@ -0,0 +1,75 @@ +package types + +import ( + "math/big" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParseChainID(t *testing.T) { + testCases := []struct { + name string + chainID string + expError bool + expInt *big.Int + }{ + { + "valid chain-id, single digit", "ethermint-1", false, big.NewInt(1), + }, + { + "valid chain-id, multiple digits", "aragonchain-256", false, big.NewInt(256), + }, + { + "invalid chain-id, double dash", "aragon-chain-1", true, nil, + }, + { + "invalid chain-id, dash only", "-", true, nil, + }, + { + "invalid chain-id, undefined", "-1", true, nil, + }, + { + "invalid chain-id, uppercases", "ETHERMINT-1", true, nil, + }, + { + "invalid chain-id, mixed cases", "Ethermint-1", true, nil, + }, + { + "invalid chain-id, special chars", "$&*#!-1", true, nil, + }, + { + "invalid epoch, cannot start with 0", "ethermint-001", true, nil, + }, + { + "invalid epoch, cannot invalid base", "ethermint-0x212", true, nil, + }, + { + "invalid epoch, non-integer", "ethermint-ethermint", true, nil, + }, + { + "invalid epoch, undefined", "ethermint-", true, nil, + }, + { + "blank chain ID", " ", true, nil, + }, + { + "empty chain ID", "", true, nil, + }, + { + "long chain-id", "ethermint-" + strings.Repeat("1", 40), true, nil, + }, + } + + for _, tc := range testCases { + chainIDEpoch, err := ParseChainID(tc.chainID) + if tc.expError { + require.Error(t, err, tc.name) + require.Nil(t, chainIDEpoch) + } else { + require.NoError(t, err, tc.name) + require.Equal(t, tc.expInt, chainIDEpoch, tc.name) + } + } +} diff --git a/types/code.go b/types/code.go new file mode 100644 index 0000000000..fa1da75dfb --- /dev/null +++ b/types/code.go @@ -0,0 +1,12 @@ +package types + +// ---------------------------------------------------------------------------- +// Code +// ---------------------------------------------------------------------------- + +// Code is account Code type alias +type Code []byte + +func (c Code) String() string { + return string(c) +} diff --git a/types/codec.go b/types/codec.go index e1855df9d3..e793e0ecff 100644 --- a/types/codec.go +++ b/types/codec.go @@ -4,14 +4,13 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ) -var typesCodec = codec.New() - -func init() { - RegisterCodec(typesCodec) -} +const ( + // EthAccountName is the amino encoding name for EthAccount + EthAccountName = "ethermint/EthAccount" +) -// RegisterCodec registers all the necessary types with amino for the given -// codec. +// RegisterCodec registers the account interfaces and concrete types on the +// provided Amino codec. func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(&Account{}, "types/Account", nil) + cdc.RegisterConcrete(&EthAccount{}, EthAccountName, nil) } diff --git a/types/coin.go b/types/coin.go new file mode 100644 index 0000000000..d7e396ed19 --- /dev/null +++ b/types/coin.go @@ -0,0 +1,38 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + // AttoPhoton defines the default coin denomination used in Ethermint in: + // + // - Staking parameters: denomination used as stake in the dPoS chain + // - Mint parameters: denomination minted due to fee distribution rewards + // - Governance parameters: denomination used for spam prevention in proposal deposits + // - Crisis parameters: constant fee denomination used for spam prevention to check broken invariant + // - EVM parameters: denomination used for running EVM state transitions in Ethermint. + AttoPhoton string = "aphoton" + + // BaseDenomUnit defines the base denomination unit for Photons. + // 1 photon = 1x10^{BaseDenomUnit} aphoton + BaseDenomUnit = 18 +) + +// NewPhotonCoin is a utility function that returns an "aphoton" coin with the given sdk.Int amount. +// The function will panic if the provided amount is negative. +func NewPhotonCoin(amount sdk.Int) sdk.Coin { + return sdk.NewCoin(AttoPhoton, amount) +} + +// NewPhotonDecCoin is a utility function that returns an "aphoton" decimal coin with the given sdk.Int amount. +// The function will panic if the provided amount is negative. +func NewPhotonDecCoin(amount sdk.Int) sdk.DecCoin { + return sdk.NewDecCoin(AttoPhoton, amount) +} + +// NewPhotonCoinInt64 is a utility function that returns an "aphoton" coin with the given int64 amount. +// The function will panic if the provided amount is negative. +func NewPhotonCoinInt64(amount int64) sdk.Coin { + return sdk.NewInt64Coin(AttoPhoton, amount) +} diff --git a/types/config.go b/types/config.go new file mode 100644 index 0000000000..3595558da1 --- /dev/null +++ b/types/config.go @@ -0,0 +1,42 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + // EthBech32Prefix defines the Bech32 prefix used for EthAccounts + EthBech32Prefix = "eth" + + // Bech32PrefixAccAddr defines the Bech32 prefix of an account's address + Bech32PrefixAccAddr = EthBech32Prefix + // Bech32PrefixAccPub defines the Bech32 prefix of an account's public key + Bech32PrefixAccPub = EthBech32Prefix + sdk.PrefixPublic + // Bech32PrefixValAddr defines the Bech32 prefix of a validator's operator address + Bech32PrefixValAddr = EthBech32Prefix + sdk.PrefixValidator + sdk.PrefixOperator + // Bech32PrefixValPub defines the Bech32 prefix of a validator's operator public key + Bech32PrefixValPub = EthBech32Prefix + sdk.PrefixValidator + sdk.PrefixOperator + sdk.PrefixPublic + // Bech32PrefixConsAddr defines the Bech32 prefix of a consensus node address + Bech32PrefixConsAddr = EthBech32Prefix + sdk.PrefixValidator + sdk.PrefixConsensus + // Bech32PrefixConsPub defines the Bech32 prefix of a consensus node public key + Bech32PrefixConsPub = EthBech32Prefix + sdk.PrefixValidator + sdk.PrefixConsensus + sdk.PrefixPublic + + // Bip44CoinType satisfies EIP84. See https://github.com/ethereum/EIPs/issues/84 for more info. + Bip44CoinType = 60 + + // BIP44HDPath is the BIP44 HD path used on Ethereum. + BIP44HDPath = "44'/60'/0'/0/0" +) + +// SetBech32Prefixes sets the global prefixes to be used when serializing addresses and public keys to Bech32 strings. +func SetBech32Prefixes(config *sdk.Config) { + config.SetBech32PrefixForAccount(Bech32PrefixAccAddr, Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(Bech32PrefixValAddr, Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(Bech32PrefixConsAddr, Bech32PrefixConsPub) +} + +// SetBip44CoinType sets the global coin type to be used in hierarchical deterministic wallets. +func SetBip44CoinType(config *sdk.Config) { + config.SetCoinType(Bip44CoinType) + config.SetFullFundraiserPath(BIP44HDPath) +} diff --git a/types/config_test.go b/types/config_test.go new file mode 100644 index 0000000000..40ce3c0729 --- /dev/null +++ b/types/config_test.go @@ -0,0 +1,46 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestSetBech32Prefixes(t *testing.T) { + config := sdk.GetConfig() + + require.Equal(t, sdk.Bech32PrefixAccAddr, config.GetBech32AccountAddrPrefix()) + require.Equal(t, sdk.Bech32PrefixAccPub, config.GetBech32AccountPubPrefix()) + require.Equal(t, sdk.Bech32PrefixValAddr, config.GetBech32ValidatorAddrPrefix()) + require.Equal(t, sdk.Bech32PrefixValPub, config.GetBech32ValidatorPubPrefix()) + require.Equal(t, sdk.Bech32PrefixConsAddr, config.GetBech32ConsensusAddrPrefix()) + require.Equal(t, sdk.Bech32PrefixConsPub, config.GetBech32ConsensusPubPrefix()) + + SetBech32Prefixes(config) + require.Equal(t, Bech32PrefixAccAddr, config.GetBech32AccountAddrPrefix()) + require.Equal(t, Bech32PrefixAccPub, config.GetBech32AccountPubPrefix()) + require.Equal(t, Bech32PrefixValAddr, config.GetBech32ValidatorAddrPrefix()) + require.Equal(t, Bech32PrefixValPub, config.GetBech32ValidatorPubPrefix()) + require.Equal(t, Bech32PrefixConsAddr, config.GetBech32ConsensusAddrPrefix()) + require.Equal(t, Bech32PrefixConsPub, config.GetBech32ConsensusPubPrefix()) + + require.Equal(t, sdk.GetConfig().GetBech32AccountAddrPrefix(), config.GetBech32AccountAddrPrefix()) + require.Equal(t, sdk.GetConfig().GetBech32AccountPubPrefix(), config.GetBech32AccountPubPrefix()) + require.Equal(t, sdk.GetConfig().GetBech32ValidatorAddrPrefix(), config.GetBech32ValidatorAddrPrefix()) + require.Equal(t, sdk.GetConfig().GetBech32ValidatorPubPrefix(), config.GetBech32ValidatorPubPrefix()) + require.Equal(t, sdk.GetConfig().GetBech32ConsensusAddrPrefix(), config.GetBech32ConsensusAddrPrefix()) + require.Equal(t, sdk.GetConfig().GetBech32ConsensusPubPrefix(), config.GetBech32ConsensusPubPrefix()) +} + +func TestSetCoinType(t *testing.T) { + config := sdk.GetConfig() + require.Equal(t, sdk.CoinType, int(config.GetCoinType())) + require.Equal(t, sdk.FullFundraiserPath, config.GetFullFundraiserPath()) + + SetBip44CoinType(config) + require.Equal(t, Bip44CoinType, int(config.GetCoinType())) + require.Equal(t, sdk.GetConfig().GetCoinType(), config.GetCoinType()) + require.Equal(t, sdk.GetConfig().GetFullFundraiserPath(), config.GetFullFundraiserPath()) +} diff --git a/types/context.go b/types/context.go deleted file mode 100644 index f9943fe405..0000000000 --- a/types/context.go +++ /dev/null @@ -1,8 +0,0 @@ -package types - -// AppContext provides the ability for the application to pass around and -// obtain immutable objects easily. More importantly, it allows for the -// utilization of the object-capability model in which components gain access -// to other components for which they truly need. -type AppContext struct { -} diff --git a/types/errors.go b/types/errors.go index 2eb6d2df6f..b36a1b5f14 100644 --- a/types/errors.go +++ b/types/errors.go @@ -1,37 +1,23 @@ package types import ( - sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -// Ethermint error codes const ( - // DefaultCodespace reserves a Codespace for Ethermint. - DefaultCodespace sdk.CodespaceType = "ethermint" - - CodeInvalidValue sdk.CodeType = 1 - CodeInvalidChainID sdk.CodeType = 2 + // RootCodespace is the codespace for all errors defined in this package + RootCodespace = "ethermint" ) -func codeToDefaultMsg(code sdk.CodeType) string { - switch code { - case CodeInvalidValue: - return "invalid value" - case CodeInvalidChainID: - return "invalid chain ID" - default: - return sdk.CodeToDefaultMsg(code) - } -} +// NOTE: We can't use 1 since that error code is reserved for internal errors. + +var ( + // ErrInvalidValue returns an error resulting from an invalid value. + ErrInvalidValue = sdkerrors.Register(RootCodespace, 2, "invalid value") -// ErrInvalidValue returns a standardized SDK error resulting from an invalid -// value. -func ErrInvalidValue(msg string) sdk.Error { - return sdk.NewError(DefaultCodespace, CodeInvalidValue, msg) -} + // ErrInvalidChainID returns an error resulting from an invalid chain ID. + ErrInvalidChainID = sdkerrors.Register(RootCodespace, 3, "invalid chain ID") -// ErrInvalidChainID returns a standardized SDK error resulting from an invalid -// chain ID. -func ErrInvalidChainID(msg string) sdk.Error { - return sdk.NewError(DefaultCodespace, CodeInvalidChainID, msg) -} + // ErrVMExecution returns an error resulting from an error in EVM execution. + ErrVMExecution = sdkerrors.Register(RootCodespace, 4, "error while executing evm transaction") +) diff --git a/types/params.go b/types/params.go new file mode 100644 index 0000000000..de577b1b9d --- /dev/null +++ b/types/params.go @@ -0,0 +1,8 @@ +package types + +const ( + // DefaultGasPrice is default gas price for evm transactions + DefaultGasPrice = 20 + // DefaultRPCGasLimit is default gas limit for RPC call operations + DefaultRPCGasLimit = 10000000 +) diff --git a/types/types.proto b/types/types.proto new file mode 100644 index 0000000000..a0a82c0cf8 --- /dev/null +++ b/types/types.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; +package ethermint.v1; + +import "third_party/proto/gogoproto/gogo.proto"; +import "third_party/proto/cosmos-sdk/x/auth/types/types.proto"; + +option go_package = "github.com/cosmos/ethermint/types"; + + +// EthAccount implements the auth.Account interface and embeds an +// auth.BaseAccount type. It is compatible with the auth.AccountKeeper. +message EthAccount { + option (gogoproto.goproto_getters) = false; + option (gogoproto.goproto_stringer) = false; + + cosmos_sdk.x.auth.v1.BaseAccount base_account = 1 [ + (gogoproto.embed) = true, + (gogoproto.moretags) = "yaml:\"base_account\"" + ]; + bytes code_hash = 2 [ + (gogoproto.moretags) = "yaml:\"code_hash\"" + ]; +} \ No newline at end of file diff --git a/utils/int.go b/utils/int.go new file mode 100644 index 0000000000..5a493e5946 --- /dev/null +++ b/utils/int.go @@ -0,0 +1,42 @@ +package utils + +import "math/big" + +// MarshalBigInt marshalls big int into text string for consistent encoding +func MarshalBigInt(i *big.Int) (string, error) { + bz, err := i.MarshalText() + if err != nil { + return "", err + } + return string(bz), nil +} + +// MustMarshalBigInt marshalls big int into text string for consistent encoding. +// It panics if an error is encountered. +func MustMarshalBigInt(i *big.Int) string { + str, err := MarshalBigInt(i) + if err != nil { + panic(err) + } + return str +} + +// UnmarshalBigInt unmarshalls string from *big.Int +func UnmarshalBigInt(s string) (*big.Int, error) { + ret := new(big.Int) + err := ret.UnmarshalText([]byte(s)) + if err != nil { + return nil, err + } + return ret, nil +} + +// MustUnmarshalBigInt unmarshalls string from *big.Int. +// It panics if an error is encountered. +func MustUnmarshalBigInt(s string) *big.Int { + ret, err := UnmarshalBigInt(s) + if err != nil { + panic(err) + } + return ret +} diff --git a/utils/int_test.go b/utils/int_test.go new file mode 100644 index 0000000000..263d0802a3 --- /dev/null +++ b/utils/int_test.go @@ -0,0 +1,18 @@ +package utils + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestMarshalAndUnmarshalInt(t *testing.T) { + i := big.NewInt(3) + m, err := MarshalBigInt(i) + require.NoError(t, err) + + i2, err := UnmarshalBigInt(m) + require.NoError(t, err) + require.Equal(t, i, i2) +} diff --git a/version/version.go b/version/version.go index 015a5b96b1..b969a7fda4 100644 --- a/version/version.go +++ b/version/version.go @@ -15,7 +15,7 @@ const AppName = "Ethermint" const Version = "0.0.0" // ProtocolVersion is the supported Ethereum protocol version (e.g., Homestead, Olympic, etc.) -const ProtocolVersion = "63" +const ProtocolVersion = 63 // GitCommit contains the git SHA1 short hash set by build flags. var GitCommit = "" diff --git a/x/evm/abci.go b/x/evm/abci.go new file mode 100644 index 0000000000..61c40c7bb9 --- /dev/null +++ b/x/evm/abci.go @@ -0,0 +1,49 @@ +package evm + +import ( + "math/big" + + abci "github.com/tendermint/tendermint/abci/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + + ethtypes "github.com/ethereum/go-ethereum/core/types" +) + +// BeginBlock sets the block hash -> block height map and resets the Bloom filter and +// the transaction count to 0. +func BeginBlock(k Keeper, ctx sdk.Context, req abci.RequestBeginBlock) { + if req.Header.LastBlockId.GetHash() == nil || req.Header.GetHeight() < 1 { + return + } + + k.SetBlockHash(ctx, req.Header.LastBlockId.GetHash(), req.Header.GetHeight()-1) + + // reset counters + k.Bloom = big.NewInt(0) + k.TxCount = 0 +} + +// EndBlock updates the accounts and commits states objects to the KV Store. +// +func EndBlock(k Keeper, ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + // Gas costs are handled within msg handler so costs should be ignored + ctx = ctx.WithBlockGasMeter(sdk.NewInfiniteGasMeter()) + + // Update account balances before committing other parts of state + k.UpdateAccounts(ctx) + + // Commit state objects to KV store + _, err := k.Commit(ctx, true) + if err != nil { + panic(err) + } + + // Clear accounts cache after account data has been committed + k.ClearStateObjects(ctx) + + bloom := ethtypes.BytesToBloom(k.Bloom.Bytes()) + k.SetBlockBloom(ctx, ctx.BlockHeight(), bloom) + + return []abci.ValidatorUpdate{} +} diff --git a/x/evm/alias.go b/x/evm/alias.go new file mode 100644 index 0000000000..32bc4647d6 --- /dev/null +++ b/x/evm/alias.go @@ -0,0 +1,26 @@ +package evm + +import ( + "github.com/cosmos/ethermint/x/evm/keeper" + "github.com/cosmos/ethermint/x/evm/types" +) + +// nolint +const ( + ModuleName = types.ModuleName + StoreKey = types.StoreKey + RouterKey = types.RouterKey + DefaultParamspace = types.DefaultParamspace +) + +// nolint +var ( + NewKeeper = keeper.NewKeeper + TxDecoder = types.TxDecoder +) + +//nolint +type ( + Keeper = keeper.Keeper + GenesisState = types.GenesisState +) diff --git a/x/evm/client/cli/query.go b/x/evm/client/cli/query.go new file mode 100644 index 0000000000..f274a20eaa --- /dev/null +++ b/x/evm/client/cli/query.go @@ -0,0 +1,88 @@ +package cli + +import ( + "fmt" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" + + "github.com/cosmos/ethermint/x/evm/types" +) + +// GetQueryCmd defines evm module queries through the cli +func GetQueryCmd(moduleName string, cdc *codec.Codec) *cobra.Command { + evmQueryCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the evm module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + evmQueryCmd.AddCommand(flags.GetCommands( + GetCmdGetStorageAt(moduleName, cdc), + GetCmdGetCode(moduleName, cdc), + )...) + return evmQueryCmd +} + +// GetCmdGetStorageAt queries a key in an accounts storage +func GetCmdGetStorageAt(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "storage [account] [key]", + Short: "Gets storage for an account at a given key", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + account, err := accountToHex(args[0]) + if err != nil { + return errors.Wrap(err, "could not parse account address") + } + + key := formatKeyToHash(args[1]) + + res, _, err := cliCtx.Query( + fmt.Sprintf("custom/%s/storage/%s/%s", queryRoute, account, key)) + + if err != nil { + return fmt.Errorf("could not resolve: %s", err) + } + var out types.QueryResStorage + cdc.MustUnmarshalJSON(res, &out) + return cliCtx.PrintOutput(out) + }, + } +} + +// GetCmdGetCode queries the code field of a given address +func GetCmdGetCode(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "code [account]", + Short: "Gets code from an account", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + account, err := accountToHex(args[0]) + if err != nil { + return errors.Wrap(err, "could not parse account address") + } + + res, _, err := cliCtx.Query( + fmt.Sprintf("custom/%s/code/%s", queryRoute, account)) + + if err != nil { + return fmt.Errorf("could not resolve: %s", err) + } + + var out types.QueryResCode + cdc.MustUnmarshalJSON(res, &out) + return cliCtx.PrintOutput(out) + }, + } +} diff --git a/x/evm/client/cli/tx.go b/x/evm/client/cli/tx.go new file mode 100644 index 0000000000..84258ac683 --- /dev/null +++ b/x/evm/client/cli/tx.go @@ -0,0 +1,164 @@ +package cli + +import ( + "bufio" + "fmt" + "strconv" + "strings" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + emint "github.com/cosmos/ethermint/types" + "github.com/cosmos/ethermint/x/evm/types" +) + +// GetTxCmd defines the CLI commands regarding evm module transactions +func GetTxCmd(cdc *codec.Codec) *cobra.Command { + evmTxCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "EVM transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + evmTxCmd.AddCommand(flags.PostCommands( + GetCmdSendTx(cdc), + GetCmdGenCreateTx(cdc), + )...) + + return evmTxCmd +} + +// GetCmdSendTx generates an Ethermint transaction (excludes create operations) +func GetCmdSendTx(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "send [to_address] [amount (in aphotons)] []", + Short: "send transaction to address (call operations included)", + Args: cobra.RangeArgs(2, 3), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(authclient.GetTxEncoder(cdc)) + + toAddr, err := cosmosAddressFromArg(args[0]) + if err != nil { + return errors.Wrap(err, "must provide a valid Bech32 address for to_address") + } + + // Ambiguously decode amount from any base + amount, err := strconv.ParseInt(args[1], 0, 64) + if err != nil { + return err + } + + var data []byte + if len(args) > 2 { + payload := args[2] + if !strings.HasPrefix(payload, "0x") { + payload = "0x" + payload + } + + data, err = hexutil.Decode(payload) + if err != nil { + return err + } + } + + from := cliCtx.GetFromAddress() + + _, seq, err := authtypes.NewAccountRetriever(cliCtx).GetAccountNumberSequence(from) + if err != nil { + return errors.Wrap(err, "Could not retrieve account sequence") + } + + // TODO: Potentially allow overriding of gas price and gas limit + msg := types.NewMsgEthermint(seq, &toAddr, sdk.NewInt(amount), txBldr.Gas(), + sdk.NewInt(emint.DefaultGasPrice), data, from) + + err = msg.ValidateBasic() + if err != nil { + return err + } + + return authclient.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + }, + } +} + +// GetCmdGenCreateTx generates an Ethermint transaction (excludes create operations) +func GetCmdGenCreateTx(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "create [contract bytecode] []", + Short: "create contract through the evm using compiled bytecode", + Args: cobra.RangeArgs(1, 2), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + inBuf := bufio.NewReader(cmd.InOrStdin()) + + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(authclient.GetTxEncoder(cdc)) + + payload := args[0] + if !strings.HasPrefix(payload, "0x") { + payload = "0x" + payload + } + + data, err := hexutil.Decode(payload) + if err != nil { + return err + } + + var amount int64 + if len(args) > 1 { + // Ambiguously decode amount from any base + amount, err = strconv.ParseInt(args[1], 0, 64) + if err != nil { + return errors.Wrap(err, "invalid amount") + } + } + + from := cliCtx.GetFromAddress() + + _, seq, err := authtypes.NewAccountRetriever(cliCtx).GetAccountNumberSequence(from) + if err != nil { + return errors.Wrap(err, "Could not retrieve account sequence") + } + + // TODO: Potentially allow overriding of gas price and gas limit + msg := types.NewMsgEthermint(seq, nil, sdk.NewInt(amount), txBldr.Gas(), + sdk.NewInt(emint.DefaultGasPrice), data, from) + + err = msg.ValidateBasic() + if err != nil { + return err + } + + if err = authclient.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}); err != nil { + return err + } + + contractAddr := ethcrypto.CreateAddress(common.BytesToAddress(from.Bytes()), seq) + fmt.Printf( + "Contract will be deployed to: \nHex: %s\nCosmos Address: %s\n", + contractAddr.Hex(), + sdk.AccAddress(contractAddr.Bytes()), + ) + return nil + }, + } +} diff --git a/x/evm/client/cli/utils.go b/x/evm/client/cli/utils.go new file mode 100644 index 0000000000..6114246ca6 --- /dev/null +++ b/x/evm/client/cli/utils.go @@ -0,0 +1,63 @@ +package cli + +import ( + "fmt" + "strings" + + "github.com/pkg/errors" + + "github.com/ethereum/go-ethereum/common" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func accountToHex(addr string) (string, error) { + if strings.HasPrefix(addr, sdk.GetConfig().GetBech32AccountAddrPrefix()) { + // Check to see if address is Cosmos bech32 formatted + toAddr, err := sdk.AccAddressFromBech32(addr) + if err != nil { + return "", errors.Wrap(err, "must provide a valid Bech32 address") + } + ethAddr := common.BytesToAddress(toAddr.Bytes()) + return ethAddr.Hex(), nil + } + + if !strings.HasPrefix(addr, "0x") { + addr = "0x" + addr + } + + valid := common.IsHexAddress(addr) + if !valid { + return "", fmt.Errorf("%s is not a valid Ethereum or Cosmos address", addr) + } + + ethAddr := common.HexToAddress(addr) + + return ethAddr.Hex(), nil +} + +func formatKeyToHash(key string) string { + if !strings.HasPrefix(key, "0x") { + key = "0x" + key + } + + ethkey := common.HexToHash(key) + + return ethkey.Hex() +} + +func cosmosAddressFromArg(addr string) (sdk.AccAddress, error) { + if strings.HasPrefix(addr, sdk.GetConfig().GetBech32AccountAddrPrefix()) { + // Check to see if address is Cosmos bech32 formatted + toAddr, err := sdk.AccAddressFromBech32(addr) + if err != nil { + return nil, errors.Wrap(err, "invalid bech32 formatted address") + } + return toAddr, nil + } + + // Strip 0x prefix if exists + addr = strings.TrimPrefix(addr, "0x") + + return sdk.AccAddressFromHex(addr) +} diff --git a/x/evm/client/cli/utils_test.go b/x/evm/client/cli/utils_test.go new file mode 100644 index 0000000000..8ddb310288 --- /dev/null +++ b/x/evm/client/cli/utils_test.go @@ -0,0 +1,84 @@ +package cli + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/ethereum/go-ethereum/common" +) + +func TestAddressFormats(t *testing.T) { + testCases := []struct { + name string + addrString string + expectedHex string + expectErr bool + }{ + {"Cosmos Address", "cosmos18wvvwfmq77a6d8tza4h5sfuy2yj3jj88yqg82a", "0x3B98c72760f7BBa69D62ED6f48278451251948e7", false}, + {"hex without 0x", "3B98C72760F7BBA69D62ED6F48278451251948E7", "0x3B98c72760f7BBa69D62ED6f48278451251948e7", false}, + {"hex with mixed casing", "3b98C72760f7BBA69D62ED6F48278451251948e7", "0x3B98c72760f7BBa69D62ED6f48278451251948e7", false}, + {"hex with 0x", "0x3B98C72760F7BBA69D62ED6F48278451251948E7", "0x3B98c72760f7BBa69D62ED6f48278451251948e7", false}, + {"invalid hex ethereum address", "0x3B98C72760F7BBA69D62ED6F48278451251948E", "", true}, + {"invalid Cosmos address", "cosmos18wvvwfmq77a6d8tza4h5sfuy2yj3jj88", "", true}, + {"empty string", "", "", true}, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + hex, err := accountToHex(tc.addrString) + require.Equal(t, tc.expectErr, err != nil, err) + + if !tc.expectErr { + require.Equal(t, hex, tc.expectedHex) + } + }) + } +} + +func TestCosmosToEthereumTypes(t *testing.T) { + hexString := "0x3B98D72760f7bbA69d62Ed6F48278451251948E7" + cosmosAddr, err := sdk.AccAddressFromHex(hexString[2:]) + require.NoError(t, err) + + cosmosFormatted := cosmosAddr.String() + + // Test decoding a cosmos formatted address + decodedHex, err := accountToHex(cosmosFormatted) + require.NoError(t, err) + require.Equal(t, hexString, decodedHex) + + // Test converting cosmos address with eth address from hex + hexEth := common.HexToAddress(hexString) + convertedEth := common.BytesToAddress(cosmosAddr.Bytes()) + require.Equal(t, hexEth, convertedEth) + + // Test decoding eth hex output against hex string + ethDecoded, err := accountToHex(hexEth.Hex()) + require.NoError(t, err) + require.Equal(t, hexString, ethDecoded) +} + +func TestAddressToCosmosAddress(t *testing.T) { + baseAddr, err := sdk.AccAddressFromHex("6A98D72760f7bbA69d62Ed6F48278451251948E7") + require.NoError(t, err) + + // Test cosmos string back to address + cosmosFormatted, err := cosmosAddressFromArg(baseAddr.String()) + require.NoError(t, err) + require.Equal(t, baseAddr, cosmosFormatted) + + // Test account address from Ethereum address + ethAddr := common.BytesToAddress(baseAddr.Bytes()) + ethFormatted, err := cosmosAddressFromArg(ethAddr.Hex()) + require.NoError(t, err) + require.Equal(t, baseAddr, ethFormatted) + + // Test encoding without the 0x prefix + ethFormatted, err = cosmosAddressFromArg(ethAddr.Hex()[2:]) + require.NoError(t, err) + require.Equal(t, baseAddr, ethFormatted) +} diff --git a/x/evm/genesis.go b/x/evm/genesis.go new file mode 100644 index 0000000000..da55321d30 --- /dev/null +++ b/x/evm/genesis.go @@ -0,0 +1,88 @@ +package evm + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + emint "github.com/cosmos/ethermint/types" + "github.com/cosmos/ethermint/x/evm/types" + + abci "github.com/tendermint/tendermint/abci/types" +) + +// InitGenesis initializes genesis state based on exported genesis +func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) []abci.ValidatorUpdate { + for _, account := range data.Accounts { + // FIXME: this will override bank InitGenesis balance! + k.SetBalance(ctx, account.Address, account.Balance) + k.SetCode(ctx, account.Address, account.Code) + for _, storage := range account.Storage { + k.SetState(ctx, account.Address, storage.Key, storage.Value) + } + } + + var err error + for _, txLog := range data.TxsLogs { + err = k.SetLogs(ctx, txLog.Hash, txLog.Logs) + if err != nil { + panic(err) + } + } + + k.SetChainConfig(ctx, data.ChainConfig) + k.SetParams(ctx, data.Params) + + // set state objects and code to store + _, err = k.Commit(ctx, false) + if err != nil { + panic(err) + } + + // set storage to store + // NOTE: don't delete empty object to prevent import-export simulation failure + err = k.Finalise(ctx, false) + if err != nil { + panic(err) + } + + return []abci.ValidatorUpdate{} +} + +// ExportGenesis exports genesis state of the EVM module +func ExportGenesis(ctx sdk.Context, k Keeper, ak types.AccountKeeper) GenesisState { + // nolint: prealloc + var ethGenAccounts []types.GenesisAccount + accounts := ak.GetAllAccounts(ctx) + + for _, account := range accounts { + + ethAccount, ok := account.(*emint.EthAccount) + if !ok { + continue + } + + addr := ethAccount.EthAddress() + + storage, err := k.GetAccountStorage(ctx, addr) + if err != nil { + panic(err) + } + + genAccount := types.GenesisAccount{ + Address: addr, + Balance: k.GetBalance(ctx, addr), + Code: k.GetCode(ctx, addr), + Storage: storage, + } + + ethGenAccounts = append(ethGenAccounts, genAccount) + } + + config, _ := k.GetChainConfig(ctx) + + return GenesisState{ + Accounts: ethGenAccounts, + TxsLogs: k.GetAllTxLogs(ctx), + ChainConfig: config, + Params: k.GetParams(ctx), + } +} diff --git a/x/evm/genesis_test.go b/x/evm/genesis_test.go new file mode 100644 index 0000000000..0bc5b0946e --- /dev/null +++ b/x/evm/genesis_test.go @@ -0,0 +1,64 @@ +package evm_test + +import ( + "crypto/ecdsa" + "math/big" + + "github.com/cosmos/ethermint/crypto" + "github.com/cosmos/ethermint/x/evm" + "github.com/cosmos/ethermint/x/evm/types" + + "github.com/ethereum/go-ethereum/common" +) + +func (suite *EvmTestSuite) TestExportImport() { + var genState types.GenesisState + suite.Require().NotPanics(func() { + genState = evm.ExportGenesis(suite.ctx, suite.app.EvmKeeper, suite.app.AccountKeeper) + }) + + _ = evm.InitGenesis(suite.ctx, suite.app.EvmKeeper, genState) +} + +func (suite *EvmTestSuite) TestContractExportImport() { + gasLimit := uint64(5000000) + gasPrice := big.NewInt(1) + + priv, err := crypto.GenerateKey() + suite.Require().NoError(err, "failed to create key") + + ensFactoryCode := common.FromHex("0x608060405234801561001057600080fd5b50612033806100206000396000f3006080604052600436106100405763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663e9358b018114610045575b600080fd5b34801561005157600080fd5b5061007373ffffffffffffffffffffffffffffffffffffffff6004351661009c565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b60008060006100a961057f565b604051809103906000f0801580156100c5573d6000803e3d6000fd5b50604080517f06ab59230000000000000000000000000000000000000000000000000000000081526000600482018190527f4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f06024830152306044830152915192945073ffffffffffffffffffffffffffffffffffffffff8516926306ab59239260648084019391929182900301818387803b15801561016357600080fd5b505af1158015610177573d6000803e3d6000fd5b505050508161018461058f565b73ffffffffffffffffffffffffffffffffffffffff909116815260405190819003602001906000f0801580156101be573d6000803e3d6000fd5b50604080517f06ab59230000000000000000000000000000000000000000000000000000000081527f93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae60048201527f329539a1d23af1810c48a07fe7fc66a3b34fbc8b37e9b3cdb97bb88ceab7e4bf6024820152306044820152905191925073ffffffffffffffffffffffffffffffffffffffff8416916306ab59239160648082019260009290919082900301818387803b15801561027c57600080fd5b505af1158015610290573d6000803e3d6000fd5b5050604080517f1896f70a0000000000000000000000000000000000000000000000000000000081527ffdd5d5de6dd63db72bbc2d487944ba13bf775b50a80805fe6fcaba9b0fba88f5600482015273ffffffffffffffffffffffffffffffffffffffff858116602483015291519186169350631896f70a925060448082019260009290919082900301818387803b15801561032b57600080fd5b505af115801561033f573d6000803e3d6000fd5b5050604080517fd5fa2b000000000000000000000000000000000000000000000000000000000081527ffdd5d5de6dd63db72bbc2d487944ba13bf775b50a80805fe6fcaba9b0fba88f5600482015273ffffffffffffffffffffffffffffffffffffffff851660248201819052915191935063d5fa2b00925060448082019260009290919082900301818387803b1580156103d957600080fd5b505af11580156103ed573d6000803e3d6000fd5b5050604080517f5b0fc9c30000000000000000000000000000000000000000000000000000000081527f93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae600482015273ffffffffffffffffffffffffffffffffffffffff888116602483015291519186169350635b0fc9c3925060448082019260009290919082900301818387803b15801561048857600080fd5b505af115801561049c573d6000803e3d6000fd5b5050604080517f5b0fc9c300000000000000000000000000000000000000000000000000000000815260006004820181905273ffffffffffffffffffffffffffffffffffffffff898116602484015292519287169450635b0fc9c39350604480830193919282900301818387803b15801561051657600080fd5b505af115801561052a573d6000803e3d6000fd5b50506040805173ffffffffffffffffffffffffffffffffffffffff8616815290517fdbfb5ababf63f86424e8df6053dfb90f8b63ea26d7e1e8f68407af4fb2d2c4f29350908190036020019150a15092915050565b60405161064a806105a083390190565b60405161141e80610bea833901905600608060405234801561001057600080fd5b5060008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb58054600160a060020a031916331790556105f1806100596000396000f3006080604052600436106100825763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630178b8bf811461008757806302571be3146100c857806306ab5923146100e057806314ab90381461011657806316a25cbd1461013b5780631896f70a146101705780635b0fc9c3146101a1575b600080fd5b34801561009357600080fd5b5061009f6004356101d2565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b3480156100d457600080fd5b5061009f6004356101fd565b3480156100ec57600080fd5b5061011460043560243573ffffffffffffffffffffffffffffffffffffffff60443516610225565b005b34801561012257600080fd5b5061011460043567ffffffffffffffff60243516610311565b34801561014757600080fd5b506101536004356103e7565b6040805167ffffffffffffffff9092168252519081900360200190f35b34801561017c57600080fd5b5061011460043573ffffffffffffffffffffffffffffffffffffffff6024351661041e565b3480156101ad57600080fd5b5061011460043573ffffffffffffffffffffffffffffffffffffffff602435166104f3565b60009081526020819052604090206001015473ffffffffffffffffffffffffffffffffffffffff1690565b60009081526020819052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b600083815260208190526040812054849073ffffffffffffffffffffffffffffffffffffffff16331461025757600080fd5b6040805186815260208082018790528251918290038301822073ffffffffffffffffffffffffffffffffffffffff871683529251929450869288927fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8292908290030190a350600090815260208190526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff929092169190911790555050565b600082815260208190526040902054829073ffffffffffffffffffffffffffffffffffffffff16331461034357600080fd5b6040805167ffffffffffffffff84168152905184917f1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa68919081900360200190a250600091825260208290526040909120600101805467ffffffffffffffff90921674010000000000000000000000000000000000000000027fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b60009081526020819052604090206001015474010000000000000000000000000000000000000000900467ffffffffffffffff1690565b600082815260208190526040902054829073ffffffffffffffffffffffffffffffffffffffff16331461045057600080fd5b6040805173ffffffffffffffffffffffffffffffffffffffff84168152905184917f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0919081900360200190a25060009182526020829052604090912060010180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055565b600082815260208190526040902054829073ffffffffffffffffffffffffffffffffffffffff16331461052557600080fd5b6040805173ffffffffffffffffffffffffffffffffffffffff84168152905184917fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266919081900360200190a25060009182526020829052604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790555600a165627a7a723058203213a96a5c5e630e44a93f7fa415f3c625e46c7a560debc4dcf02cff9018ee6e0029608060405234801561001057600080fd5b5060405160208061141e833981016040525160008054600160a060020a03909216600160a060020a03199092169190911790556113cc806100526000396000f3006080604052600436106100c45763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166301ffc9a781146100c957806310f13a8c146101175780632203ab56146101b557806329cd62ea1461024f5780632dff69411461026d5780633b3b57de1461029757806359d1d43c146102d8578063623195b0146103ab578063691f34311461040b5780637737221314610423578063c3d014d614610481578063c86902331461049c578063d5fa2b00146104cd575b600080fd5b3480156100d557600080fd5b506101037fffffffff00000000000000000000000000000000000000000000000000000000600435166104fe565b604080519115158252519081900360200190f35b34801561012357600080fd5b5060408051602060046024803582810135601f81018590048502860185019096528585526101b395833595369560449491939091019190819084018382808284375050604080516020601f89358b018035918201839004830284018301909452808352979a9998810197919650918201945092508291508401838280828437509497506107139650505050505050565b005b3480156101c157600080fd5b506101d060043560243561098f565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156102135781810151838201526020016101fb565b50505050905090810190601f1680156102405780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b34801561025b57600080fd5b506101b3600435602435604435610a9b565b34801561027957600080fd5b50610285600435610bcb565b60408051918252519081900360200190f35b3480156102a357600080fd5b506102af600435610be1565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b3480156102e457600080fd5b5060408051602060046024803582810135601f8101859004850286018501909652858552610336958335953695604494919390910191908190840183828082843750949750610c099650505050505050565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610370578181015183820152602001610358565b50505050905090810190601f16801561039d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156103b757600080fd5b50604080516020600460443581810135601f81018490048402850184019095528484526101b3948235946024803595369594606494920191908190840183828082843750949750610d309650505050505050565b34801561041757600080fd5b50610336600435610e61565b34801561042f57600080fd5b5060408051602060046024803582810135601f81018590048502860185019096528585526101b3958335953695604494919390910191908190840183828082843750949750610f059650505050505050565b34801561048d57600080fd5b506101b360043560243561108b565b3480156104a857600080fd5b506104b460043561119c565b6040805192835260208301919091528051918290030190f35b3480156104d957600080fd5b506101b360043573ffffffffffffffffffffffffffffffffffffffff602435166111b9565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f3b3b57de00000000000000000000000000000000000000000000000000000000148061059157507fffffffff0000000000000000000000000000000000000000000000000000000082167fd8389dc500000000000000000000000000000000000000000000000000000000145b806105dd57507fffffffff0000000000000000000000000000000000000000000000000000000082167f691f343100000000000000000000000000000000000000000000000000000000145b8061062957507fffffffff0000000000000000000000000000000000000000000000000000000082167f2203ab5600000000000000000000000000000000000000000000000000000000145b8061067557507fffffffff0000000000000000000000000000000000000000000000000000000082167fc869023300000000000000000000000000000000000000000000000000000000145b806106c157507fffffffff0000000000000000000000000000000000000000000000000000000082167f59d1d43c00000000000000000000000000000000000000000000000000000000145b8061070d57507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b60008054604080517f02571be30000000000000000000000000000000000000000000000000000000081526004810187905290518693339373ffffffffffffffffffffffffffffffffffffffff16926302571be39260248083019360209383900390910190829087803b15801561078957600080fd5b505af115801561079d573d6000803e3d6000fd5b505050506040513d60208110156107b357600080fd5b505173ffffffffffffffffffffffffffffffffffffffff16146107d557600080fd5b6000848152600160209081526040918290209151855185936005019287929182918401908083835b6020831061083a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107fd565b51815160209384036101000a6000190180199092169116179052920194855250604051938490038101909320845161087b9591949190910192509050611305565b50826040518082805190602001908083835b602083106108ca57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161088d565b51815160209384036101000a60001901801990921691161790526040805192909401829003822081835289518383015289519096508a95507fd8c9334b1a9c2f9da342a0a2b32629c1a229b6445dad78947f674b44444a7550948a94508392908301919085019080838360005b8381101561094f578181015183820152602001610937565b50505050905090810190601f16801561097c5780820380516001836020036101000a031916815260200191505b509250505060405180910390a350505050565b60008281526001602081905260409091206060905b838311610a8e57828416158015906109dd5750600083815260068201602052604081205460026000196101006001841615020190911604115b15610a8357600083815260068201602090815260409182902080548351601f600260001961010060018616150201909316929092049182018490048402810184019094528084529091830182828015610a775780601f10610a4c57610100808354040283529160200191610a77565b820191906000526020600020905b815481529060010190602001808311610a5a57829003601f168201915b50505050509150610a93565b6002909202916109a4565b600092505b509250929050565b60008054604080517f02571be30000000000000000000000000000000000000000000000000000000081526004810187905290518693339373ffffffffffffffffffffffffffffffffffffffff16926302571be39260248083019360209383900390910190829087803b158015610b1157600080fd5b505af1158015610b25573d6000803e3d6000fd5b505050506040513d6020811015610b3b57600080fd5b505173ffffffffffffffffffffffffffffffffffffffff1614610b5d57600080fd5b604080518082018252848152602080820185815260008881526001835284902092516003840155516004909201919091558151858152908101849052815186927f1d6f5e03d3f63eb58751986629a5439baee5079ff04f345becb66e23eb154e46928290030190a250505050565b6000908152600160208190526040909120015490565b60009081526001602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b600082815260016020908152604091829020915183516060936005019285929182918401908083835b60208310610c6f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c32565b518151600019602094850361010090810a820192831692199390931691909117909252949092019687526040805197889003820188208054601f6002600183161590980290950116959095049283018290048202880182019052818752929450925050830182828015610d235780601f10610cf857610100808354040283529160200191610d23565b820191906000526020600020905b815481529060010190602001808311610d0657829003601f168201915b5050505050905092915050565b60008054604080517f02571be30000000000000000000000000000000000000000000000000000000081526004810187905290518693339373ffffffffffffffffffffffffffffffffffffffff16926302571be39260248083019360209383900390910190829087803b158015610da657600080fd5b505af1158015610dba573d6000803e3d6000fd5b505050506040513d6020811015610dd057600080fd5b505173ffffffffffffffffffffffffffffffffffffffff1614610df257600080fd5b6000198301831615610e0357600080fd5b600084815260016020908152604080832086845260060182529091208351610e2d92850190611305565b50604051839085907faa121bbeef5f32f5961a2a28966e769023910fc9479059ee3495d4c1a696efe390600090a350505050565b6000818152600160208181526040928390206002908101805485516000199582161561010002959095011691909104601f81018390048302840183019094528383526060939091830182828015610ef95780601f10610ece57610100808354040283529160200191610ef9565b820191906000526020600020905b815481529060010190602001808311610edc57829003601f168201915b50505050509050919050565b60008054604080517f02571be30000000000000000000000000000000000000000000000000000000081526004810186905290518593339373ffffffffffffffffffffffffffffffffffffffff16926302571be39260248083019360209383900390910190829087803b158015610f7b57600080fd5b505af1158015610f8f573d6000803e3d6000fd5b505050506040513d6020811015610fa557600080fd5b505173ffffffffffffffffffffffffffffffffffffffff1614610fc757600080fd5b60008381526001602090815260409091208351610fec92600290920191850190611305565b50604080516020808252845181830152845186937fb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f79387939092839283019185019080838360005b8381101561104c578181015183820152602001611034565b50505050905090810190601f1680156110795780820380516001836020036101000a031916815260200191505b509250505060405180910390a2505050565b60008054604080517f02571be30000000000000000000000000000000000000000000000000000000081526004810186905290518593339373ffffffffffffffffffffffffffffffffffffffff16926302571be39260248083019360209383900390910190829087803b15801561110157600080fd5b505af1158015611115573d6000803e3d6000fd5b505050506040513d602081101561112b57600080fd5b505173ffffffffffffffffffffffffffffffffffffffff161461114d57600080fd5b6000838152600160208181526040928390209091018490558151848152915185927f0424b6fe0d9c3bdbece0e7879dc241bb0c22e900be8b6c168b4ee08bd9bf83bc92908290030190a2505050565b600090815260016020526040902060038101546004909101549091565b60008054604080517f02571be30000000000000000000000000000000000000000000000000000000081526004810186905290518593339373ffffffffffffffffffffffffffffffffffffffff16926302571be39260248083019360209383900390910190829087803b15801561122f57600080fd5b505af1158015611243573d6000803e3d6000fd5b505050506040513d602081101561125957600080fd5b505173ffffffffffffffffffffffffffffffffffffffff161461127b57600080fd5b60008381526001602090815260409182902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff86169081179091558251908152915185927f52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd292908290030190a2505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061134657805160ff1916838001178555611373565b82800160010185558215611373579182015b82811115611373578251825591602001919060010190611358565b5061137f929150611383565b5090565b61139d91905b8082111561137f5760008155600101611389565b905600a165627a7a72305820494d2089cb484863f6172bb6f0ed3b148d6c8eb2a0dd86d330bf08813f99f65d0029a165627a7a72305820df7d6399d923bc8a4fe3d290869ee8614cd2e7d4ac7481c4de85e2df61d183370029") + address := suite.deployContract(ensFactoryCode, 1, gasLimit, gasPrice, priv.ToECDSA()) + + var genState types.GenesisState + suite.Require().NotPanics(func() { + genState = evm.ExportGenesis(suite.ctx, suite.app.EvmKeeper, suite.app.AccountKeeper) + }) + + // sanity check that contract was deployed + deployedEnsFactoryCode := common.FromHex("0x6080604052600436106100405763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663e9358b018114610045575b600080fd5b34801561005157600080fd5b5061007373ffffffffffffffffffffffffffffffffffffffff6004351661009c565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b60008060006100a961057f565b604051809103906000f0801580156100c5573d6000803e3d6000fd5b50604080517f06ab59230000000000000000000000000000000000000000000000000000000081526000600482018190527f4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f06024830152306044830152915192945073ffffffffffffffffffffffffffffffffffffffff8516926306ab59239260648084019391929182900301818387803b15801561016357600080fd5b505af1158015610177573d6000803e3d6000fd5b505050508161018461058f565b73ffffffffffffffffffffffffffffffffffffffff909116815260405190819003602001906000f0801580156101be573d6000803e3d6000fd5b50604080517f06ab59230000000000000000000000000000000000000000000000000000000081527f93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae60048201527f329539a1d23af1810c48a07fe7fc66a3b34fbc8b37e9b3cdb97bb88ceab7e4bf6024820152306044820152905191925073ffffffffffffffffffffffffffffffffffffffff8416916306ab59239160648082019260009290919082900301818387803b15801561027c57600080fd5b505af1158015610290573d6000803e3d6000fd5b5050604080517f1896f70a0000000000000000000000000000000000000000000000000000000081527ffdd5d5de6dd63db72bbc2d487944ba13bf775b50a80805fe6fcaba9b0fba88f5600482015273ffffffffffffffffffffffffffffffffffffffff858116602483015291519186169350631896f70a925060448082019260009290919082900301818387803b15801561032b57600080fd5b505af115801561033f573d6000803e3d6000fd5b5050604080517fd5fa2b000000000000000000000000000000000000000000000000000000000081527ffdd5d5de6dd63db72bbc2d487944ba13bf775b50a80805fe6fcaba9b0fba88f5600482015273ffffffffffffffffffffffffffffffffffffffff851660248201819052915191935063d5fa2b00925060448082019260009290919082900301818387803b1580156103d957600080fd5b505af11580156103ed573d6000803e3d6000fd5b5050604080517f5b0fc9c30000000000000000000000000000000000000000000000000000000081527f93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae600482015273ffffffffffffffffffffffffffffffffffffffff888116602483015291519186169350635b0fc9c3925060448082019260009290919082900301818387803b15801561048857600080fd5b505af115801561049c573d6000803e3d6000fd5b5050604080517f5b0fc9c300000000000000000000000000000000000000000000000000000000815260006004820181905273ffffffffffffffffffffffffffffffffffffffff898116602484015292519287169450635b0fc9c39350604480830193919282900301818387803b15801561051657600080fd5b505af115801561052a573d6000803e3d6000fd5b50506040805173ffffffffffffffffffffffffffffffffffffffff8616815290517fdbfb5ababf63f86424e8df6053dfb90f8b63ea26d7e1e8f68407af4fb2d2c4f29350908190036020019150a15092915050565b60405161064a806105a083390190565b60405161141e80610bea833901905600608060405234801561001057600080fd5b5060008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb58054600160a060020a031916331790556105f1806100596000396000f3006080604052600436106100825763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630178b8bf811461008757806302571be3146100c857806306ab5923146100e057806314ab90381461011657806316a25cbd1461013b5780631896f70a146101705780635b0fc9c3146101a1575b600080fd5b34801561009357600080fd5b5061009f6004356101d2565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b3480156100d457600080fd5b5061009f6004356101fd565b3480156100ec57600080fd5b5061011460043560243573ffffffffffffffffffffffffffffffffffffffff60443516610225565b005b34801561012257600080fd5b5061011460043567ffffffffffffffff60243516610311565b34801561014757600080fd5b506101536004356103e7565b6040805167ffffffffffffffff9092168252519081900360200190f35b34801561017c57600080fd5b5061011460043573ffffffffffffffffffffffffffffffffffffffff6024351661041e565b3480156101ad57600080fd5b5061011460043573ffffffffffffffffffffffffffffffffffffffff602435166104f3565b60009081526020819052604090206001015473ffffffffffffffffffffffffffffffffffffffff1690565b60009081526020819052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b600083815260208190526040812054849073ffffffffffffffffffffffffffffffffffffffff16331461025757600080fd5b6040805186815260208082018790528251918290038301822073ffffffffffffffffffffffffffffffffffffffff871683529251929450869288927fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8292908290030190a350600090815260208190526040902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff929092169190911790555050565b600082815260208190526040902054829073ffffffffffffffffffffffffffffffffffffffff16331461034357600080fd5b6040805167ffffffffffffffff84168152905184917f1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa68919081900360200190a250600091825260208290526040909120600101805467ffffffffffffffff90921674010000000000000000000000000000000000000000027fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b60009081526020819052604090206001015474010000000000000000000000000000000000000000900467ffffffffffffffff1690565b600082815260208190526040902054829073ffffffffffffffffffffffffffffffffffffffff16331461045057600080fd5b6040805173ffffffffffffffffffffffffffffffffffffffff84168152905184917f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0919081900360200190a25060009182526020829052604090912060010180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055565b600082815260208190526040902054829073ffffffffffffffffffffffffffffffffffffffff16331461052557600080fd5b6040805173ffffffffffffffffffffffffffffffffffffffff84168152905184917fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266919081900360200190a25060009182526020829052604090912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9092169190911790555600a165627a7a723058203213a96a5c5e630e44a93f7fa415f3c625e46c7a560debc4dcf02cff9018ee6e0029608060405234801561001057600080fd5b5060405160208061141e833981016040525160008054600160a060020a03909216600160a060020a03199092169190911790556113cc806100526000396000f3006080604052600436106100c45763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166301ffc9a781146100c957806310f13a8c146101175780632203ab56146101b557806329cd62ea1461024f5780632dff69411461026d5780633b3b57de1461029757806359d1d43c146102d8578063623195b0146103ab578063691f34311461040b5780637737221314610423578063c3d014d614610481578063c86902331461049c578063d5fa2b00146104cd575b600080fd5b3480156100d557600080fd5b506101037fffffffff00000000000000000000000000000000000000000000000000000000600435166104fe565b604080519115158252519081900360200190f35b34801561012357600080fd5b5060408051602060046024803582810135601f81018590048502860185019096528585526101b395833595369560449491939091019190819084018382808284375050604080516020601f89358b018035918201839004830284018301909452808352979a9998810197919650918201945092508291508401838280828437509497506107139650505050505050565b005b3480156101c157600080fd5b506101d060043560243561098f565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156102135781810151838201526020016101fb565b50505050905090810190601f1680156102405780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b34801561025b57600080fd5b506101b3600435602435604435610a9b565b34801561027957600080fd5b50610285600435610bcb565b60408051918252519081900360200190f35b3480156102a357600080fd5b506102af600435610be1565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b3480156102e457600080fd5b5060408051602060046024803582810135601f8101859004850286018501909652858552610336958335953695604494919390910191908190840183828082843750949750610c099650505050505050565b6040805160208082528351818301528351919283929083019185019080838360005b83811015610370578181015183820152602001610358565b50505050905090810190601f16801561039d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156103b757600080fd5b50604080516020600460443581810135601f81018490048402850184019095528484526101b3948235946024803595369594606494920191908190840183828082843750949750610d309650505050505050565b34801561041757600080fd5b50610336600435610e61565b34801561042f57600080fd5b5060408051602060046024803582810135601f81018590048502860185019096528585526101b3958335953695604494919390910191908190840183828082843750949750610f059650505050505050565b34801561048d57600080fd5b506101b360043560243561108b565b3480156104a857600080fd5b506104b460043561119c565b6040805192835260208301919091528051918290030190f35b3480156104d957600080fd5b506101b360043573ffffffffffffffffffffffffffffffffffffffff602435166111b9565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f3b3b57de00000000000000000000000000000000000000000000000000000000148061059157507fffffffff0000000000000000000000000000000000000000000000000000000082167fd8389dc500000000000000000000000000000000000000000000000000000000145b806105dd57507fffffffff0000000000000000000000000000000000000000000000000000000082167f691f343100000000000000000000000000000000000000000000000000000000145b8061062957507fffffffff0000000000000000000000000000000000000000000000000000000082167f2203ab5600000000000000000000000000000000000000000000000000000000145b8061067557507fffffffff0000000000000000000000000000000000000000000000000000000082167fc869023300000000000000000000000000000000000000000000000000000000145b806106c157507fffffffff0000000000000000000000000000000000000000000000000000000082167f59d1d43c00000000000000000000000000000000000000000000000000000000145b8061070d57507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b60008054604080517f02571be30000000000000000000000000000000000000000000000000000000081526004810187905290518693339373ffffffffffffffffffffffffffffffffffffffff16926302571be39260248083019360209383900390910190829087803b15801561078957600080fd5b505af115801561079d573d6000803e3d6000fd5b505050506040513d60208110156107b357600080fd5b505173ffffffffffffffffffffffffffffffffffffffff16146107d557600080fd5b6000848152600160209081526040918290209151855185936005019287929182918401908083835b6020831061083a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107fd565b51815160209384036101000a6000190180199092169116179052920194855250604051938490038101909320845161087b9591949190910192509050611305565b50826040518082805190602001908083835b602083106108ca57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161088d565b51815160209384036101000a60001901801990921691161790526040805192909401829003822081835289518383015289519096508a95507fd8c9334b1a9c2f9da342a0a2b32629c1a229b6445dad78947f674b44444a7550948a94508392908301919085019080838360005b8381101561094f578181015183820152602001610937565b50505050905090810190601f16801561097c5780820380516001836020036101000a031916815260200191505b509250505060405180910390a350505050565b60008281526001602081905260409091206060905b838311610a8e57828416158015906109dd5750600083815260068201602052604081205460026000196101006001841615020190911604115b15610a8357600083815260068201602090815260409182902080548351601f600260001961010060018616150201909316929092049182018490048402810184019094528084529091830182828015610a775780601f10610a4c57610100808354040283529160200191610a77565b820191906000526020600020905b815481529060010190602001808311610a5a57829003601f168201915b50505050509150610a93565b6002909202916109a4565b600092505b509250929050565b60008054604080517f02571be30000000000000000000000000000000000000000000000000000000081526004810187905290518693339373ffffffffffffffffffffffffffffffffffffffff16926302571be39260248083019360209383900390910190829087803b158015610b1157600080fd5b505af1158015610b25573d6000803e3d6000fd5b505050506040513d6020811015610b3b57600080fd5b505173ffffffffffffffffffffffffffffffffffffffff1614610b5d57600080fd5b604080518082018252848152602080820185815260008881526001835284902092516003840155516004909201919091558151858152908101849052815186927f1d6f5e03d3f63eb58751986629a5439baee5079ff04f345becb66e23eb154e46928290030190a250505050565b6000908152600160208190526040909120015490565b60009081526001602052604090205473ffffffffffffffffffffffffffffffffffffffff1690565b600082815260016020908152604091829020915183516060936005019285929182918401908083835b60208310610c6f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c32565b518151600019602094850361010090810a820192831692199390931691909117909252949092019687526040805197889003820188208054601f6002600183161590980290950116959095049283018290048202880182019052818752929450925050830182828015610d235780601f10610cf857610100808354040283529160200191610d23565b820191906000526020600020905b815481529060010190602001808311610d0657829003601f168201915b5050505050905092915050565b60008054604080517f02571be30000000000000000000000000000000000000000000000000000000081526004810187905290518693339373ffffffffffffffffffffffffffffffffffffffff16926302571be39260248083019360209383900390910190829087803b158015610da657600080fd5b505af1158015610dba573d6000803e3d6000fd5b505050506040513d6020811015610dd057600080fd5b505173ffffffffffffffffffffffffffffffffffffffff1614610df257600080fd5b6000198301831615610e0357600080fd5b600084815260016020908152604080832086845260060182529091208351610e2d92850190611305565b50604051839085907faa121bbeef5f32f5961a2a28966e769023910fc9479059ee3495d4c1a696efe390600090a350505050565b6000818152600160208181526040928390206002908101805485516000199582161561010002959095011691909104601f81018390048302840183019094528383526060939091830182828015610ef95780601f10610ece57610100808354040283529160200191610ef9565b820191906000526020600020905b815481529060010190602001808311610edc57829003601f168201915b50505050509050919050565b60008054604080517f02571be30000000000000000000000000000000000000000000000000000000081526004810186905290518593339373ffffffffffffffffffffffffffffffffffffffff16926302571be39260248083019360209383900390910190829087803b158015610f7b57600080fd5b505af1158015610f8f573d6000803e3d6000fd5b505050506040513d6020811015610fa557600080fd5b505173ffffffffffffffffffffffffffffffffffffffff1614610fc757600080fd5b60008381526001602090815260409091208351610fec92600290920191850190611305565b50604080516020808252845181830152845186937fb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f79387939092839283019185019080838360005b8381101561104c578181015183820152602001611034565b50505050905090810190601f1680156110795780820380516001836020036101000a031916815260200191505b509250505060405180910390a2505050565b60008054604080517f02571be30000000000000000000000000000000000000000000000000000000081526004810186905290518593339373ffffffffffffffffffffffffffffffffffffffff16926302571be39260248083019360209383900390910190829087803b15801561110157600080fd5b505af1158015611115573d6000803e3d6000fd5b505050506040513d602081101561112b57600080fd5b505173ffffffffffffffffffffffffffffffffffffffff161461114d57600080fd5b6000838152600160208181526040928390209091018490558151848152915185927f0424b6fe0d9c3bdbece0e7879dc241bb0c22e900be8b6c168b4ee08bd9bf83bc92908290030190a2505050565b600090815260016020526040902060038101546004909101549091565b60008054604080517f02571be30000000000000000000000000000000000000000000000000000000081526004810186905290518593339373ffffffffffffffffffffffffffffffffffffffff16926302571be39260248083019360209383900390910190829087803b15801561122f57600080fd5b505af1158015611243573d6000803e3d6000fd5b505050506040513d602081101561125957600080fd5b505173ffffffffffffffffffffffffffffffffffffffff161461127b57600080fd5b60008381526001602090815260409182902080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff86169081179091558251908152915185927f52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd292908290030190a2505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061134657805160ff1916838001178555611373565b82800160010185558215611373579182015b82811115611373578251825591602001919060010190611358565b5061137f929150611383565b5090565b61139d91905b8082111561137f5760008155600101611389565b905600a165627a7a72305820494d2089cb484863f6172bb6f0ed3b148d6c8eb2a0dd86d330bf08813f99f65d0029a165627a7a72305820df7d6399d923bc8a4fe3d290869ee8614cd2e7d4ac7481c4de85e2df61d183370029") + code := suite.app.EvmKeeper.GetCode(suite.ctx, address) + suite.Require().Equal(deployedEnsFactoryCode, code) + + suite.T().Logf("account address 0x%s", priv.PubKey().Address()) + suite.T().Logf("contract addr 0x%x", address) + + // clear keeper code and re-initialize + suite.app.EvmKeeper.SetCode(suite.ctx, address, nil) + _ = evm.InitGenesis(suite.ctx, suite.app.EvmKeeper, genState) + + resCode := suite.app.EvmKeeper.GetCode(suite.ctx, address) + suite.Require().Equal(deployedEnsFactoryCode, resCode) +} + +func (suite *EvmTestSuite) deployContract(code []byte, nonce, gasLimit uint64, gasPrice *big.Int, priv *ecdsa.PrivateKey) common.Address { + tx := types.NewMsgEthereumTx(nonce, nil, big.NewInt(0), gasLimit, gasPrice, code) + err := tx.Sign(big.NewInt(3), priv) + suite.Require().NoError(err) + + result, err := suite.handler(suite.ctx, tx) + suite.Require().NoError(err, "failed to handle eth tx msg") + resData, err := types.DecodeResultData(result.Data) + suite.Require().NoError(err) + return resData.ContractAddress +} diff --git a/x/evm/handler.go b/x/evm/handler.go index e998e5586b..f1615f6799 100644 --- a/x/evm/handler.go +++ b/x/evm/handler.go @@ -1 +1,204 @@ package evm + +import ( + "github.com/ethereum/go-ethereum/common" + + ethermint "github.com/cosmos/ethermint/types" + "github.com/cosmos/ethermint/x/evm/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + tmtypes "github.com/tendermint/tendermint/types" +) + +// NewHandler returns a handler for Ethermint type messages. +func NewHandler(k Keeper) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + ctx = ctx.WithEventManager(sdk.NewEventManager()) + switch msg := msg.(type) { + case types.MsgEthereumTx: + return handleMsgEthereumTx(ctx, k, msg) + case types.MsgEthermint: + return handleMsgEthermint(ctx, k, msg) + default: + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg) + } + } +} + +// handleMsgEthereumTx handles an Ethereum specific tx +func handleMsgEthereumTx(ctx sdk.Context, k Keeper, msg types.MsgEthereumTx) (*sdk.Result, error) { + // parse the chainID from a string to a base-10 integer + chainIDEpoch, err := ethermint.ParseChainID(ctx.ChainID()) + if err != nil { + return nil, err + } + + // Verify signature and retrieve sender address + sender, err := msg.VerifySig(chainIDEpoch) + if err != nil { + return nil, err + } + + txHash := tmtypes.Tx(ctx.TxBytes()).Hash() + ethHash := common.BytesToHash(txHash) + + st := types.StateTransition{ + AccountNonce: msg.Data.AccountNonce, + Price: msg.Data.Price, + GasLimit: msg.Data.GasLimit, + Recipient: msg.Data.Recipient, + Amount: msg.Data.Amount, + Payload: msg.Data.Payload, + Csdb: k.CommitStateDB.WithContext(ctx), + ChainID: chainIDEpoch, + TxHash: ðHash, + Sender: sender, + Simulate: ctx.IsCheckTx(), + } + + // since the txCount is used by the stateDB, and a simulated tx is run only on the node it's submitted to, + // then this will cause the txCount/stateDB of the node that ran the simulated tx to be different than the + // other nodes, causing a consensus error + if !st.Simulate { + // Prepare db for logs + // TODO: block hash + k.CommitStateDB.Prepare(ethHash, common.Hash{}, k.TxCount) + k.TxCount++ + } + + config, found := k.GetChainConfig(ctx) + if !found { + return nil, types.ErrChainConfigNotFound + } + + executionResult, err := st.TransitionDb(ctx, config) + if err != nil { + return nil, err + } + + if !st.Simulate { + // update block bloom filter + k.Bloom.Or(k.Bloom, executionResult.Bloom) + + // update transaction logs in KVStore + err = k.SetLogs(ctx, common.BytesToHash(txHash), executionResult.Logs) + if err != nil { + panic(err) + } + } + + // log successful execution + k.Logger(ctx).Info(executionResult.Result.Log) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeEthereumTx, + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Data.Amount.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, sender.String()), + ), + }) + + if msg.Data.Recipient != nil { + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeEthereumTx, + sdk.NewAttribute(types.AttributeKeyRecipient, msg.Data.Recipient.String()), + ), + ) + } + + // set the events to the result + executionResult.Result.Events = ctx.EventManager().Events() + return executionResult.Result, nil +} + +// handleMsgEthermint handles an sdk.StdTx for an Ethereum state transition +func handleMsgEthermint(ctx sdk.Context, k Keeper, msg types.MsgEthermint) (*sdk.Result, error) { + // parse the chainID from a string to a base-10 integer + chainIDEpoch, err := ethermint.ParseChainID(ctx.ChainID()) + if err != nil { + return nil, err + } + + txHash := tmtypes.Tx(ctx.TxBytes()).Hash() + ethHash := common.BytesToHash(txHash) + + st := types.StateTransition{ + AccountNonce: msg.AccountNonce, + Price: msg.Price.BigInt(), + GasLimit: msg.GasLimit, + Amount: msg.Amount.BigInt(), + Payload: msg.Payload, + Csdb: k.CommitStateDB.WithContext(ctx), + ChainID: chainIDEpoch, + TxHash: ðHash, + Sender: common.BytesToAddress(msg.From.Bytes()), + Simulate: ctx.IsCheckTx(), + } + + if msg.Recipient != nil { + to := common.BytesToAddress(msg.Recipient.Bytes()) + st.Recipient = &to + } + + if !st.Simulate { + // Prepare db for logs + k.CommitStateDB.Prepare(ethHash, common.Hash{}, k.TxCount) + k.TxCount++ + } + + config, found := k.GetChainConfig(ctx) + if !found { + return nil, types.ErrChainConfigNotFound + } + + executionResult, err := st.TransitionDb(ctx, config) + if err != nil { + return nil, err + } + + // update block bloom filter + if !st.Simulate { + k.Bloom.Or(k.Bloom, executionResult.Bloom) + + // update transaction logs in KVStore + err = k.SetLogs(ctx, common.BytesToHash(txHash), executionResult.Logs) + if err != nil { + panic(err) + } + } + + // log successful execution + k.Logger(ctx).Info(executionResult.Result.Log) + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeEthermint, + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.From.String()), + ), + }) + + if msg.Recipient != nil { + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeEthermint, + sdk.NewAttribute(types.AttributeKeyRecipient, msg.Recipient.String()), + ), + ) + } + + // set the events to the result + executionResult.Result.Events = ctx.EventManager().Events() + return executionResult.Result, nil +} diff --git a/x/evm/handler_test.go b/x/evm/handler_test.go new file mode 100644 index 0000000000..70244f25c8 --- /dev/null +++ b/x/evm/handler_test.go @@ -0,0 +1,417 @@ +package evm_test + +import ( + "crypto/ecdsa" + "fmt" + "math/big" + "strings" + "testing" + "time" + + "github.com/status-im/keycard-go/hexutils" + + "github.com/stretchr/testify/suite" + + "github.com/ethereum/go-ethereum/common" + ethcmn "github.com/ethereum/go-ethereum/common" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ethermint/app" + "github.com/cosmos/ethermint/crypto" + ethermint "github.com/cosmos/ethermint/types" + "github.com/cosmos/ethermint/x/evm" + "github.com/cosmos/ethermint/x/evm/keeper" + "github.com/cosmos/ethermint/x/evm/types" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/secp256k1" +) + +type EvmTestSuite struct { + suite.Suite + + ctx sdk.Context + handler sdk.Handler + querier sdk.Querier + app *app.EthermintApp + codec *codec.Codec +} + +func (suite *EvmTestSuite) SetupTest() { + checkTx := false + + suite.app = app.Setup(checkTx) + suite.ctx = suite.app.BaseApp.NewContext(checkTx, abci.Header{Height: 1, ChainID: "ethermint-3", Time: time.Now().UTC()}) + suite.handler = evm.NewHandler(suite.app.EvmKeeper) + suite.querier = keeper.NewQuerier(suite.app.EvmKeeper) + suite.codec = codec.New() +} + +func TestEvmTestSuite(t *testing.T) { + suite.Run(t, new(EvmTestSuite)) +} + +func (suite *EvmTestSuite) TestHandleMsgEthereumTx() { + privkey, err := crypto.GenerateKey() + suite.Require().NoError(err) + sender := ethcmn.HexToAddress(privkey.PubKey().Address().String()) + + var tx types.MsgEthereumTx + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "passed", + func() { + suite.app.EvmKeeper.SetBalance(suite.ctx, sender, big.NewInt(100)) + tx = types.NewMsgEthereumTx(0, &sender, big.NewInt(100), 0, big.NewInt(10000), nil) + + // parse context chain ID to big.Int + chainID, err := ethermint.ParseChainID(suite.ctx.ChainID()) + suite.Require().NoError(err) + + // sign transaction + err = tx.Sign(chainID, privkey.ToECDSA()) + suite.Require().NoError(err) + }, + true, + }, + { + "insufficient balance", + func() { + tx = types.NewMsgEthereumTxContract(0, big.NewInt(100), 0, big.NewInt(10000), nil) + + // parse context chain ID to big.Int + chainID, err := ethermint.ParseChainID(suite.ctx.ChainID()) + suite.Require().NoError(err) + + // sign transaction + err = tx.Sign(chainID, privkey.ToECDSA()) + suite.Require().NoError(err) + }, + false, + }, + { + "tx encoding failed", + func() { + tx = types.NewMsgEthereumTxContract(0, big.NewInt(100), 0, big.NewInt(10000), nil) + }, + false, + }, + { + "invalid chain ID", + func() { + suite.ctx = suite.ctx.WithChainID("chainID") + }, + false, + }, + { + "VerifySig failed", + func() { + tx = types.NewMsgEthereumTxContract(0, big.NewInt(100), 0, big.NewInt(10000), nil) + }, + false, + }, + } + + for _, tc := range testCases { + suite.Run(tc.msg, func() { + suite.SetupTest() // reset + //nolint + tc.malleate() + + res, err := suite.handler(suite.ctx, tx) + + //nolint + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + } else { + suite.Require().Error(err) + suite.Require().Nil(res) + } + }) + } +} + +func (suite *EvmTestSuite) TestMsgEthermint() { + var ( + tx types.MsgEthermint + from = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + to = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "passed", + func() { + tx = types.NewMsgEthermint(0, &to, sdk.NewInt(1), 100000, sdk.NewInt(2), []byte("test"), from) + suite.app.EvmKeeper.SetBalance(suite.ctx, ethcmn.BytesToAddress(from.Bytes()), big.NewInt(100)) + }, + true, + }, + { + "invalid state transition", + func() { + tx = types.NewMsgEthermint(0, &to, sdk.NewInt(1), 100000, sdk.NewInt(2), []byte("test"), from) + }, + false, + }, + { + "invalid chain ID", + func() { + suite.ctx = suite.ctx.WithChainID("chainID") + }, + false, + }, + } + + for _, tc := range testCases { + suite.Run("", func() { + suite.SetupTest() // reset + //nolint + tc.malleate() + + res, err := suite.handler(suite.ctx, tx) + + //nolint + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + } else { + suite.Require().Error(err) + suite.Require().Nil(res) + } + }) + } +} + +func (suite *EvmTestSuite) TestHandlerLogs() { + // Test contract: + + // pragma solidity ^0.5.1; + + // contract Test { + // event Hello(uint256 indexed world); + + // constructor() public { + // emit Hello(17); + // } + // } + + // { + // "linkReferences": {}, + // "object": "6080604052348015600f57600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a2603580604b6000396000f3fe6080604052600080fdfea165627a7a723058206cab665f0f557620554bb45adf266708d2bd349b8a4314bdff205ee8440e3c240029", + // "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH1 0xF JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x11 PUSH32 0x775A94827B8FD9B519D36CD827093C664F93347070A554F65E4A6F56CD738898 PUSH1 0x40 MLOAD PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG2 PUSH1 0x35 DUP1 PUSH1 0x4B PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN INVALID PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT INVALID LOG1 PUSH6 0x627A7A723058 KECCAK256 PUSH13 0xAB665F0F557620554BB45ADF26 PUSH8 0x8D2BD349B8A4314 0xbd SELFDESTRUCT KECCAK256 0x5e 0xe8 DIFFICULTY 0xe EXTCODECOPY 0x24 STOP 0x29 ", + // "sourceMap": "25:119:0:-;;;90:52;8:9:-1;5:2;;;30:1;27;20:12;5:2;90:52:0;132:2;126:9;;;;;;;;;;25:119;;;;;;" + // } + + gasLimit := uint64(100000) + gasPrice := big.NewInt(1000000) + + priv, err := crypto.GenerateKey() + suite.Require().NoError(err, "failed to create key") + + bytecode := common.FromHex("0x6080604052348015600f57600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a2603580604b6000396000f3fe6080604052600080fdfea165627a7a723058206cab665f0f557620554bb45adf266708d2bd349b8a4314bdff205ee8440e3c240029") + tx := types.NewMsgEthereumTx(1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode) + err = tx.Sign(big.NewInt(3), priv.ToECDSA()) + suite.Require().NoError(err) + + result, err := suite.handler(suite.ctx, tx) + suite.Require().NoError(err, "failed to handle eth tx msg") + + resultData, err := types.DecodeResultData(result.Data) + suite.Require().NoError(err, "failed to decode result data") + + suite.Require().Equal(len(resultData.Logs), 1) + suite.Require().Equal(len(resultData.Logs[0].Topics), 2) + + hash := []byte{1} + err = suite.app.EvmKeeper.SetLogs(suite.ctx, ethcmn.BytesToHash(hash), resultData.Logs) + suite.Require().NoError(err) + + logs, err := suite.app.EvmKeeper.GetLogs(suite.ctx, ethcmn.BytesToHash(hash)) + suite.Require().NoError(err, "failed to get logs") + + suite.Require().Equal(logs, resultData.Logs) +} + +func (suite *EvmTestSuite) TestQueryTxLogs() { + gasLimit := uint64(100000) + gasPrice := big.NewInt(1000000) + + priv, err := crypto.GenerateKey() + suite.Require().NoError(err, "failed to create key") + + // send contract deployment transaction with an event in the constructor + bytecode := common.FromHex("0x6080604052348015600f57600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a2603580604b6000396000f3fe6080604052600080fdfea165627a7a723058206cab665f0f557620554bb45adf266708d2bd349b8a4314bdff205ee8440e3c240029") + tx := types.NewMsgEthereumTx(1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode) + err = tx.Sign(big.NewInt(3), priv.ToECDSA()) + suite.Require().NoError(err) + + result, err := suite.handler(suite.ctx, tx) + suite.Require().NoError(err) + suite.Require().NotNil(result) + + resultData, err := types.DecodeResultData(result.Data) + suite.Require().NoError(err, "failed to decode result data") + + suite.Require().Equal(len(resultData.Logs), 1) + suite.Require().Equal(len(resultData.Logs[0].Topics), 2) + + // get logs by tx hash + hash := resultData.TxHash.Bytes() + + logs, err := suite.app.EvmKeeper.GetLogs(suite.ctx, ethcmn.BytesToHash(hash)) + suite.Require().NoError(err, "failed to get logs") + + suite.Require().Equal(logs, resultData.Logs) + + // query tx logs + path := []string{"transactionLogs", fmt.Sprintf("0x%x", hash)} + res, err := suite.querier(suite.ctx, path, abci.RequestQuery{}) + suite.Require().NoError(err, "failed to query txLogs") + + var txLogs types.QueryETHLogs + suite.codec.MustUnmarshalJSON(res, &txLogs) + + // amino decodes an empty byte array as nil, whereas JSON decodes it as []byte{} causing a discrepancy + resultData.Logs[0].Data = []byte{} + suite.Require().Equal(txLogs.Logs[0], resultData.Logs[0]) +} + +func (suite *EvmTestSuite) TestDeployAndCallContract() { + // Test contract: + //http://remix.ethereum.org/#optimize=false&evmVersion=istanbul&version=soljson-v0.5.15+commit.6a57276f.js + //2_Owner.sol + // + //pragma solidity >=0.4.22 <0.7.0; + // + ///** + // * @title Owner + // * @dev Set & change owner + // */ + //contract Owner { + // + // address private owner; + // + // // event for EVM logging + // event OwnerSet(address indexed oldOwner, address indexed newOwner); + // + // // modifier to check if caller is owner + // modifier isOwner() { + // // If the first argument of 'require' evaluates to 'false', execution terminates and all + // // changes to the state and to Ether balances are reverted. + // // This used to consume all gas in old EVM versions, but not anymore. + // // It is often a good idea to use 'require' to check if functions are called correctly. + // // As a second argument, you can also provide an explanation about what went wrong. + // require(msg.sender == owner, "Caller is not owner"); + // _; + //} + // + // /** + // * @dev Set contract deployer as owner + // */ + // constructor() public { + // owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor + // emit OwnerSet(address(0), owner); + //} + // + // /** + // * @dev Change owner + // * @param newOwner address of new owner + // */ + // function changeOwner(address newOwner) public isOwner { + // emit OwnerSet(owner, newOwner); + // owner = newOwner; + //} + // + // /** + // * @dev Return owner address + // * @return address of owner + // */ + // function getOwner() external view returns (address) { + // return owner; + //} + //} + + // Deploy contract - Owner.sol + gasLimit := uint64(100000000) + gasPrice := big.NewInt(10000) + + priv, err := crypto.GenerateKey() + suite.Require().NoError(err, "failed to create key") + + bytecode := common.FromHex("0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a36102c4806100dc6000396000f3fe608060405234801561001057600080fd5b5060043610610053576000357c010000000000000000000000000000000000000000000000000000000090048063893d20e814610058578063a6f9dae1146100a2575b600080fd5b6100606100e6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100e4600480360360208110156100b857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061010f565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146101d1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f43616c6c6572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72315820f397f2733a89198bc7fed0764083694c5b828791f39ebcbc9e414bccef14b48064736f6c63430005100032") + tx := types.NewMsgEthereumTx(1, nil, big.NewInt(0), gasLimit, gasPrice, bytecode) + tx.Sign(big.NewInt(3), priv.ToECDSA()) + suite.Require().NoError(err) + + result, err := suite.handler(suite.ctx, tx) + suite.Require().NoError(err, "failed to handle eth tx msg") + + resultData, err := types.DecodeResultData(result.Data) + suite.Require().NoError(err, "failed to decode result data") + + // store - changeOwner + gasLimit = uint64(100000000000) + gasPrice = big.NewInt(100) + receiver := common.HexToAddress(resultData.ContractAddress.String()) + + storeAddr := "0xa6f9dae10000000000000000000000006a82e4a67715c8412a9114fbd2cbaefbc8181424" + bytecode = common.FromHex(storeAddr) + tx = types.NewMsgEthereumTx(2, &receiver, big.NewInt(0), gasLimit, gasPrice, bytecode) + tx.Sign(big.NewInt(3), priv.ToECDSA()) + suite.Require().NoError(err) + + result, err = suite.handler(suite.ctx, tx) + suite.Require().NoError(err, "failed to handle eth tx msg") + + resultData, err = types.DecodeResultData(result.Data) + suite.Require().NoError(err, "failed to decode result data") + + // query - getOwner + bytecode = common.FromHex("0x893d20e8") + tx = types.NewMsgEthereumTx(2, &receiver, big.NewInt(0), gasLimit, gasPrice, bytecode) + tx.Sign(big.NewInt(3), priv.ToECDSA()) + suite.Require().NoError(err) + + result, err = suite.handler(suite.ctx, tx) + suite.Require().NoError(err, "failed to handle eth tx msg") + + resultData, err = types.DecodeResultData(result.Data) + suite.Require().NoError(err, "failed to decode result data") + + getAddr := strings.ToLower(hexutils.BytesToHex(resultData.Ret)) + suite.Require().Equal(true, strings.HasSuffix(storeAddr, getAddr), "Fail to query the address") +} + +func (suite *EvmTestSuite) TestSendTransaction() { + gasLimit := uint64(21000) + gasPrice := big.NewInt(0x55ae82600) + + priv, err := crypto.GenerateKey() + suite.Require().NoError(err, "failed to create key") + pub := priv.ToECDSA().Public().(*ecdsa.PublicKey) + + suite.app.EvmKeeper.SetBalance(suite.ctx, ethcrypto.PubkeyToAddress(*pub), big.NewInt(100)) + + // send simple value transfer with gasLimit=21000 + tx := types.NewMsgEthereumTx(1, ðcmn.Address{0x1}, big.NewInt(1), gasLimit, gasPrice, nil) + err = tx.Sign(big.NewInt(3), priv.ToECDSA()) + suite.Require().NoError(err) + + result, err := suite.handler(suite.ctx, tx) + suite.Require().NoError(err) + suite.Require().NotNil(result) +} diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go new file mode 100644 index 0000000000..18bfd9454a --- /dev/null +++ b/x/evm/keeper/keeper.go @@ -0,0 +1,167 @@ +package keeper + +import ( + "encoding/binary" + "fmt" + "math/big" + + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params" + + "github.com/cosmos/ethermint/x/evm/types" + + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" +) + +// Keeper wraps the CommitStateDB, allowing us to pass in SDK context while adhering +// to the StateDB interface. +type Keeper struct { + // Amino codec + cdc *codec.Codec + // Store key required for the EVM Prefix KVStore. It is required by: + // - storing Account's Storage State + // - storing Account's Code + // - storing transaction Logs + // - storing block height -> bloom filter map. Needed for the Web3 API. + // - storing block hash -> block height map. Needed for the Web3 API. + storeKey sdk.StoreKey + // Ethermint concrete implementation on the EVM StateDB interface + CommitStateDB *types.CommitStateDB + // Transaction counter in a block. Used on StateSB's Prepare function. + // It is reset to 0 every block on BeginBlock so there's no point in storing the counter + // on the KVStore or adding it as a field on the EVM genesis state. + TxCount int + Bloom *big.Int +} + +// NewKeeper generates new evm module keeper +func NewKeeper( + cdc *codec.Codec, storeKey sdk.StoreKey, paramSpace params.Subspace, ak types.AccountKeeper, +) Keeper { + // set KeyTable if it has not already been set + if !paramSpace.HasKeyTable() { + paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable()) + } + + // NOTE: we pass in the parameter space to the CommitStateDB in order to use custom denominations for the EVM operations + return Keeper{ + cdc: cdc, + storeKey: storeKey, + CommitStateDB: types.NewCommitStateDB(sdk.Context{}, storeKey, paramSpace, ak), + TxCount: 0, + Bloom: big.NewInt(0), + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +// ---------------------------------------------------------------------------- +// Block hash mapping functions +// Required by Web3 API. +// TODO: remove once tendermint support block queries by hash. +// ---------------------------------------------------------------------------- + +// GetBlockHash gets block height from block consensus hash +func (k Keeper) GetBlockHash(ctx sdk.Context, hash []byte) (int64, bool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixBlockHash) + bz := store.Get(hash) + if len(bz) == 0 { + return 0, false + } + + height := binary.BigEndian.Uint64(bz) + return int64(height), true +} + +// SetBlockHash sets the mapping from block consensus hash to block height +func (k Keeper) SetBlockHash(ctx sdk.Context, hash []byte, height int64) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixBlockHash) + bz := sdk.Uint64ToBigEndian(uint64(height)) + store.Set(hash, bz) +} + +// ---------------------------------------------------------------------------- +// Block bloom bits mapping functions +// Required by Web3 API. +// ---------------------------------------------------------------------------- + +// GetBlockBloom gets bloombits from block height +func (k Keeper) GetBlockBloom(ctx sdk.Context, height int64) (ethtypes.Bloom, bool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixBloom) + has := store.Has(types.BloomKey(height)) + if !has { + return ethtypes.Bloom{}, true // TODO: sometimes bloom cannot be found, fix this + } + + bz := store.Get(types.BloomKey(height)) + return ethtypes.BytesToBloom(bz), true +} + +// SetBlockBloom sets the mapping from block height to bloom bits +func (k Keeper) SetBlockBloom(ctx sdk.Context, height int64, bloom ethtypes.Bloom) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixBloom) + store.Set(types.BloomKey(height), bloom.Bytes()) +} + +// GetAllTxLogs return all the transaction logs from the store. +func (k Keeper) GetAllTxLogs(ctx sdk.Context) []types.TransactionLogs { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, types.KeyPrefixLogs) + defer iterator.Close() + + txsLogs := []types.TransactionLogs{} + for ; iterator.Valid(); iterator.Next() { + hash := common.BytesToHash(iterator.Key()) + var logs []*ethtypes.Log + k.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &logs) + + // add a new entry + txLog := types.NewTransactionLogs(hash, logs) + txsLogs = append(txsLogs, txLog) + } + return txsLogs +} + +// GetAccountStorage return state storage associated with an account +func (k Keeper) GetAccountStorage(ctx sdk.Context, address common.Address) (types.Storage, error) { + storage := types.Storage{} + err := k.ForEachStorage(ctx, address, func(key, value common.Hash) bool { + storage = append(storage, types.NewState(key, value)) + return false + }) + if err != nil { + return types.Storage{}, err + } + + return storage, nil +} + +// GetChainConfig gets block height from block consensus hash +func (k Keeper) GetChainConfig(ctx sdk.Context) (types.ChainConfig, bool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixChainConfig) + // get from an empty key that's already prefixed by KeyPrefixChainConfig + bz := store.Get([]byte{}) + if len(bz) == 0 { + return types.ChainConfig{}, false + } + + var config types.ChainConfig + k.cdc.MustUnmarshalBinaryBare(bz, &config) + return config, true +} + +// SetChainConfig sets the mapping from block consensus hash to block height +func (k Keeper) SetChainConfig(ctx sdk.Context, config types.ChainConfig) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixChainConfig) + bz := k.cdc.MustMarshalBinaryBare(config) + // get to an empty key that's already prefixed by KeyPrefixChainConfig + store.Set([]byte{}, bz) +} diff --git a/x/evm/keeper/keeper_test.go b/x/evm/keeper/keeper_test.go new file mode 100644 index 0000000000..09943fe96d --- /dev/null +++ b/x/evm/keeper/keeper_test.go @@ -0,0 +1,163 @@ +package keeper_test + +import ( + "math/big" + "testing" + "time" + + "github.com/stretchr/testify/suite" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + + "github.com/cosmos/ethermint/app" + ethermint "github.com/cosmos/ethermint/types" + "github.com/cosmos/ethermint/x/evm/keeper" + "github.com/cosmos/ethermint/x/evm/types" + + ethcmn "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + + abci "github.com/tendermint/tendermint/abci/types" +) + +const addrHex = "0x756F45E3FA69347A9A973A725E3C98bC4db0b4c1" +const hex = "0x0d87a3a5f73140f46aac1bf419263e4e94e87c292f25007700ab7f2060e2af68" + +var ( + hash = ethcmn.FromHex(hex) +) + +type KeeperTestSuite struct { + suite.Suite + + ctx sdk.Context + querier sdk.Querier + app *app.EthermintApp + address ethcmn.Address +} + +func (suite *KeeperTestSuite) SetupTest() { + checkTx := false + + suite.app = app.Setup(checkTx) + suite.ctx = suite.app.BaseApp.NewContext(checkTx, abci.Header{Height: 1, ChainID: "3", Time: time.Now().UTC()}) + suite.querier = keeper.NewQuerier(suite.app.EvmKeeper) + suite.address = ethcmn.HexToAddress(addrHex) + + balance := sdk.NewCoins(ethermint.NewPhotonCoin(sdk.ZeroInt())) + acc := ðermint.EthAccount{ + BaseAccount: auth.NewBaseAccount(sdk.AccAddress(suite.address.Bytes()), balance, nil, 0, 0), + CodeHash: ethcrypto.Keccak256(nil), + } + + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} + +func (suite *KeeperTestSuite) TestTransactionLogs() { + ethHash := ethcmn.BytesToHash(hash) + log := ðtypes.Log{ + Address: suite.address, + Data: []byte("log"), + BlockNumber: 10, + } + log2 := ðtypes.Log{ + Address: suite.address, + Data: []byte("log2"), + BlockNumber: 11, + } + expLogs := []*ethtypes.Log{log} + + err := suite.app.EvmKeeper.SetLogs(suite.ctx, ethHash, expLogs) + suite.Require().NoError(err) + + logs, err := suite.app.EvmKeeper.GetLogs(suite.ctx, ethHash) + suite.Require().NoError(err) + suite.Require().Equal(expLogs, logs) + + expLogs = []*ethtypes.Log{log2, log} + + // add another log under the zero hash + suite.app.EvmKeeper.AddLog(suite.ctx, log2) + logs = suite.app.EvmKeeper.AllLogs(suite.ctx) + suite.Require().Equal(expLogs, logs) + + // add another log under the zero hash + log3 := ðtypes.Log{ + Address: suite.address, + Data: []byte("log3"), + BlockNumber: 10, + } + suite.app.EvmKeeper.AddLog(suite.ctx, log3) + + txLogs := suite.app.EvmKeeper.GetAllTxLogs(suite.ctx) + suite.Require().Equal(2, len(txLogs)) + + suite.Require().Equal(ethcmn.Hash{}.String(), txLogs[0].Hash.String()) + suite.Require().Equal([]*ethtypes.Log{log2, log3}, txLogs[0].Logs) + + suite.Require().Equal(ethHash.String(), txLogs[1].Hash.String()) + suite.Require().Equal([]*ethtypes.Log{log}, txLogs[1].Logs) +} + +func (suite *KeeperTestSuite) TestDBStorage() { + // Perform state transitions + suite.app.EvmKeeper.CreateAccount(suite.ctx, suite.address) + suite.app.EvmKeeper.SetBalance(suite.ctx, suite.address, big.NewInt(5)) + suite.app.EvmKeeper.SetNonce(suite.ctx, suite.address, 4) + suite.app.EvmKeeper.SetState(suite.ctx, suite.address, ethcmn.HexToHash("0x2"), ethcmn.HexToHash("0x3")) + suite.app.EvmKeeper.SetCode(suite.ctx, suite.address, []byte{0x1}) + + // Test block hash mapping functionality + suite.app.EvmKeeper.SetBlockHash(suite.ctx, hash, 7) + height, found := suite.app.EvmKeeper.GetBlockHash(suite.ctx, hash) + suite.Require().True(found) + suite.Require().Equal(int64(7), height) + + suite.app.EvmKeeper.SetBlockHash(suite.ctx, []byte{0x43, 0x32}, 8) + + // Test block height mapping functionality + testBloom := ethtypes.BytesToBloom([]byte{0x1, 0x3}) + suite.app.EvmKeeper.SetBlockBloom(suite.ctx, 4, testBloom) + + // Get those state transitions + suite.Require().Equal(suite.app.EvmKeeper.GetBalance(suite.ctx, suite.address).Cmp(big.NewInt(5)), 0) + suite.Require().Equal(suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address), uint64(4)) + suite.Require().Equal(suite.app.EvmKeeper.GetState(suite.ctx, suite.address, ethcmn.HexToHash("0x2")), ethcmn.HexToHash("0x3")) + suite.Require().Equal(suite.app.EvmKeeper.GetCode(suite.ctx, suite.address), []byte{0x1}) + + height, found = suite.app.EvmKeeper.GetBlockHash(suite.ctx, hash) + suite.Require().True(found) + suite.Require().Equal(height, int64(7)) + height, found = suite.app.EvmKeeper.GetBlockHash(suite.ctx, []byte{0x43, 0x32}) + suite.Require().True(found) + suite.Require().Equal(height, int64(8)) + + bloom, found := suite.app.EvmKeeper.GetBlockBloom(suite.ctx, 4) + suite.Require().True(found) + suite.Require().Equal(bloom, testBloom) + + // commit stateDB + _, err := suite.app.EvmKeeper.Commit(suite.ctx, false) + suite.Require().NoError(err, "failed to commit StateDB") + + // simulate BaseApp EndBlocker commitment + suite.app.Commit() +} + +func (suite *KeeperTestSuite) TestChainConfig() { + config, found := suite.app.EvmKeeper.GetChainConfig(suite.ctx) + suite.Require().True(found) + suite.Require().Equal(types.DefaultChainConfig(), config) + + config.EIP150Block = sdk.NewInt(100) + suite.app.EvmKeeper.SetChainConfig(suite.ctx, config) + newConfig, found := suite.app.EvmKeeper.GetChainConfig(suite.ctx) + suite.Require().True(found) + suite.Require().Equal(config, newConfig) +} diff --git a/x/evm/keeper/params.go b/x/evm/keeper/params.go new file mode 100644 index 0000000000..c24ec8e4a4 --- /dev/null +++ b/x/evm/keeper/params.go @@ -0,0 +1,17 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ethermint/x/evm/types" +) + +// GetParams returns the total set of evm parameters. +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { + return k.CommitStateDB.WithContext(ctx).GetParams() +} + +// SetParams sets the evm parameters to the param space. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { + k.CommitStateDB.WithContext(ctx).SetParams(params) +} diff --git a/x/evm/keeper/params_test.go b/x/evm/keeper/params_test.go new file mode 100644 index 0000000000..4eda8b7374 --- /dev/null +++ b/x/evm/keeper/params_test.go @@ -0,0 +1,14 @@ +package keeper_test + +import ( + "github.com/cosmos/ethermint/x/evm/types" +) + +func (suite *KeeperTestSuite) TestParams() { + params := suite.app.EvmKeeper.GetParams(suite.ctx) + suite.Require().Equal(types.DefaultParams(), params) + params.EvmDenom = "ara" + suite.app.EvmKeeper.SetParams(suite.ctx, params) + newParams := suite.app.EvmKeeper.GetParams(suite.ctx) + suite.Require().Equal(newParams, params) +} diff --git a/x/evm/keeper/querier.go b/x/evm/keeper/querier.go new file mode 100644 index 0000000000..9b062849df --- /dev/null +++ b/x/evm/keeper/querier.go @@ -0,0 +1,228 @@ +package keeper + +import ( + "encoding/json" + "fmt" + "strconv" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/cosmos/ethermint/utils" + "github.com/cosmos/ethermint/version" + "github.com/cosmos/ethermint/x/evm/types" + + ethcmn "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + + abci "github.com/tendermint/tendermint/abci/types" +) + +// NewQuerier is the module level router for state queries +func NewQuerier(keeper Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, _ abci.RequestQuery) ([]byte, error) { + switch path[0] { + case types.QueryProtocolVersion: + return queryProtocolVersion(keeper) + case types.QueryBalance: + return queryBalance(ctx, path, keeper) + case types.QueryBlockNumber: + return queryBlockNumber(ctx, keeper) + case types.QueryStorage: + return queryStorage(ctx, path, keeper) + case types.QueryCode: + return queryCode(ctx, path, keeper) + case types.QueryHashToHeight: + return queryHashToHeight(ctx, path, keeper) + case types.QueryTransactionLogs: + return queryTransactionLogs(ctx, path, keeper) + case types.QueryBloom: + return queryBlockBloom(ctx, path, keeper) + case types.QueryLogs: + return queryLogs(ctx, keeper) + case types.QueryAccount: + return queryAccount(ctx, path, keeper) + case types.QueryExportAccount: + return queryExportAccount(ctx, path, keeper) + default: + return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown query endpoint") + } + } +} + +func queryProtocolVersion(keeper Keeper) ([]byte, error) { + vers := version.ProtocolVersion + + bz, err := codec.MarshalJSONIndent(keeper.cdc, hexutil.Uint(vers)) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return bz, nil +} + +func queryBalance(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { + addr := ethcmn.HexToAddress(path[1]) + balance := keeper.GetBalance(ctx, addr) + balanceStr, err := utils.MarshalBigInt(balance) + if err != nil { + return nil, err + } + + res := types.QueryResBalance{Balance: balanceStr} + bz, err := codec.MarshalJSONIndent(keeper.cdc, res) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return bz, nil +} + +func queryBlockNumber(ctx sdk.Context, keeper Keeper) ([]byte, error) { + num := ctx.BlockHeight() + bnRes := types.QueryResBlockNumber{Number: num} + bz, err := codec.MarshalJSONIndent(keeper.cdc, bnRes) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return bz, nil +} + +func queryStorage(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { + addr := ethcmn.HexToAddress(path[1]) + key := ethcmn.HexToHash(path[2]) + val := keeper.GetState(ctx, addr, key) + res := types.QueryResStorage{Value: val.Bytes()} + bz, err := codec.MarshalJSONIndent(keeper.cdc, res) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + return bz, nil +} + +func queryCode(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { + addr := ethcmn.HexToAddress(path[1]) + code := keeper.GetCode(ctx, addr) + res := types.QueryResCode{Code: code} + bz, err := codec.MarshalJSONIndent(keeper.cdc, res) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return bz, nil +} + +func queryHashToHeight(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { + blockHash := ethcmn.FromHex(path[1]) + blockNumber, found := keeper.GetBlockHash(ctx, blockHash) + if !found { + return []byte{}, fmt.Errorf("block height not found for hash %s", path[1]) + } + + res := types.QueryResBlockNumber{Number: blockNumber} + bz, err := codec.MarshalJSONIndent(keeper.cdc, res) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return bz, nil +} + +func queryBlockBloom(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { + num, err := strconv.ParseInt(path[1], 10, 64) + if err != nil { + return nil, fmt.Errorf("could not unmarshal block height: %w", err) + } + + bloom, found := keeper.GetBlockBloom(ctx.WithBlockHeight(num), num) + if !found { + return nil, fmt.Errorf("block bloom not found for height %d", num) + } + + res := types.QueryBloomFilter{Bloom: bloom} + bz, err := codec.MarshalJSONIndent(keeper.cdc, res) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return bz, nil +} + +func queryTransactionLogs(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { + txHash := ethcmn.HexToHash(path[1]) + + logs, err := keeper.GetLogs(ctx, txHash) + if err != nil { + return nil, err + } + + res := types.QueryETHLogs{Logs: logs} + bz, err := codec.MarshalJSONIndent(keeper.cdc, res) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return bz, nil +} + +func queryLogs(ctx sdk.Context, keeper Keeper) ([]byte, error) { + logs := keeper.AllLogs(ctx) + + res := types.QueryETHLogs{Logs: logs} + bz, err := codec.MarshalJSONIndent(keeper.cdc, res) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + return bz, nil +} + +func queryAccount(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { + addr := ethcmn.HexToAddress(path[1]) + so := keeper.GetOrNewStateObject(ctx, addr) + + balance, err := utils.MarshalBigInt(so.Balance()) + if err != nil { + return nil, err + } + + res := types.QueryResAccount{ + Balance: balance, + CodeHash: so.CodeHash(), + Nonce: so.Nonce(), + } + bz, err := codec.MarshalJSONIndent(keeper.cdc, res) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + return bz, nil +} + +func queryExportAccount(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { + addr := ethcmn.HexToAddress(path[1]) + + var storage types.Storage + err := keeper.ForEachStorage(ctx, addr, func(key, value ethcmn.Hash) bool { + storage = append(storage, types.NewState(key, value)) + return false + }) + if err != nil { + return nil, err + } + + res := types.GenesisAccount{ + Address: addr, + Balance: keeper.GetBalance(ctx, addr), + Code: keeper.GetCode(ctx, addr), + Storage: storage, + } + + // TODO: codec.MarshalJSONIndent doesn't call the String() method of types properly + bz, err := json.MarshalIndent(res, "", "\t") + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return bz, nil +} diff --git a/x/evm/keeper/querier_test.go b/x/evm/keeper/querier_test.go new file mode 100644 index 0000000000..c5a70ae8a9 --- /dev/null +++ b/x/evm/keeper/querier_test.go @@ -0,0 +1,63 @@ +package keeper_test + +import ( + "math/big" + + "github.com/cosmos/ethermint/x/evm/types" + ethtypes "github.com/ethereum/go-ethereum/core/types" + + abci "github.com/tendermint/tendermint/abci/types" +) + +func (suite *KeeperTestSuite) TestQuerier() { + + testCases := []struct { + msg string + path []string + malleate func() + expPass bool + }{ + {"protocol version", []string{types.QueryProtocolVersion}, func() {}, true}, + {"balance", []string{types.QueryBalance, addrHex}, func() { + suite.app.EvmKeeper.SetBalance(suite.ctx, suite.address, big.NewInt(5)) + }, true}, + // {"balance fail", []string{types.QueryBalance, "0x01232"}, func() {}, false}, + {"block number", []string{types.QueryBlockNumber, "0x0"}, func() {}, true}, + {"storage", []string{types.QueryStorage, "0x0", "0x0"}, func() {}, true}, + {"code", []string{types.QueryCode, "0x0"}, func() {}, true}, + {"hash to height", []string{types.QueryHashToHeight, hex}, func() { + suite.app.EvmKeeper.SetBlockHash(suite.ctx, hash, 8) + }, true}, + {"tx logs", []string{types.QueryTransactionLogs, "0x0"}, func() {}, true}, + {"bloom", []string{types.QueryBloom, "4"}, func() { + testBloom := ethtypes.BytesToBloom([]byte{0x1, 0x3}) + suite.app.EvmKeeper.SetBlockBloom(suite.ctx, 4, testBloom) + }, true}, + {"logs", []string{types.QueryLogs, "0x0"}, func() {}, true}, + {"account", []string{types.QueryAccount, "0x0"}, func() {}, true}, + {"exportAccount", []string{types.QueryExportAccount, "0x0"}, func() {}, true}, + {"unknown request", []string{"other"}, func() {}, false}, + } + + for i, tc := range testCases { + suite.Run("", func() { + //nolint + tc := tc + suite.SetupTest() // reset + //nolint + tc.malleate() + + bz, err := suite.querier(suite.ctx, tc.path, abci.RequestQuery{}) + + //nolint + if tc.expPass { + //nolint + suite.Require().NoError(err, "valid test %d failed: %s", i, tc.msg) + suite.Require().NotZero(len(bz)) + } else { + //nolint + suite.Require().Error(err, "invalid test %d passed: %s", i, tc.msg) + } + }) + } +} diff --git a/x/evm/keeper/statedb.go b/x/evm/keeper/statedb.go new file mode 100644 index 0000000000..272404b645 --- /dev/null +++ b/x/evm/keeper/statedb.go @@ -0,0 +1,260 @@ +package keeper + +import ( + "math/big" + + "github.com/cosmos/ethermint/x/evm/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + + ethcmn "github.com/ethereum/go-ethereum/common" + ethstate "github.com/ethereum/go-ethereum/core/state" + ethtypes "github.com/ethereum/go-ethereum/core/types" + ethvm "github.com/ethereum/go-ethereum/core/vm" +) + +// ---------------------------------------------------------------------------- +// Setters +// ---------------------------------------------------------------------------- + +// SetBalance calls CommitStateDB.SetBalance using the passed in context +func (k *Keeper) SetBalance(ctx sdk.Context, addr ethcmn.Address, amount *big.Int) { + k.CommitStateDB.WithContext(ctx).SetBalance(addr, amount) +} + +// AddBalance calls CommitStateDB.AddBalance using the passed in context +func (k *Keeper) AddBalance(ctx sdk.Context, addr ethcmn.Address, amount *big.Int) { + k.CommitStateDB.WithContext(ctx).AddBalance(addr, amount) +} + +// SubBalance calls CommitStateDB.SubBalance using the passed in context +func (k *Keeper) SubBalance(ctx sdk.Context, addr ethcmn.Address, amount *big.Int) { + k.CommitStateDB.WithContext(ctx).SubBalance(addr, amount) +} + +// SetNonce calls CommitStateDB.SetNonce using the passed in context +func (k *Keeper) SetNonce(ctx sdk.Context, addr ethcmn.Address, nonce uint64) { + k.CommitStateDB.WithContext(ctx).SetNonce(addr, nonce) +} + +// SetState calls CommitStateDB.SetState using the passed in context +func (k *Keeper) SetState(ctx sdk.Context, addr ethcmn.Address, key, value ethcmn.Hash) { + k.CommitStateDB.WithContext(ctx).SetState(addr, key, value) +} + +// SetCode calls CommitStateDB.SetCode using the passed in context +func (k *Keeper) SetCode(ctx sdk.Context, addr ethcmn.Address, code []byte) { + k.CommitStateDB.WithContext(ctx).SetCode(addr, code) +} + +// SetLogs calls CommitStateDB.SetLogs using the passed in context +func (k *Keeper) SetLogs(ctx sdk.Context, hash ethcmn.Hash, logs []*ethtypes.Log) error { + return k.CommitStateDB.WithContext(ctx).SetLogs(hash, logs) +} + +// DeleteLogs calls CommitStateDB.DeleteLogs using the passed in context +func (k *Keeper) DeleteLogs(ctx sdk.Context, hash ethcmn.Hash) { + k.CommitStateDB.WithContext(ctx).DeleteLogs(hash) +} + +// AddLog calls CommitStateDB.AddLog using the passed in context +func (k *Keeper) AddLog(ctx sdk.Context, log *ethtypes.Log) { + k.CommitStateDB.WithContext(ctx).AddLog(log) +} + +// AddPreimage calls CommitStateDB.AddPreimage using the passed in context +func (k *Keeper) AddPreimage(ctx sdk.Context, hash ethcmn.Hash, preimage []byte) { + k.CommitStateDB.WithContext(ctx).AddPreimage(hash, preimage) +} + +// AddRefund calls CommitStateDB.AddRefund using the passed in context +func (k *Keeper) AddRefund(ctx sdk.Context, gas uint64) { + k.CommitStateDB.WithContext(ctx).AddRefund(gas) +} + +// SubRefund calls CommitStateDB.SubRefund using the passed in context +func (k *Keeper) SubRefund(ctx sdk.Context, gas uint64) { + k.CommitStateDB.WithContext(ctx).SubRefund(gas) +} + +// ---------------------------------------------------------------------------- +// Getters +// ---------------------------------------------------------------------------- + +// GetBalance calls CommitStateDB.GetBalance using the passed in context +func (k *Keeper) GetBalance(ctx sdk.Context, addr ethcmn.Address) *big.Int { + return k.CommitStateDB.WithContext(ctx).GetBalance(addr) +} + +// GetNonce calls CommitStateDB.GetNonce using the passed in context +func (k *Keeper) GetNonce(ctx sdk.Context, addr ethcmn.Address) uint64 { + return k.CommitStateDB.WithContext(ctx).GetNonce(addr) +} + +// TxIndex calls CommitStateDB.TxIndex using the passed in context +func (k *Keeper) TxIndex(ctx sdk.Context) int { + return k.CommitStateDB.WithContext(ctx).TxIndex() +} + +// BlockHash calls CommitStateDB.BlockHash using the passed in context +func (k *Keeper) BlockHash(ctx sdk.Context) ethcmn.Hash { + return k.CommitStateDB.WithContext(ctx).BlockHash() +} + +// GetCode calls CommitStateDB.GetCode using the passed in context +func (k *Keeper) GetCode(ctx sdk.Context, addr ethcmn.Address) []byte { + return k.CommitStateDB.WithContext(ctx).GetCode(addr) +} + +// GetCodeSize calls CommitStateDB.GetCodeSize using the passed in context +func (k *Keeper) GetCodeSize(ctx sdk.Context, addr ethcmn.Address) int { + return k.CommitStateDB.WithContext(ctx).GetCodeSize(addr) +} + +// GetCodeHash calls CommitStateDB.GetCodeHash using the passed in context +func (k *Keeper) GetCodeHash(ctx sdk.Context, addr ethcmn.Address) ethcmn.Hash { + return k.CommitStateDB.WithContext(ctx).GetCodeHash(addr) +} + +// GetState calls CommitStateDB.GetState using the passed in context +func (k *Keeper) GetState(ctx sdk.Context, addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash { + return k.CommitStateDB.WithContext(ctx).GetState(addr, hash) +} + +// GetCommittedState calls CommitStateDB.GetCommittedState using the passed in context +func (k *Keeper) GetCommittedState(ctx sdk.Context, addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash { + return k.CommitStateDB.WithContext(ctx).GetCommittedState(addr, hash) +} + +// GetLogs calls CommitStateDB.GetLogs using the passed in context +func (k *Keeper) GetLogs(ctx sdk.Context, hash ethcmn.Hash) ([]*ethtypes.Log, error) { + return k.CommitStateDB.WithContext(ctx).GetLogs(hash) +} + +// AllLogs calls CommitStateDB.AllLogs using the passed in context +func (k *Keeper) AllLogs(ctx sdk.Context) []*ethtypes.Log { + return k.CommitStateDB.WithContext(ctx).AllLogs() +} + +// GetRefund calls CommitStateDB.GetRefund using the passed in context +func (k *Keeper) GetRefund(ctx sdk.Context) uint64 { + return k.CommitStateDB.WithContext(ctx).GetRefund() +} + +// Preimages calls CommitStateDB.Preimages using the passed in context +func (k *Keeper) Preimages(ctx sdk.Context) map[ethcmn.Hash][]byte { + return k.CommitStateDB.WithContext(ctx).Preimages() +} + +// HasSuicided calls CommitStateDB.HasSuicided using the passed in context +func (k *Keeper) HasSuicided(ctx sdk.Context, addr ethcmn.Address) bool { + return k.CommitStateDB.WithContext(ctx).HasSuicided(addr) +} + +// StorageTrie calls CommitStateDB.StorageTrie using the passed in context +func (k *Keeper) StorageTrie(ctx sdk.Context, addr ethcmn.Address) ethstate.Trie { + return k.CommitStateDB.WithContext(ctx).StorageTrie(addr) +} + +// ---------------------------------------------------------------------------- +// Persistence +// ---------------------------------------------------------------------------- + +// Commit calls CommitStateDB.Commit using the passed in context +func (k *Keeper) Commit(ctx sdk.Context, deleteEmptyObjects bool) (root ethcmn.Hash, err error) { + return k.CommitStateDB.WithContext(ctx).Commit(deleteEmptyObjects) +} + +// Finalise calls CommitStateDB.Finalise using the passed in context +func (k *Keeper) Finalise(ctx sdk.Context, deleteEmptyObjects bool) error { + return k.CommitStateDB.WithContext(ctx).Finalise(deleteEmptyObjects) +} + +// IntermediateRoot calls CommitStateDB.IntermediateRoot using the passed in context +func (k *Keeper) IntermediateRoot(ctx sdk.Context, deleteEmptyObjects bool) error { + _, err := k.CommitStateDB.WithContext(ctx).IntermediateRoot(deleteEmptyObjects) + return err +} + +// ---------------------------------------------------------------------------- +// Snapshotting +// ---------------------------------------------------------------------------- + +// Snapshot calls CommitStateDB.Snapshot using the passed in context +func (k *Keeper) Snapshot(ctx sdk.Context) int { + return k.CommitStateDB.WithContext(ctx).Snapshot() +} + +// RevertToSnapshot calls CommitStateDB.RevertToSnapshot using the passed in context +func (k *Keeper) RevertToSnapshot(ctx sdk.Context, revID int) { + k.CommitStateDB.WithContext(ctx).RevertToSnapshot(revID) +} + +// ---------------------------------------------------------------------------- +// Auxiliary +// ---------------------------------------------------------------------------- + +// Database calls CommitStateDB.Database using the passed in context +func (k *Keeper) Database(ctx sdk.Context) ethstate.Database { + return k.CommitStateDB.WithContext(ctx).Database() +} + +// Empty calls CommitStateDB.Empty using the passed in context +func (k *Keeper) Empty(ctx sdk.Context, addr ethcmn.Address) bool { + return k.CommitStateDB.WithContext(ctx).Empty(addr) +} + +// Exist calls CommitStateDB.Exist using the passed in context +func (k *Keeper) Exist(ctx sdk.Context, addr ethcmn.Address) bool { + return k.CommitStateDB.WithContext(ctx).Exist(addr) +} + +// Error calls CommitStateDB.Error using the passed in context +func (k *Keeper) Error(ctx sdk.Context) error { + return k.CommitStateDB.WithContext(ctx).Error() +} + +// Suicide calls CommitStateDB.Suicide using the passed in context +func (k *Keeper) Suicide(ctx sdk.Context, addr ethcmn.Address) bool { + return k.CommitStateDB.WithContext(ctx).Suicide(addr) +} + +// Reset calls CommitStateDB.Reset using the passed in context +func (k *Keeper) Reset(ctx sdk.Context, root ethcmn.Hash) error { + return k.CommitStateDB.WithContext(ctx).Reset(root) +} + +// Prepare calls CommitStateDB.Prepare using the passed in context +func (k *Keeper) Prepare(ctx sdk.Context, thash, bhash ethcmn.Hash, txi int) { + k.CommitStateDB.WithContext(ctx).Prepare(thash, bhash, txi) +} + +// CreateAccount calls CommitStateDB.CreateAccount using the passed in context +func (k *Keeper) CreateAccount(ctx sdk.Context, addr ethcmn.Address) { + k.CommitStateDB.WithContext(ctx).CreateAccount(addr) +} + +// UpdateAccounts calls CommitStateDB.UpdateAccounts using the passed in context +func (k *Keeper) UpdateAccounts(ctx sdk.Context) { + k.CommitStateDB.WithContext(ctx).UpdateAccounts() +} + +// ClearStateObjects calls CommitStateDB.ClearStateObjects using the passed in context +func (k *Keeper) ClearStateObjects(ctx sdk.Context) { + k.CommitStateDB.WithContext(ctx).ClearStateObjects() +} + +// Copy calls CommitStateDB.Copy using the passed in context +func (k *Keeper) Copy(ctx sdk.Context) ethvm.StateDB { + return k.CommitStateDB.WithContext(ctx).Copy() +} + +// ForEachStorage calls CommitStateDB.ForEachStorage using passed in context +func (k *Keeper) ForEachStorage(ctx sdk.Context, addr ethcmn.Address, cb func(key, value ethcmn.Hash) bool) error { + return k.CommitStateDB.WithContext(ctx).ForEachStorage(addr, cb) +} + +// GetOrNewStateObject calls CommitStateDB.GetOrNetStateObject using the passed in context +func (k *Keeper) GetOrNewStateObject(ctx sdk.Context, addr ethcmn.Address) types.StateObject { + return k.CommitStateDB.WithContext(ctx).GetOrNewStateObject(addr) +} diff --git a/x/evm/keeper/statedb_test.go b/x/evm/keeper/statedb_test.go new file mode 100644 index 0000000000..01e56637f5 --- /dev/null +++ b/x/evm/keeper/statedb_test.go @@ -0,0 +1,644 @@ +package keeper_test + +import ( + "fmt" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + + ethcmn "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + + "github.com/cosmos/ethermint/crypto" + ethermint "github.com/cosmos/ethermint/types" + "github.com/cosmos/ethermint/x/evm/types" +) + +func (suite *KeeperTestSuite) TestBloomFilter() { + // Prepare db for logs + tHash := ethcmn.BytesToHash([]byte{0x1}) + suite.app.EvmKeeper.Prepare(suite.ctx, tHash, ethcmn.Hash{}, 0) + contractAddress := ethcmn.BigToAddress(big.NewInt(1)) + log := ethtypes.Log{Address: contractAddress} + + testCase := []struct { + name string + malleate func() + numLogs int + isBloom bool + }{ + { + "no logs", + func() {}, + 0, + false, + }, + { + "add log", + func() { + suite.app.EvmKeeper.AddLog(suite.ctx, &log) + }, + 1, + false, + }, + { + "bloom", + func() {}, + 0, + true, + }, + } + + for _, tc := range testCase { + tc.malleate() + logs, err := suite.app.EvmKeeper.GetLogs(suite.ctx, tHash) + if !tc.isBloom { + suite.Require().NoError(err, tc.name) + suite.Require().Len(logs, tc.numLogs, tc.name) + if len(logs) != 0 { + suite.Require().Equal(log, *logs[0], tc.name) + } + } else { + // get logs bloom from the log + bloomInt := ethtypes.LogsBloom(logs) + bloomFilter := ethtypes.BytesToBloom(bloomInt.Bytes()) + suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress), tc.name) + suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))), tc.name) + } + } +} + +func (suite *KeeperTestSuite) TestStateDB_Balance() { + testCase := []struct { + name string + malleate func() + balance *big.Int + }{ + { + "set balance", + func() { + suite.app.EvmKeeper.SetBalance(suite.ctx, suite.address, big.NewInt(100)) + }, + big.NewInt(100), + }, + { + "sub balance", + func() { + suite.app.EvmKeeper.SubBalance(suite.ctx, suite.address, big.NewInt(100)) + }, + big.NewInt(0), + }, + { + "add balance", + func() { + suite.app.EvmKeeper.AddBalance(suite.ctx, suite.address, big.NewInt(200)) + }, + big.NewInt(200), + }, + } + + for _, tc := range testCase { + tc.malleate() + suite.Require().Equal(tc.balance, suite.app.EvmKeeper.GetBalance(suite.ctx, suite.address), tc.name) + } +} + +func (suite *KeeperTestSuite) TestStateDBNonce() { + nonce := uint64(123) + suite.app.EvmKeeper.SetNonce(suite.ctx, suite.address, nonce) + suite.Require().Equal(nonce, suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address)) +} + +func (suite *KeeperTestSuite) TestStateDB_Error() { + nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, ethcmn.Address{}) + suite.Require().Equal(0, int(nonce)) + suite.Require().Error(suite.app.EvmKeeper.Error(suite.ctx)) +} + +func (suite *KeeperTestSuite) TestStateDB_Database() { + suite.Require().Nil(suite.app.EvmKeeper.Database(suite.ctx)) +} + +func (suite *KeeperTestSuite) TestStateDB_State() { + key := ethcmn.BytesToHash([]byte("foo")) + val := ethcmn.BytesToHash([]byte("bar")) + suite.app.EvmKeeper.SetState(suite.ctx, suite.address, key, val) + + testCase := []struct { + name string + address ethcmn.Address + key ethcmn.Hash + value ethcmn.Hash + }{ + { + "found state", + suite.address, + ethcmn.BytesToHash([]byte("foo")), + ethcmn.BytesToHash([]byte("bar")), + }, + { + "state not found", + suite.address, + ethcmn.BytesToHash([]byte("key")), + ethcmn.Hash{}, + }, + { + "object not found", + ethcmn.Address{}, + ethcmn.BytesToHash([]byte("foo")), + ethcmn.Hash{}, + }, + } + for _, tc := range testCase { + value := suite.app.EvmKeeper.GetState(suite.ctx, tc.address, tc.key) + suite.Require().Equal(tc.value, value, tc.name) + } +} + +func (suite *KeeperTestSuite) TestStateDB_Code() { + testCase := []struct { + name string + address ethcmn.Address + code []byte + malleate func() + }{ + { + "no stored code for state object", + suite.address, + nil, + func() {}, + }, + { + "existing address", + suite.address, + []byte("code"), + func() { + suite.app.EvmKeeper.SetCode(suite.ctx, suite.address, []byte("code")) + }, + }, + { + "state object not found", + ethcmn.Address{}, + nil, + func() {}, + }, + } + + for _, tc := range testCase { + tc.malleate() + + suite.Require().Equal(tc.code, suite.app.EvmKeeper.GetCode(suite.ctx, tc.address), tc.name) + suite.Require().Equal(len(tc.code), suite.app.EvmKeeper.GetCodeSize(suite.ctx, tc.address), tc.name) + } +} + +func (suite *KeeperTestSuite) TestStateDB_Logs() { + testCase := []struct { + name string + log ethtypes.Log + }{ + { + "state db log", + ethtypes.Log{ + Address: suite.address, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.Hash{}, + TxIndex: 1, + BlockHash: ethcmn.Hash{}, + Index: 1, + Removed: false, + }, + }, + } + + for _, tc := range testCase { + hash := ethcmn.BytesToHash([]byte("hash")) + logs := []*ethtypes.Log{&tc.log} + + err := suite.app.EvmKeeper.SetLogs(suite.ctx, hash, logs) + suite.Require().NoError(err, tc.name) + dbLogs, err := suite.app.EvmKeeper.GetLogs(suite.ctx, hash) + suite.Require().NoError(err, tc.name) + suite.Require().Equal(logs, dbLogs, tc.name) + + suite.app.EvmKeeper.DeleteLogs(suite.ctx, hash) + dbLogs, err = suite.app.EvmKeeper.GetLogs(suite.ctx, hash) + suite.Require().NoError(err, tc.name) + suite.Require().Empty(dbLogs, tc.name) + + suite.app.EvmKeeper.AddLog(suite.ctx, &tc.log) + suite.Require().Equal(logs, suite.app.EvmKeeper.AllLogs(suite.ctx), tc.name) + + //resets state but checking to see if storekey still persists. + err = suite.app.EvmKeeper.Reset(suite.ctx, hash) + suite.Require().NoError(err, tc.name) + suite.Require().Equal(logs, suite.app.EvmKeeper.AllLogs(suite.ctx), tc.name) + } +} + +func (suite *KeeperTestSuite) TestStateDB_Preimage() { + hash := ethcmn.BytesToHash([]byte("hash")) + preimage := []byte("preimage") + + suite.app.EvmKeeper.AddPreimage(suite.ctx, hash, preimage) + suite.Require().Equal(preimage, suite.app.EvmKeeper.Preimages(suite.ctx)[hash]) +} + +func (suite *KeeperTestSuite) TestStateDB_Refund() { + testCase := []struct { + name string + addAmount uint64 + subAmount uint64 + expRefund uint64 + expPanic bool + }{ + { + "refund 0", + 0, 0, 0, + false, + }, + { + "refund positive amount", + 100, 0, 100, + false, + }, + { + "refund panic", + 100, 200, 100, + true, + }, + } + + for _, tc := range testCase { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + suite.app.EvmKeeper.AddRefund(suite.ctx, tc.addAmount) + suite.Require().Equal(tc.addAmount, suite.app.EvmKeeper.GetRefund(suite.ctx)) + + if tc.expPanic { + suite.Panics(func() { + suite.app.EvmKeeper.SubRefund(suite.ctx, tc.subAmount) + }) + } else { + suite.app.EvmKeeper.SubRefund(suite.ctx, tc.subAmount) + suite.Require().Equal(tc.expRefund, suite.app.EvmKeeper.GetRefund(suite.ctx)) + } + }) + } +} + +func (suite *KeeperTestSuite) TestStateDB_CreateAccount() { + prevBalance := big.NewInt(12) + + testCase := []struct { + name string + address ethcmn.Address + malleate func() + }{ + { + "existing account", + suite.address, + func() { + suite.app.EvmKeeper.AddBalance(suite.ctx, suite.address, prevBalance) + }, + }, + { + "new account", + ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b4c1"), + func() { + prevBalance = big.NewInt(0) + }, + }, + } + + for _, tc := range testCase { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + tc.malleate() + + suite.app.EvmKeeper.CreateAccount(suite.ctx, tc.address) + suite.Require().True(suite.app.EvmKeeper.Exist(suite.ctx, tc.address)) + suite.Require().Equal(prevBalance, suite.app.EvmKeeper.GetBalance(suite.ctx, tc.address)) + }) + } +} + +func (suite *KeeperTestSuite) TestStateDB_ClearStateObj() { + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + suite.app.EvmKeeper.CreateAccount(suite.ctx, addr) + suite.Require().True(suite.app.EvmKeeper.Exist(suite.ctx, addr)) + + suite.app.EvmKeeper.ClearStateObjects(suite.ctx) + suite.Require().False(suite.app.EvmKeeper.Exist(suite.ctx, addr)) +} + +func (suite *KeeperTestSuite) TestStateDB_Reset() { + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + suite.app.EvmKeeper.CreateAccount(suite.ctx, addr) + suite.Require().True(suite.app.EvmKeeper.Exist(suite.ctx, addr)) + + err = suite.app.EvmKeeper.Reset(suite.ctx, ethcmn.BytesToHash(nil)) + suite.Require().NoError(err) + suite.Require().False(suite.app.EvmKeeper.Exist(suite.ctx, addr)) +} + +func (suite *KeeperTestSuite) TestSuiteDB_Prepare() { + thash := ethcmn.BytesToHash([]byte("thash")) + bhash := ethcmn.BytesToHash([]byte("bhash")) + txi := 1 + + suite.app.EvmKeeper.Prepare(suite.ctx, thash, bhash, txi) + + suite.Require().Equal(txi, suite.app.EvmKeeper.TxIndex(suite.ctx)) + suite.Require().Equal(bhash, suite.app.EvmKeeper.BlockHash(suite.ctx)) +} + +func (suite *KeeperTestSuite) TestSuiteDB_CopyState() { + testCase := []struct { + name string + log ethtypes.Log + }{ + { + "copy state", + ethtypes.Log{ + Address: suite.address, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.Hash{}, + TxIndex: 1, + BlockHash: ethcmn.Hash{}, + Index: 1, + Removed: false, + }, + }, + } + + for _, tc := range testCase { + hash := ethcmn.BytesToHash([]byte("hash")) + logs := []*ethtypes.Log{&tc.log} + + err := suite.app.EvmKeeper.SetLogs(suite.ctx, hash, logs) + suite.Require().NoError(err, tc.name) + + copyDB := suite.app.EvmKeeper.Copy(suite.ctx) + suite.Require().Equal(suite.app.EvmKeeper.Exist(suite.ctx, suite.address), copyDB.Exist(suite.address), tc.name) + } +} + +func (suite *KeeperTestSuite) TestSuiteDB_Empty() { + suite.Require().True(suite.app.EvmKeeper.Empty(suite.ctx, suite.address)) + + suite.app.EvmKeeper.SetBalance(suite.ctx, suite.address, big.NewInt(100)) + suite.Require().False(suite.app.EvmKeeper.Empty(suite.ctx, suite.address)) +} + +func (suite *KeeperTestSuite) TestSuiteDB_Suicide() { + testCase := []struct { + name string + amount *big.Int + expPass bool + delete bool + }{ + { + "suicide zero balance", + big.NewInt(0), + false, false, + }, + { + "suicide with balance", + big.NewInt(100), + true, false, + }, + { + "delete", + big.NewInt(0), + true, true, + }, + } + + for _, tc := range testCase { + if tc.delete { + _, err := suite.app.EvmKeeper.Commit(suite.ctx, tc.delete) + suite.Require().NoError(err, tc.name) + suite.Require().False(suite.app.EvmKeeper.Exist(suite.ctx, suite.address), tc.name) + continue + } + + if tc.expPass { + suite.app.EvmKeeper.SetBalance(suite.ctx, suite.address, tc.amount) + suicide := suite.app.EvmKeeper.Suicide(suite.ctx, suite.address) + suite.Require().True(suicide, tc.name) + suite.Require().True(suite.app.EvmKeeper.HasSuicided(suite.ctx, suite.address), tc.name) + } else { + //Suicide only works for an account with non-zero balance/nonce + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + suicide := suite.app.EvmKeeper.Suicide(suite.ctx, addr) + suite.Require().False(suicide, tc.name) + suite.Require().False(suite.app.EvmKeeper.HasSuicided(suite.ctx, addr), tc.name) + } + } +} + +func (suite *KeeperTestSuite) TestCommitStateDB_Commit() { + testCase := []struct { + name string + malleate func() + deleteObjs bool + expPass bool + }{ + { + "commit suicided", + func() { + ok := suite.app.EvmKeeper.Suicide(suite.ctx, suite.address) + suite.Require().True(ok) + }, + true, true, + }, + { + "commit with dirty value", + func() { + suite.app.EvmKeeper.SetCode(suite.ctx, suite.address, []byte("code")) + }, + false, true, + }, + } + + for _, tc := range testCase { + tc.malleate() + + hash, err := suite.app.EvmKeeper.Commit(suite.ctx, tc.deleteObjs) + suite.Require().Equal(ethcmn.Hash{}, hash) + + if !tc.expPass { + suite.Require().Error(err, tc.name) + continue + } + + suite.Require().NoError(err, tc.name) + acc := suite.app.AccountKeeper.GetAccount(suite.ctx, sdk.AccAddress(suite.address.Bytes())) + + if tc.deleteObjs { + suite.Require().Nil(acc, tc.name) + continue + } + + suite.Require().NotNil(acc, tc.name) + ethAcc, ok := acc.(*ethermint.EthAccount) + suite.Require().True(ok) + suite.Require().Equal(ethcrypto.Keccak256([]byte("code")), ethAcc.CodeHash) + } +} + +func (suite *KeeperTestSuite) TestCommitStateDB_Finalize() { + testCase := []struct { + name string + malleate func() + deleteObjs bool + expPass bool + }{ + { + "finalize suicided", + func() { + ok := suite.app.EvmKeeper.Suicide(suite.ctx, suite.address) + suite.Require().True(ok) + }, + true, true, + }, + { + "finalize, not suicided", + func() { + suite.app.EvmKeeper.AddBalance(suite.ctx, suite.address, big.NewInt(5)) + }, + false, true, + }, + { + "finalize, dirty storage", + func() { + suite.app.EvmKeeper.SetState(suite.ctx, suite.address, ethcmn.BytesToHash([]byte("key")), ethcmn.BytesToHash([]byte("value"))) + }, + false, true, + }, + } + + for _, tc := range testCase { + tc.malleate() + + err := suite.app.EvmKeeper.Finalise(suite.ctx, tc.deleteObjs) + + if !tc.expPass { + suite.Require().Error(err, tc.name) + hash := suite.app.EvmKeeper.GetCommittedState(suite.ctx, suite.address, ethcmn.BytesToHash([]byte("key"))) + suite.Require().NotEqual(ethcmn.Hash{}, hash, tc.name) + continue + } + + suite.Require().NoError(err, tc.name) + acc := suite.app.AccountKeeper.GetAccount(suite.ctx, sdk.AccAddress(suite.address.Bytes())) + + if tc.deleteObjs { + suite.Require().Nil(acc, tc.name) + continue + } + + suite.Require().NotNil(acc, tc.name) + } +} +func (suite *KeeperTestSuite) TestCommitStateDB_GetCommittedState() { + hash := suite.app.EvmKeeper.GetCommittedState(suite.ctx, ethcmn.Address{}, ethcmn.BytesToHash([]byte("key"))) + suite.Require().Equal(ethcmn.Hash{}, hash) +} + +func (suite *KeeperTestSuite) TestCommitStateDB_Snapshot() { + id := suite.app.EvmKeeper.Snapshot(suite.ctx) + suite.Require().NotPanics(func() { + suite.app.EvmKeeper.RevertToSnapshot(suite.ctx, id) + }) + + suite.Require().Panics(func() { + suite.app.EvmKeeper.RevertToSnapshot(suite.ctx, -1) + }, "invalid revision should panic") +} + +func (suite *KeeperTestSuite) TestCommitStateDB_ForEachStorage() { + var storage types.Storage + + testCase := []struct { + name string + malleate func() + callback func(key, value ethcmn.Hash) (stop bool) + expValues []ethcmn.Hash + }{ + { + "aggregate state", + func() { + for i := 0; i < 5; i++ { + suite.app.EvmKeeper.SetState(suite.ctx, suite.address, ethcmn.BytesToHash([]byte(fmt.Sprintf("key%d", i))), ethcmn.BytesToHash([]byte(fmt.Sprintf("value%d", i)))) + } + }, + func(key, value ethcmn.Hash) bool { + storage = append(storage, types.NewState(key, value)) + return false + }, + []ethcmn.Hash{ + ethcmn.BytesToHash([]byte("value0")), + ethcmn.BytesToHash([]byte("value1")), + ethcmn.BytesToHash([]byte("value2")), + ethcmn.BytesToHash([]byte("value3")), + ethcmn.BytesToHash([]byte("value4")), + }, + }, + { + "filter state", + func() { + suite.app.EvmKeeper.SetState(suite.ctx, suite.address, ethcmn.BytesToHash([]byte("key")), ethcmn.BytesToHash([]byte("value"))) + suite.app.EvmKeeper.SetState(suite.ctx, suite.address, ethcmn.BytesToHash([]byte("filterkey")), ethcmn.BytesToHash([]byte("filtervalue"))) + }, + func(key, value ethcmn.Hash) bool { + if value == ethcmn.BytesToHash([]byte("filtervalue")) { + storage = append(storage, types.NewState(key, value)) + return true + } + return false + }, + []ethcmn.Hash{ + ethcmn.BytesToHash([]byte("filtervalue")), + }, + }, + } + + for _, tc := range testCase { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + tc.malleate() + suite.app.EvmKeeper.Finalise(suite.ctx, false) + + err := suite.app.EvmKeeper.ForEachStorage(suite.ctx, suite.address, tc.callback) + suite.Require().NoError(err) + suite.Require().Equal(len(tc.expValues), len(storage), fmt.Sprintf("Expected values:\n%v\nStorage Values\n%v", tc.expValues, storage)) + + vals := make([]ethcmn.Hash, len(storage)) + for i := range storage { + vals[i] = storage[i].Value + } + + suite.Require().ElementsMatch(tc.expValues, vals) + }) + storage = types.Storage{} + } +} diff --git a/x/evm/module.go b/x/evm/module.go new file mode 100644 index 0000000000..e1108e9e46 --- /dev/null +++ b/x/evm/module.go @@ -0,0 +1,135 @@ +package evm + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/cosmos/ethermint/x/evm/client/cli" + "github.com/cosmos/ethermint/x/evm/keeper" + "github.com/cosmos/ethermint/x/evm/types" +) + +var _ module.AppModuleBasic = AppModuleBasic{} +var _ module.AppModule = AppModule{} + +// AppModuleBasic struct +type AppModuleBasic struct{} + +// Name for app module basic +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +// RegisterCodec registers types for module +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + types.RegisterCodec(cdc) +} + +// DefaultGenesis is json default structure +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return types.ModuleCdc.MustMarshalJSON(types.DefaultGenesisState()) +} + +// ValidateGenesis is the validation check of the Genesis +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + var genesisState types.GenesisState + err := types.ModuleCdc.UnmarshalJSON(bz, &genesisState) + if err != nil { + return err + } + + return genesisState.Validate() +} + +// RegisterRESTRoutes Registers rest routes +func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { + //rpc.RegisterRoutes(ctx, rtr, StoreKey) +} + +// GetQueryCmd Gets the root query command of this module +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetQueryCmd(types.ModuleName, cdc) +} + +// GetTxCmd Gets the root tx command of this module +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetTxCmd(cdc) +} + +//____________________________________________________________________________ + +// AppModule implements an application module for the evm module. +type AppModule struct { + AppModuleBasic + keeper Keeper + ak types.AccountKeeper +} + +// NewAppModule creates a new AppModule Object +func NewAppModule(k Keeper, ak types.AccountKeeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + keeper: k, + ak: ak, + } +} + +// Name is module name +func (AppModule) Name() string { + return types.ModuleName +} + +// RegisterInvariants interface for registering invariants +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} + +// Route specifies path for transactions +func (am AppModule) Route() string { + return types.RouterKey +} + +// NewHandler sets up a new handler for module +func (am AppModule) NewHandler() sdk.Handler { + return NewHandler(am.keeper) +} + +// QuerierRoute sets up path for queries +func (am AppModule) QuerierRoute() string { + return types.ModuleName +} + +// NewQuerierHandler sets up new querier handler for module +func (am AppModule) NewQuerierHandler() sdk.Querier { + return keeper.NewQuerier(am.keeper) +} + +// BeginBlock function for module at start of each block +func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { + BeginBlock(am.keeper, ctx, req) +} + +// EndBlock function for module at end of block +func (am AppModule) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate { + return EndBlock(am.keeper, ctx, req) +} + +// InitGenesis instantiates the genesis state +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + types.ModuleCdc.MustUnmarshalJSON(data, &genesisState) + return InitGenesis(ctx, am.keeper, genesisState) +} + +// ExportGenesis exports the genesis state to be used by daemon +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + gs := ExportGenesis(ctx, am.keeper, am.ak) + return types.ModuleCdc.MustMarshalJSON(gs) +} diff --git a/x/evm/module_test.go b/x/evm/module_test.go new file mode 100644 index 0000000000..561210c8df --- /dev/null +++ b/x/evm/module_test.go @@ -0,0 +1,38 @@ +package evm_test + +import ( + "encoding/json" + + "github.com/cosmos/ethermint/x/evm" + + "github.com/ethereum/go-ethereum/common" +) + +var testJSON = `{ + "accounts": [ + { + "address": "0x00cabdd44664b73cfc3194b9d32eb6c351ef7652", + "balance": 34 + }, + { + "address": "0x2cc7fdf9fde6746731d7f11979609d455c2c197a", + "balance": 0, + "code": "0x60806040" + } + ], + "params": { + "evm_denom": "aphoton" + } + }` + +func (suite *EvmTestSuite) TestInitGenesis() { + am := evm.NewAppModule(suite.app.EvmKeeper, suite.app.AccountKeeper) + in := json.RawMessage([]byte(testJSON)) + _ = am.InitGenesis(suite.ctx, in) + + testAddr := common.HexToAddress("0x2cc7fdf9fde6746731d7f11979609d455c2c197a") + + res := suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx).GetCode(testAddr) + expectedCode := common.FromHex("0x60806040") + suite.Require().Equal(expectedCode, res) +} diff --git a/x/evm/spec/README.md b/x/evm/spec/README.md new file mode 100644 index 0000000000..fc9247e5d1 --- /dev/null +++ b/x/evm/spec/README.md @@ -0,0 +1,16 @@ + + +# `evm` + +## Abstract + + + +## Content + + \ No newline at end of file diff --git a/x/evm/types/chain_config.go b/x/evm/types/chain_config.go new file mode 100644 index 0000000000..a923bc97a0 --- /dev/null +++ b/x/evm/types/chain_config.go @@ -0,0 +1,172 @@ +package types + +import ( + "math/big" + "strings" + + "gopkg.in/yaml.v2" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" +) + +// ChainConfig defines the Ethereum ChainConfig parameters using sdk.Int values instead of big.Int. +// +// NOTE 1: Since empty/uninitialized Ints (i.e with a nil big.Int value) are parsed to zero, we need to manually +// specify that negative Int values will be considered as nil. See getBlockValue for reference. +// +// NOTE 2: This type is not a configurable Param since the SDK does not allow for validation against +// a previous stored parameter values or the current block height (retrieved from context). If you +// want to update the config values, use an software upgrade procedure. +type ChainConfig struct { + HomesteadBlock sdk.Int `json:"homestead_block" yaml:"homestead_block"` // Homestead switch block (< 0 no fork, 0 = already homestead) + + DAOForkBlock sdk.Int `json:"dao_fork_block" yaml:"dao_fork_block"` // TheDAO hard-fork switch block (< 0 no fork) + DAOForkSupport bool `json:"dao_fork_support" yaml:"dao_fork_support"` // Whether the nodes supports or opposes the DAO hard-fork + + // EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150) + EIP150Block sdk.Int `json:"eip150_block" yaml:"eip150_block"` // EIP150 HF block (< 0 no fork) + EIP150Hash string `json:"eip150_hash" yaml:"eip150_hash"` // EIP150 HF hash (needed for header only clients as only gas pricing changed) + + EIP155Block sdk.Int `json:"eip155_block" yaml:"eip155_block"` // EIP155 HF block + EIP158Block sdk.Int `json:"eip158_block" yaml:"eip158_block"` // EIP158 HF block + + ByzantiumBlock sdk.Int `json:"byzantium_block" yaml:"byzantium_block"` // Byzantium switch block (< 0 no fork, 0 = already on byzantium) + ConstantinopleBlock sdk.Int `json:"constantinople_block" yaml:"constantinople_block"` // Constantinople switch block (< 0 no fork, 0 = already activated) + PetersburgBlock sdk.Int `json:"petersburg_block" yaml:"petersburg_block"` // Petersburg switch block (< 0 same as Constantinople) + IstanbulBlock sdk.Int `json:"istanbul_block" yaml:"istanbul_block"` // Istanbul switch block (< 0 no fork, 0 = already on istanbul) + MuirGlacierBlock sdk.Int `json:"muir_glacier_block" yaml:"muir_glacier_block"` // Eip-2384 (bomb delay) switch block (< 0 no fork, 0 = already activated) + + YoloV1Block sdk.Int `json:"yoloV1_block" yaml:"yoloV1_block"` // YOLO v1: https://github.com/ethereum/EIPs/pull/2657 (Ephemeral testnet) + EWASMBlock sdk.Int `json:"ewasm_block" yaml:"ewasm_block"` // EWASM switch block (< 0 no fork, 0 = already activated) +} + +// EthereumConfig returns an Ethereum ChainConfig for EVM state transitions. +// All the negative or nil values are converted to nil +func (cc ChainConfig) EthereumConfig(chainID *big.Int) *params.ChainConfig { + return ¶ms.ChainConfig{ + ChainID: chainID, + HomesteadBlock: getBlockValue(cc.HomesteadBlock), + DAOForkBlock: getBlockValue(cc.DAOForkBlock), + DAOForkSupport: cc.DAOForkSupport, + EIP150Block: getBlockValue(cc.EIP150Block), + EIP150Hash: common.HexToHash(cc.EIP150Hash), + EIP155Block: getBlockValue(cc.EIP155Block), + EIP158Block: getBlockValue(cc.EIP158Block), + ByzantiumBlock: getBlockValue(cc.ByzantiumBlock), + ConstantinopleBlock: getBlockValue(cc.ConstantinopleBlock), + PetersburgBlock: getBlockValue(cc.PetersburgBlock), + IstanbulBlock: getBlockValue(cc.IstanbulBlock), + MuirGlacierBlock: getBlockValue(cc.MuirGlacierBlock), + YoloV1Block: getBlockValue(cc.YoloV1Block), + EWASMBlock: getBlockValue(cc.EWASMBlock), + } +} + +// String implements the fmt.Stringer interface +func (cc ChainConfig) String() string { + out, _ := yaml.Marshal(cc) + return string(out) +} + +// DefaultChainConfig returns default evm parameters. Th +func DefaultChainConfig() ChainConfig { + return ChainConfig{ + HomesteadBlock: sdk.ZeroInt(), + DAOForkBlock: sdk.ZeroInt(), + DAOForkSupport: true, + EIP150Block: sdk.ZeroInt(), + EIP150Hash: common.Hash{}.String(), + EIP155Block: sdk.ZeroInt(), + EIP158Block: sdk.ZeroInt(), + ByzantiumBlock: sdk.ZeroInt(), + ConstantinopleBlock: sdk.ZeroInt(), + PetersburgBlock: sdk.ZeroInt(), + IstanbulBlock: sdk.NewInt(-1), + MuirGlacierBlock: sdk.NewInt(-1), + YoloV1Block: sdk.NewInt(-1), + EWASMBlock: sdk.NewInt(-1), + } +} + +func getBlockValue(block sdk.Int) *big.Int { + if block.IsNegative() { + return nil + } + + return block.BigInt() +} + +// Validate performs a basic validation of the ChainConfig params. The function will return an error +// if any of the block values is uninitialized (i.e nil) or if the EIP150Hash is an invalid hash. +func (cc ChainConfig) Validate() error { + if err := validateBlock(cc.HomesteadBlock); err != nil { + return sdkerrors.Wrap(err, "homesteadBlock") + } + if err := validateBlock(cc.DAOForkBlock); err != nil { + return sdkerrors.Wrap(err, "daoForkBlock") + } + if err := validateBlock(cc.EIP150Block); err != nil { + return sdkerrors.Wrap(err, "eip150Block") + } + if err := validateHash(cc.EIP150Hash); err != nil { + return err + } + if err := validateBlock(cc.EIP155Block); err != nil { + return sdkerrors.Wrap(err, "eip155Block") + } + if err := validateBlock(cc.EIP158Block); err != nil { + return sdkerrors.Wrap(err, "eip158Block") + } + if err := validateBlock(cc.ByzantiumBlock); err != nil { + return sdkerrors.Wrap(err, "byzantiumBlock") + } + if err := validateBlock(cc.ConstantinopleBlock); err != nil { + return sdkerrors.Wrap(err, "constantinopleBlock") + } + if err := validateBlock(cc.PetersburgBlock); err != nil { + return sdkerrors.Wrap(err, "petersburgBlock") + } + if err := validateBlock(cc.IstanbulBlock); err != nil { + return sdkerrors.Wrap(err, "istanbulBlock") + } + if err := validateBlock(cc.MuirGlacierBlock); err != nil { + return sdkerrors.Wrap(err, "muirGlacierBlock") + } + if err := validateBlock(cc.YoloV1Block); err != nil { + return sdkerrors.Wrap(err, "yoloV1Block") + } + if err := validateBlock(cc.EWASMBlock); err != nil { + return sdkerrors.Wrap(err, "eWASMBlock") + } + + return nil +} + +func validateHash(hex string) error { + if hex != "" && strings.TrimSpace(hex) == "" { + return sdkerrors.Wrapf(ErrInvalidChainConfig, "hash cannot be blank") + } + + bz := common.FromHex(hex) + lenHex := len(bz) + if lenHex > 0 && lenHex != common.HashLength { + return sdkerrors.Wrapf(ErrInvalidChainConfig, "invalid hash length, expected %d, got %d", common.HashLength, lenHex) + } + + return nil +} + +func validateBlock(block sdk.Int) error { + if block == (sdk.Int{}) || block.BigInt() == nil { + return sdkerrors.Wrapf( + ErrInvalidChainConfig, + "cannot use uninitialized or nil values for Int, set a negative Int value if you want to define a nil Ethereum block", + ) + } + + return nil +} diff --git a/x/evm/types/chain_config_test.go b/x/evm/types/chain_config_test.go new file mode 100644 index 0000000000..00b6d9489e --- /dev/null +++ b/x/evm/types/chain_config_test.go @@ -0,0 +1,245 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/ethereum/go-ethereum/common" +) + +var defaultEIP150Hash = common.Hash{}.String() + +func TestChainConfigValidate(t *testing.T) { + testCases := []struct { + name string + config ChainConfig + expError bool + }{ + {"default", DefaultChainConfig(), false}, + { + "valid", + ChainConfig{ + HomesteadBlock: sdk.OneInt(), + DAOForkBlock: sdk.OneInt(), + EIP150Block: sdk.OneInt(), + EIP150Hash: defaultEIP150Hash, + EIP155Block: sdk.OneInt(), + EIP158Block: sdk.OneInt(), + ByzantiumBlock: sdk.OneInt(), + ConstantinopleBlock: sdk.OneInt(), + PetersburgBlock: sdk.OneInt(), + IstanbulBlock: sdk.OneInt(), + MuirGlacierBlock: sdk.OneInt(), + YoloV1Block: sdk.OneInt(), + EWASMBlock: sdk.OneInt(), + }, + false, + }, + { + "empty", + ChainConfig{}, + true, + }, + { + "invalid HomesteadBlock", + ChainConfig{ + HomesteadBlock: sdk.Int{}, + }, + true, + }, + { + "invalid DAOForkBlock", + ChainConfig{ + HomesteadBlock: sdk.OneInt(), + DAOForkBlock: sdk.Int{}, + }, + true, + }, + { + "invalid EIP150Block", + ChainConfig{ + HomesteadBlock: sdk.OneInt(), + DAOForkBlock: sdk.OneInt(), + EIP150Block: sdk.Int{}, + }, + true, + }, + { + "invalid EIP150Hash", + ChainConfig{ + HomesteadBlock: sdk.OneInt(), + DAOForkBlock: sdk.OneInt(), + EIP150Block: sdk.OneInt(), + EIP150Hash: " ", + }, + true, + }, + { + "invalid EIP155Block", + ChainConfig{ + HomesteadBlock: sdk.OneInt(), + DAOForkBlock: sdk.OneInt(), + EIP150Block: sdk.OneInt(), + EIP150Hash: defaultEIP150Hash, + EIP155Block: sdk.Int{}, + }, + true, + }, + { + "invalid EIP158Block", + ChainConfig{ + HomesteadBlock: sdk.OneInt(), + DAOForkBlock: sdk.OneInt(), + EIP150Block: sdk.OneInt(), + EIP150Hash: defaultEIP150Hash, + EIP155Block: sdk.OneInt(), + EIP158Block: sdk.Int{}, + }, + true, + }, + { + "invalid ByzantiumBlock", + ChainConfig{ + HomesteadBlock: sdk.OneInt(), + DAOForkBlock: sdk.OneInt(), + EIP150Block: sdk.OneInt(), + EIP150Hash: defaultEIP150Hash, + EIP155Block: sdk.OneInt(), + EIP158Block: sdk.OneInt(), + ByzantiumBlock: sdk.Int{}, + }, + true, + }, + { + "invalid ConstantinopleBlock", + ChainConfig{ + HomesteadBlock: sdk.OneInt(), + DAOForkBlock: sdk.OneInt(), + EIP150Block: sdk.OneInt(), + EIP150Hash: defaultEIP150Hash, + EIP155Block: sdk.OneInt(), + EIP158Block: sdk.OneInt(), + ByzantiumBlock: sdk.OneInt(), + ConstantinopleBlock: sdk.Int{}, + }, + true, + }, + { + "invalid PetersburgBlock", + ChainConfig{ + HomesteadBlock: sdk.OneInt(), + DAOForkBlock: sdk.OneInt(), + EIP150Block: sdk.OneInt(), + EIP150Hash: defaultEIP150Hash, + EIP155Block: sdk.OneInt(), + EIP158Block: sdk.OneInt(), + ByzantiumBlock: sdk.OneInt(), + ConstantinopleBlock: sdk.OneInt(), + PetersburgBlock: sdk.Int{}, + }, + true, + }, + { + "invalid IstanbulBlock", + ChainConfig{ + HomesteadBlock: sdk.OneInt(), + DAOForkBlock: sdk.OneInt(), + EIP150Block: sdk.OneInt(), + EIP150Hash: defaultEIP150Hash, + EIP155Block: sdk.OneInt(), + EIP158Block: sdk.OneInt(), + ByzantiumBlock: sdk.OneInt(), + ConstantinopleBlock: sdk.OneInt(), + PetersburgBlock: sdk.OneInt(), + IstanbulBlock: sdk.Int{}, + }, + true, + }, + { + "invalid MuirGlacierBlock", + ChainConfig{ + HomesteadBlock: sdk.OneInt(), + DAOForkBlock: sdk.OneInt(), + EIP150Block: sdk.OneInt(), + EIP150Hash: defaultEIP150Hash, + EIP155Block: sdk.OneInt(), + EIP158Block: sdk.OneInt(), + ByzantiumBlock: sdk.OneInt(), + ConstantinopleBlock: sdk.OneInt(), + PetersburgBlock: sdk.OneInt(), + IstanbulBlock: sdk.OneInt(), + MuirGlacierBlock: sdk.Int{}, + }, + true, + }, + { + "invalid YoloV1Block", + ChainConfig{ + HomesteadBlock: sdk.OneInt(), + DAOForkBlock: sdk.OneInt(), + EIP150Block: sdk.OneInt(), + EIP150Hash: defaultEIP150Hash, + EIP155Block: sdk.OneInt(), + EIP158Block: sdk.OneInt(), + ByzantiumBlock: sdk.OneInt(), + ConstantinopleBlock: sdk.OneInt(), + PetersburgBlock: sdk.OneInt(), + IstanbulBlock: sdk.OneInt(), + MuirGlacierBlock: sdk.OneInt(), + YoloV1Block: sdk.Int{}, + }, + true, + }, + { + "invalid EWASMBlock", + ChainConfig{ + HomesteadBlock: sdk.OneInt(), + DAOForkBlock: sdk.OneInt(), + EIP150Block: sdk.OneInt(), + EIP150Hash: defaultEIP150Hash, + EIP155Block: sdk.OneInt(), + EIP158Block: sdk.OneInt(), + ByzantiumBlock: sdk.OneInt(), + ConstantinopleBlock: sdk.OneInt(), + PetersburgBlock: sdk.OneInt(), + IstanbulBlock: sdk.OneInt(), + MuirGlacierBlock: sdk.OneInt(), + YoloV1Block: sdk.OneInt(), + EWASMBlock: sdk.Int{}, + }, + true, + }, + } + + for _, tc := range testCases { + err := tc.config.Validate() + + if tc.expError { + require.Error(t, err, tc.name) + } else { + require.NoError(t, err, tc.name) + } + } +} + +func TestChainConfig_String(t *testing.T) { + configStr := `homestead_block: "0" +dao_fork_block: "0" +dao_fork_support: true +eip150_block: "0" +eip150_hash: "0x0000000000000000000000000000000000000000000000000000000000000000" +eip155_block: "0" +eip158_block: "0" +byzantium_block: "0" +constantinople_block: "0" +petersburg_block: "0" +istanbul_block: "-1" +muir_glacier_block: "-1" +yoloV1_block: "-1" +ewasm_block: "-1" +` + require.Equal(t, configStr, DefaultChainConfig().String()) +} diff --git a/x/evm/types/codec.go b/x/evm/types/codec.go index 38e5c4f65d..6ccbd5624f 100644 --- a/x/evm/types/codec.go +++ b/x/evm/types/codec.go @@ -1,19 +1,23 @@ package types -import "github.com/cosmos/cosmos-sdk/codec" +import ( + "github.com/cosmos/cosmos-sdk/codec" +) -var msgCodec = codec.New() +// ModuleCdc defines the evm module's codec +var ModuleCdc = codec.New() -func init() { - cdc := codec.New() - - RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - - msgCodec = cdc.Seal() +// RegisterCodec registers all the necessary types and interfaces for the +// evm module +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(MsgEthereumTx{}, "ethermint/MsgEthereumTx", nil) + cdc.RegisterConcrete(MsgEthermint{}, "ethermint/MsgEthermint", nil) + cdc.RegisterConcrete(TxData{}, "ethermint/TxData", nil) + cdc.RegisterConcrete(ChainConfig{}, "ethermint/ChainConfig", nil) } -// Register concrete types and interfaces on the given codec. -func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(&EthereumTxMsg{}, "ethermint/MsgEthereumTx", nil) +func init() { + RegisterCodec(ModuleCdc) + codec.RegisterCrypto(ModuleCdc) + ModuleCdc.Seal() } diff --git a/x/evm/types/dump.go b/x/evm/types/dump.go deleted file mode 100644 index ed9fd66b3e..0000000000 --- a/x/evm/types/dump.go +++ /dev/null @@ -1,12 +0,0 @@ -package types - -import ( - ethstate "github.com/ethereum/go-ethereum/core/state" -) - -// RawDump returns a raw state dump. -// -// TODO: Implement if we need it, especially for the RPC API. -func (csdb *CommitStateDB) RawDump() ethstate.Dump { - return ethstate.Dump{} -} diff --git a/x/evm/types/errors.go b/x/evm/types/errors.go new file mode 100644 index 0000000000..7754f555f8 --- /dev/null +++ b/x/evm/types/errors.go @@ -0,0 +1,18 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// NOTE: We can't use 1 since that error code is reserved for internal errors. + +var ( + // ErrInvalidState returns an error resulting from an invalid Storage State. + ErrInvalidState = sdkerrors.Register(ModuleName, 2, "invalid storage state") + + // ErrChainConfigNotFound returns an error if the chain config cannot be found on the store. + ErrChainConfigNotFound = sdkerrors.Register(ModuleName, 3, "chain configuration not found") + + // ErrInvalidChainConfig returns an error resulting from an invalid ChainConfig. + ErrInvalidChainConfig = sdkerrors.Register(ModuleName, 4, "invalid chain configuration") +) diff --git a/x/evm/types/events.go b/x/evm/types/events.go new file mode 100644 index 0000000000..e5dca425a4 --- /dev/null +++ b/x/evm/types/events.go @@ -0,0 +1,11 @@ +package types + +// Evm module events +const ( + EventTypeEthermint = TypeMsgEthermint + EventTypeEthereumTx = TypeMsgEthereumTx + + AttributeKeyContractAddress = "contract" + AttributeKeyRecipient = "recipient" + AttributeValueCategory = ModuleName +) diff --git a/x/evm/types/expected_keepers.go b/x/evm/types/expected_keepers.go new file mode 100644 index 0000000000..8e0afc260e --- /dev/null +++ b/x/evm/types/expected_keepers.go @@ -0,0 +1,15 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" +) + +// AccountKeeper defines the expected account keeper interface +type AccountKeeper interface { + NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) authexported.Account + GetAllAccounts(ctx sdk.Context) (accounts []authexported.Account) + GetAccount(ctx sdk.Context, addr sdk.AccAddress) authexported.Account + SetAccount(ctx sdk.Context, account authexported.Account) + RemoveAccount(ctx sdk.Context, account authexported.Account) +} diff --git a/x/evm/types/genesis.go b/x/evm/types/genesis.go new file mode 100644 index 0000000000..82db992d33 --- /dev/null +++ b/x/evm/types/genesis.go @@ -0,0 +1,93 @@ +package types + +import ( + "bytes" + "errors" + "fmt" + "math/big" + + ethcmn "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +type ( + // GenesisState defines the evm module genesis state + GenesisState struct { + Accounts []GenesisAccount `json:"accounts"` + TxsLogs []TransactionLogs `json:"txs_logs"` + ChainConfig ChainConfig `json:"chain_config"` + Params Params `json:"params"` + } + + // GenesisAccount defines an account to be initialized in the genesis state. + // Its main difference between with Geth's GenesisAccount is that it uses a custom + // storage type and that it doesn't contain the private key field. + GenesisAccount struct { + Address ethcmn.Address `json:"address"` + Balance *big.Int `json:"balance"` + Code hexutil.Bytes `json:"code,omitempty"` + Storage Storage `json:"storage,omitempty"` + } +) + +// Validate performs a basic validation of a GenesisAccount fields. +func (ga GenesisAccount) Validate() error { + if bytes.Equal(ga.Address.Bytes(), ethcmn.Address{}.Bytes()) { + return fmt.Errorf("address cannot be the zero address %s", ga.Address.String()) + } + if ga.Balance == nil { + return errors.New("balance cannot be nil") + } + if ga.Balance.Sign() == -1 { + return errors.New("balance cannot be negative") + } + if ga.Code != nil && len(ga.Code) == 0 { + return errors.New("code bytes cannot be empty") + } + + return ga.Storage.Validate() +} + +// DefaultGenesisState sets default evm genesis state with empty accounts and default params and +// chain config values. +func DefaultGenesisState() GenesisState { + return GenesisState{ + Accounts: []GenesisAccount{}, + TxsLogs: []TransactionLogs{}, + ChainConfig: DefaultChainConfig(), + Params: DefaultParams(), + } +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + seenAccounts := make(map[string]bool) + seenTxs := make(map[string]bool) + for _, acc := range gs.Accounts { + if seenAccounts[acc.Address.String()] { + return fmt.Errorf("duplicated genesis account %s", acc.Address.String()) + } + if err := acc.Validate(); err != nil { + return fmt.Errorf("invalid genesis account %s: %w", acc.Address.String(), err) + } + seenAccounts[acc.Address.String()] = true + } + for _, tx := range gs.TxsLogs { + if seenTxs[tx.Hash.String()] { + return fmt.Errorf("duplicated logs from transaction %s", tx.Hash.String()) + } + + if err := tx.Validate(); err != nil { + return fmt.Errorf("invalid logs from transaction %s: %w", tx.Hash.String(), err) + } + + seenTxs[tx.Hash.String()] = true + } + + if err := gs.ChainConfig.Validate(); err != nil { + return err + } + + return gs.Params.Validate() +} diff --git a/x/evm/types/genesis_test.go b/x/evm/types/genesis_test.go new file mode 100644 index 0000000000..8f499aab03 --- /dev/null +++ b/x/evm/types/genesis_test.go @@ -0,0 +1,264 @@ +package types + +import ( + "math/big" + "testing" + + "github.com/cosmos/ethermint/crypto" + "github.com/stretchr/testify/require" + + ethcmn "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + ethcrypto "github.com/ethereum/go-ethereum/crypto" +) + +func TestValidateGenesisAccount(t *testing.T) { + testCases := []struct { + name string + genesisAccount GenesisAccount + expPass bool + }{ + { + "valid genesis account", + GenesisAccount{ + Address: ethcmn.BytesToAddress([]byte{1, 2, 3, 4, 5}), + Balance: big.NewInt(1), + Code: []byte{1, 2, 3}, + Storage: Storage{ + NewState(ethcmn.BytesToHash([]byte{1, 2, 3}), ethcmn.BytesToHash([]byte{1, 2, 3})), + }, + }, + true, + }, + { + "empty account address bytes", + GenesisAccount{ + Address: ethcmn.Address{}, + Balance: big.NewInt(1), + }, + false, + }, + { + "nil account balance", + GenesisAccount{ + Address: ethcmn.BytesToAddress([]byte{1, 2, 3, 4, 5}), + Balance: nil, + }, + false, + }, + { + "nil account balance", + GenesisAccount{ + Address: ethcmn.BytesToAddress([]byte{1, 2, 3, 4, 5}), + Balance: big.NewInt(-1), + }, + false, + }, + { + "empty code bytes", + GenesisAccount{ + Address: ethcmn.BytesToAddress([]byte{1, 2, 3, 4, 5}), + Balance: big.NewInt(1), + Code: []byte{}, + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + err := tc.genesisAccount.Validate() + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} + +func TestValidateGenesis(t *testing.T) { + priv, err := crypto.GenerateKey() + require.NoError(t, err) + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + testCases := []struct { + name string + genState GenesisState + expPass bool + }{ + { + name: "default", + genState: DefaultGenesisState(), + expPass: true, + }, + { + name: "valid genesis", + genState: GenesisState{ + Accounts: []GenesisAccount{ + { + Address: ethcmn.BytesToAddress([]byte{1, 2, 3, 4, 5}), + Balance: big.NewInt(1), + Code: []byte{1, 2, 3}, + Storage: Storage{ + {Key: ethcmn.BytesToHash([]byte{1, 2, 3})}, + }, + }, + }, + TxsLogs: []TransactionLogs{ + { + Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Logs: []*ethtypes.Log{ + { + Address: addr, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxIndex: 1, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + Index: 1, + Removed: false, + }, + }, + }, + }, + ChainConfig: DefaultChainConfig(), + Params: DefaultParams(), + }, + expPass: true, + }, + { + name: "empty genesis", + genState: GenesisState{}, + expPass: false, + }, + { + name: "invalid genesis", + genState: GenesisState{ + Accounts: []GenesisAccount{ + { + Address: ethcmn.Address{}, + }, + }, + }, + expPass: false, + }, + { + name: "duplicated genesis account", + genState: GenesisState{ + Accounts: []GenesisAccount{ + { + Address: ethcmn.BytesToAddress([]byte{1, 2, 3, 4, 5}), + Balance: big.NewInt(1), + Code: []byte{1, 2, 3}, + Storage: Storage{ + NewState(ethcmn.BytesToHash([]byte{1, 2, 3}), ethcmn.BytesToHash([]byte{1, 2, 3})), + }, + }, + { + Address: ethcmn.BytesToAddress([]byte{1, 2, 3, 4, 5}), + Balance: big.NewInt(1), + Code: []byte{1, 2, 3}, + Storage: Storage{ + NewState(ethcmn.BytesToHash([]byte{1, 2, 3}), ethcmn.BytesToHash([]byte{1, 2, 3})), + }, + }, + }, + }, + expPass: false, + }, + { + name: "duplicated tx log", + genState: GenesisState{ + Accounts: []GenesisAccount{ + { + Address: ethcmn.BytesToAddress([]byte{1, 2, 3, 4, 5}), + Balance: big.NewInt(1), + Code: []byte{1, 2, 3}, + Storage: Storage{ + {Key: ethcmn.BytesToHash([]byte{1, 2, 3})}, + }, + }, + }, + TxsLogs: []TransactionLogs{ + { + Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Logs: []*ethtypes.Log{ + { + Address: addr, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxIndex: 1, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + Index: 1, + Removed: false, + }, + }, + }, + { + Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Logs: []*ethtypes.Log{ + { + Address: addr, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxIndex: 1, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + Index: 1, + Removed: false, + }, + }, + }, + }, + }, + expPass: false, + }, + { + name: "invalid tx log", + genState: GenesisState{ + Accounts: []GenesisAccount{ + { + Address: ethcmn.BytesToAddress([]byte{1, 2, 3, 4, 5}), + Balance: big.NewInt(1), + Code: []byte{1, 2, 3}, + Storage: Storage{ + {Key: ethcmn.BytesToHash([]byte{1, 2, 3})}, + }, + }, + }, + TxsLogs: []TransactionLogs{NewTransactionLogs(ethcmn.Hash{}, nil)}, + }, + expPass: false, + }, + { + name: "invalid params", + genState: GenesisState{ + ChainConfig: DefaultChainConfig(), + Params: Params{}, + }, + expPass: false, + }, + { + name: "invalid chain config", + genState: GenesisState{ + ChainConfig: ChainConfig{}, + Params: DefaultParams(), + }, + expPass: false, + }, + } + + for _, tc := range testCases { + tc := tc + err := tc.genState.Validate() + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} diff --git a/x/evm/types/journal.go b/x/evm/types/journal.go index eea5db8960..b2190759df 100644 --- a/x/evm/types/journal.go +++ b/x/evm/types/journal.go @@ -22,14 +22,24 @@ type journalEntry interface { // commit. These are tracked to be able to be reverted in case of an execution // exception or revertal request. type journal struct { - entries []journalEntry // Current changes tracked by the journal - dirties map[ethcmn.Address]int // Dirty accounts and the number of changes + entries []journalEntry // Current changes tracked by the journal + dirties []dirty // Dirty accounts and the number of changes + addressToJournalIndex map[ethcmn.Address]int // map from address to the index of the dirties slice +} + +// dirty represents a single key value pair of the journal dirties, where the +// key correspons to the account address and the value to the number of +// changes for that account. +type dirty struct { + address ethcmn.Address + changes int } // newJournal create a new initialized journal. func newJournal() *journal { return &journal{ - dirties: make(map[ethcmn.Address]int), + dirties: []dirty{}, + addressToJournalIndex: make(map[ethcmn.Address]int), } } @@ -37,7 +47,7 @@ func newJournal() *journal { func (j *journal) append(entry journalEntry) { j.entries = append(j.entries, entry) if addr := entry.dirtied(); addr != nil { - j.dirties[*addr]++ + j.addDirty(*addr) } } @@ -50,8 +60,9 @@ func (j *journal) revert(statedb *CommitStateDB, snapshot int) { // Drop any dirty tracking induced by the change if addr := j.entries[i].dirtied(); addr != nil { - if j.dirties[*addr]--; j.dirties[*addr] == 0 { - delete(j.dirties, *addr) + j.substractDirty(*addr) + if j.getDirty(*addr) == 0 { + j.deleteDirty(*addr) } } } @@ -62,7 +73,7 @@ func (j *journal) revert(statedb *CommitStateDB, snapshot int) { // otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD // precompile consensus exception. func (j *journal) dirty(addr ethcmn.Address) { - j.dirties[addr]++ + j.addDirty(addr) } // length returns the current number of entries in the journal. @@ -70,6 +81,56 @@ func (j *journal) length() int { return len(j.entries) } +// getDirty returns the dirty count for a given address. If the address is not +// found it returns 0. +func (j *journal) getDirty(addr ethcmn.Address) int { + idx, found := j.addressToJournalIndex[addr] + if !found { + return 0 + } + + return j.dirties[idx].changes +} + +// addDirty adds 1 to the dirty count of an address. If the dirty entry is not +// found it creates it. +func (j *journal) addDirty(addr ethcmn.Address) { + idx, found := j.addressToJournalIndex[addr] + if !found { + j.dirties = append(j.dirties, dirty{address: addr, changes: 0}) + idx = len(j.dirties) - 1 + j.addressToJournalIndex[addr] = idx + } + + j.dirties[idx].changes++ +} + +// substractDirty subtracts 1 to the dirty count of an address. It performs a +// no-op if the address is not found. +func (j *journal) substractDirty(addr ethcmn.Address) { + idx, found := j.addressToJournalIndex[addr] + if !found { + return + } + + if j.dirties[idx].changes == 0 { + return + } + j.dirties[idx].changes-- +} + +// deleteDirty deletes a dirty entry from the jounal's dirties slice. If the +// entry is not found it performs a no-op. +func (j *journal) deleteDirty(addr ethcmn.Address) { + idx, found := j.addressToJournalIndex[addr] + if !found { + return + } + + j.dirties = append(j.dirties[:idx], j.dirties[idx+1:]...) + delete(j.addressToJournalIndex, addr) +} + type ( // Changes to the account trie. createObjectChange struct { @@ -121,15 +182,38 @@ type ( } touchChange struct { - account *ethcmn.Address - prev bool - prevDirty bool + account *ethcmn.Address + // prev bool + // prevDirty bool } ) func (ch createObjectChange) revert(s *CommitStateDB) { - delete(s.stateObjects, *ch.account) delete(s.stateObjectsDirty, *ch.account) + idx, exists := s.addressToObjectIndex[*ch.account] + if !exists { + // perform no-op + return + } + + // remove from the slice + delete(s.addressToObjectIndex, *ch.account) + + // if the slice contains one element, delete it + if len(s.stateObjects) == 1 { + s.stateObjects = []stateEntry{} + return + } + + // move the elements one position left on the array + for i := idx + 1; i < len(s.stateObjects); i++ { + s.stateObjects[i-1] = s.stateObjects[i] + // the new index is i - 1 + s.addressToObjectIndex[s.stateObjects[i].address] = i - 1 + } + + // finally, delete the last element of the slice to account for the removed object + s.stateObjects = s.stateObjects[:len(s.stateObjects)-1] } func (ch createObjectChange) dirtied() *ethcmn.Address { @@ -148,7 +232,8 @@ func (ch suicideChange) revert(s *CommitStateDB) { so := s.getStateObject(*ch.account) if so != nil { so.suicided = ch.prev - so.setBalance(ch.prevBalance) + evmDenom := s.GetParams().EvmDenom + so.setBalance(evmDenom, ch.prevBalance) } } @@ -164,7 +249,8 @@ func (ch touchChange) dirtied() *ethcmn.Address { } func (ch balanceChange) revert(s *CommitStateDB) { - s.getStateObject(*ch.account).setBalance(ch.prev) + evmDenom := s.GetParams().EvmDenom + s.getStateObject(*ch.account).setBalance(evmDenom, ch.prev) } func (ch balanceChange) dirtied() *ethcmn.Address { @@ -204,11 +290,18 @@ func (ch refundChange) dirtied() *ethcmn.Address { } func (ch addLogChange) revert(s *CommitStateDB) { - logs := s.logs[ch.txhash] - if len(logs) == 1 { - delete(s.logs, ch.txhash) - } else { - s.logs[ch.txhash] = logs[:len(logs)-1] + logs, err := s.GetLogs(ch.txhash) + if err != nil { + // panic on unmarshal error + panic(err) + } + + // delete logs if entry is empty or has only one item + if len(logs) <= 1 { + s.DeleteLogs(ch.txhash) + } else if err := s.SetLogs(ch.txhash, logs[:len(logs)-1]); err != nil { + // panic on marshal error + panic(err) } s.logSize-- @@ -219,7 +312,31 @@ func (ch addLogChange) dirtied() *ethcmn.Address { } func (ch addPreimageChange) revert(s *CommitStateDB) { - delete(s.preimages, ch.hash) + idx, exists := s.hashToPreimageIndex[ch.hash] + if !exists { + // perform no-op + return + } + + // remove from the slice + delete(s.hashToPreimageIndex, ch.hash) + + // if the slice contains one element, delete it + if len(s.preimages) == 1 { + s.preimages = []preimageEntry{} + return + } + + // move the elements one position left on the array + for i := idx + 1; i < len(s.preimages); i++ { + s.preimages[i-1] = s.preimages[i] + // the new index is i - 1 + s.hashToPreimageIndex[s.preimages[i].hash] = i - 1 + } + + // finally, delete the last element + + s.preimages = s.preimages[:len(s.preimages)-1] } func (ch addPreimageChange) dirtied() *ethcmn.Address { diff --git a/x/evm/types/journal_test.go b/x/evm/types/journal_test.go new file mode 100644 index 0000000000..ee54fe91bc --- /dev/null +++ b/x/evm/types/journal_test.go @@ -0,0 +1,335 @@ +package types + +import ( + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/suite" + + abci "github.com/tendermint/tendermint/abci/types" + tmlog "github.com/tendermint/tendermint/libs/log" + tmdb "github.com/tendermint/tm-db" + + sdkcodec "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/params" + + ethcmn "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + + "github.com/cosmos/ethermint/crypto" + ethermint "github.com/cosmos/ethermint/types" +) + +type JournalTestSuite struct { + suite.Suite + + address ethcmn.Address + journal *journal + ctx sdk.Context + stateDB *CommitStateDB +} + +func newTestCodec() *sdkcodec.Codec { + cdc := sdkcodec.New() + + RegisterCodec(cdc) + sdk.RegisterCodec(cdc) + crypto.RegisterCodec(cdc) + sdkcodec.RegisterCrypto(cdc) + auth.RegisterCodec(cdc) + ethermint.RegisterCodec(cdc) + + return cdc +} + +func (suite *JournalTestSuite) SetupTest() { + suite.setup() + + privkey, err := crypto.GenerateKey() + suite.Require().NoError(err) + + suite.address = ethcmn.BytesToAddress(privkey.PubKey().Address().Bytes()) + suite.journal = newJournal() + + balance := sdk.NewCoins(ethermint.NewPhotonCoin(sdk.NewInt(100))) + acc := ðermint.EthAccount{ + BaseAccount: auth.NewBaseAccount(sdk.AccAddress(suite.address.Bytes()), balance, nil, 0, 0), + CodeHash: ethcrypto.Keccak256(nil), + } + + suite.stateDB.accountKeeper.SetAccount(suite.ctx, acc) + // suite.stateDB.bankKeeper.SetBalance(suite.ctx, sdk.AccAddress(suite.address.Bytes()), balance) + suite.stateDB.SetLogs(ethcmn.BytesToHash([]byte("txhash")), []*ethtypes.Log{ + { + Address: suite.address, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic_0"))}, + Data: []byte("data_0"), + BlockNumber: 1, + TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxIndex: 1, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + Index: 1, + Removed: false, + }, + { + Address: suite.address, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic_1"))}, + Data: []byte("data_1"), + BlockNumber: 10, + TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxIndex: 0, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + Index: 0, + Removed: false, + }, + }) +} + +// setup performs a manual setup of the GoLevelDB and mounts the required IAVL stores. We use the manual +// setup here instead of the Ethermint app test setup because the journal methods are private and using +// the latter would result in a cycle dependency. We also want to avoid declaring the journal methods public +// to maintain consistency with the Geth implementation. +func (suite *JournalTestSuite) setup() { + authKey := sdk.NewKVStoreKey(auth.StoreKey) + paramsKey := sdk.NewKVStoreKey(params.StoreKey) + paramsTKey := sdk.NewTransientStoreKey(params.TStoreKey) + // bankKey := sdk.NewKVStoreKey(bank.StoreKey) + storeKey := sdk.NewKVStoreKey(StoreKey) + + db := tmdb.NewDB("state", tmdb.GoLevelDBBackend, "temp") + defer func() { + os.RemoveAll("temp") + }() + + cms := store.NewCommitMultiStore(db) + cms.MountStoreWithDB(authKey, sdk.StoreTypeIAVL, db) + cms.MountStoreWithDB(paramsKey, sdk.StoreTypeIAVL, db) + cms.MountStoreWithDB(storeKey, sdk.StoreTypeIAVL, db) + cms.MountStoreWithDB(paramsTKey, sdk.StoreTypeTransient, db) + + err := cms.LoadLatestVersion() + suite.Require().NoError(err) + + cdc := newTestCodec() + + paramsKeeper := params.NewKeeper(cdc, paramsKey, paramsTKey) + + authSubspace := paramsKeeper.Subspace(auth.DefaultParamspace) + evmSubspace := paramsKeeper.Subspace(types.DefaultParamspace).WithKeyTable(ParamKeyTable()) + + ak := auth.NewAccountKeeper(cdc, authKey, authSubspace, ethermint.ProtoAccount) + suite.ctx = sdk.NewContext(cms, abci.Header{ChainID: "8"}, false, tmlog.NewNopLogger()) + suite.stateDB = NewCommitStateDB(suite.ctx, storeKey, evmSubspace, ak).WithContext(suite.ctx) + suite.stateDB.SetParams(DefaultParams()) +} + +func TestJournalTestSuite(t *testing.T) { + suite.Run(t, new(JournalTestSuite)) +} + +func (suite *JournalTestSuite) TestJournal_append_revert() { + testCases := []struct { + name string + entry journalEntry + }{ + { + "createObjectChange", + createObjectChange{ + account: &suite.address, + }, + }, + { + "resetObjectChange", + resetObjectChange{ + prev: &stateObject{ + address: suite.address, + }, + }, + }, + { + "suicideChange", + suicideChange{ + account: &suite.address, + prev: false, + prevBalance: sdk.OneInt(), + }, + }, + { + "balanceChange", + balanceChange{ + account: &suite.address, + prev: sdk.OneInt(), + }, + }, + { + "nonceChange", + nonceChange{ + account: &suite.address, + prev: 1, + }, + }, + { + "storageChange", + storageChange{ + account: &suite.address, + key: ethcmn.BytesToHash([]byte("key")), + prevValue: ethcmn.BytesToHash([]byte("value")), + }, + }, + { + "codeChange", + codeChange{ + account: &suite.address, + prevCode: []byte("code"), + prevHash: []byte("hash"), + }, + }, + { + "touchChange", + touchChange{ + account: &suite.address, + }, + }, + { + "refundChange", + refundChange{ + prev: 1, + }, + }, + { + "addPreimageChange", + addPreimageChange{ + hash: ethcmn.BytesToHash([]byte("hash")), + }, + }, + { + "addLogChange", + addLogChange{ + txhash: ethcmn.BytesToHash([]byte("hash")), + }, + }, + { + "addLogChange - 2 logs", + addLogChange{ + txhash: ethcmn.BytesToHash([]byte("txhash")), + }, + }, + } + var dirtyCount int + for i, tc := range testCases { + suite.journal.append(tc.entry) + suite.Require().Equal(suite.journal.length(), i+1, tc.name) + if tc.entry.dirtied() != nil { + dirtyCount++ + + suite.Require().Equal(dirtyCount, suite.journal.getDirty(suite.address), tc.name) + } + } + + // revert to the initial journal state + suite.journal.revert(suite.stateDB, 0) + + // verify the dirty entry has been deleted + idx, ok := suite.journal.addressToJournalIndex[suite.address] + suite.Require().False(ok) + suite.Require().Zero(idx) +} + +func (suite *JournalTestSuite) TestJournal_preimage_revert() { + suite.stateDB.preimages = []preimageEntry{ + { + hash: ethcmn.BytesToHash([]byte("hash")), + preimage: []byte("preimage0"), + }, + { + hash: ethcmn.BytesToHash([]byte("hash1")), + preimage: []byte("preimage1"), + }, + { + hash: ethcmn.BytesToHash([]byte("hash2")), + preimage: []byte("preimage2"), + }, + } + + for i, preimage := range suite.stateDB.preimages { + suite.stateDB.hashToPreimageIndex[preimage.hash] = i + } + + change := addPreimageChange{ + hash: ethcmn.BytesToHash([]byte("hash")), + } + + // delete first entry + change.revert(suite.stateDB) + suite.Require().Len(suite.stateDB.preimages, 2) + suite.Require().Equal(len(suite.stateDB.preimages), len(suite.stateDB.hashToPreimageIndex)) + + for i, entry := range suite.stateDB.preimages { + suite.Require().Equal(fmt.Sprintf("preimage%d", i+1), string(entry.preimage), entry.hash.String()) + idx, found := suite.stateDB.hashToPreimageIndex[entry.hash] + suite.Require().True(found) + suite.Require().Equal(i, idx) + } +} + +func (suite *JournalTestSuite) TestJournal_createObjectChange_revert() { + addr := ethcmn.BytesToAddress([]byte("addr")) + + suite.stateDB.stateObjects = []stateEntry{ + { + address: addr, + stateObject: &stateObject{ + address: addr, + }, + }, + { + address: ethcmn.BytesToAddress([]byte("addr1")), + stateObject: &stateObject{ + address: ethcmn.BytesToAddress([]byte("addr1")), + }, + }, + { + address: ethcmn.BytesToAddress([]byte("addr2")), + stateObject: &stateObject{ + address: ethcmn.BytesToAddress([]byte("addr2")), + }, + }, + } + + for i, so := range suite.stateDB.stateObjects { + suite.stateDB.addressToObjectIndex[so.address] = i + } + + change := createObjectChange{ + account: &addr, + } + + // delete first entry + change.revert(suite.stateDB) + suite.Require().Len(suite.stateDB.stateObjects, 2) + suite.Require().Equal(len(suite.stateDB.stateObjects), len(suite.stateDB.addressToObjectIndex)) + + for i, entry := range suite.stateDB.stateObjects { + suite.Require().Equal(ethcmn.BytesToAddress([]byte(fmt.Sprintf("addr%d", i+1))).String(), entry.address.String()) + idx, found := suite.stateDB.addressToObjectIndex[entry.address] + suite.Require().True(found) + suite.Require().Equal(i, idx) + } +} + +func (suite *JournalTestSuite) TestJournal_dirty() { + // dirty entry hasn't been set + idx, ok := suite.journal.addressToJournalIndex[suite.address] + suite.Require().False(ok) + suite.Require().Zero(idx) + + // update dirty count + suite.journal.dirty(suite.address) + suite.Require().Equal(1, suite.journal.getDirty(suite.address)) +} diff --git a/x/evm/types/key.go b/x/evm/types/key.go new file mode 100644 index 0000000000..a5e7d665a6 --- /dev/null +++ b/x/evm/types/key.go @@ -0,0 +1,40 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + ethcmn "github.com/ethereum/go-ethereum/common" +) + +const ( + // ModuleName string name of module + ModuleName = "evm" + + // StoreKey key for ethereum storage data, account code (StateDB) or block + // related data for Web3. + // The EVM module should use a prefix store. + StoreKey = ModuleName + + // RouterKey uses module name for routing + RouterKey = ModuleName +) + +// KVStore key prefixes +var ( + KeyPrefixBlockHash = []byte{0x01} + KeyPrefixBloom = []byte{0x02} + KeyPrefixLogs = []byte{0x03} + KeyPrefixCode = []byte{0x04} + KeyPrefixStorage = []byte{0x05} + KeyPrefixChainConfig = []byte{0x06} +) + +// BloomKey defines the store key for a block Bloom +func BloomKey(height int64) []byte { + return sdk.Uint64ToBigEndian(uint64(height)) +} + +// AddressStoragePrefix returns a prefix to iterate over a given account storage. +func AddressStoragePrefix(address ethcmn.Address) []byte { + return append(KeyPrefixStorage, address.Bytes()...) +} diff --git a/x/evm/types/logs.go b/x/evm/types/logs.go new file mode 100644 index 0000000000..9694ddbedb --- /dev/null +++ b/x/evm/types/logs.go @@ -0,0 +1,75 @@ +package types + +import ( + "bytes" + "errors" + "fmt" + + ethcmn "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" +) + +// TransactionLogs define the logs generated from a transaction execution +// with a given hash. It it used for import/export data as transactions are not persisted +// on blockchain state after an upgrade. +type TransactionLogs struct { + Hash ethcmn.Hash `json:"hash"` + Logs []*ethtypes.Log `json:"logs"` +} + +// NewTransactionLogs creates a new NewTransactionLogs instance. +func NewTransactionLogs(hash ethcmn.Hash, logs []*ethtypes.Log) TransactionLogs { + return TransactionLogs{ + Hash: hash, + Logs: logs, + } +} + +// MarshalLogs encodes an array of logs using amino +func MarshalLogs(logs []*ethtypes.Log) ([]byte, error) { + return ModuleCdc.MarshalBinaryLengthPrefixed(logs) +} + +// UnmarshalLogs decodes an amino-encoded byte array into an array of logs +func UnmarshalLogs(in []byte) ([]*ethtypes.Log, error) { + logs := []*ethtypes.Log{} + err := ModuleCdc.UnmarshalBinaryLengthPrefixed(in, &logs) + return logs, err +} + +// Validate performs a basic validation of a GenesisAccount fields. +func (tx TransactionLogs) Validate() error { + if bytes.Equal(tx.Hash.Bytes(), ethcmn.Hash{}.Bytes()) { + return fmt.Errorf("hash cannot be the empty %s", tx.Hash.String()) + } + + for i, log := range tx.Logs { + if err := ValidateLog(log); err != nil { + return fmt.Errorf("invalid log %d: %w", i, err) + } + if !bytes.Equal(log.TxHash.Bytes(), tx.Hash.Bytes()) { + return fmt.Errorf("log tx hash mismatch (%s ≠ %s)", log.TxHash.String(), tx.Hash.String()) + } + } + return nil +} + +// ValidateLog performs a basic validation of an ethereum Log fields. +func ValidateLog(log *ethtypes.Log) error { + if log == nil { + return errors.New("log cannot be nil") + } + if bytes.Equal(log.Address.Bytes(), ethcmn.Address{}.Bytes()) { + return fmt.Errorf("log address cannot be empty %s", log.Address.String()) + } + if bytes.Equal(log.BlockHash.Bytes(), ethcmn.Hash{}.Bytes()) { + return fmt.Errorf("block hash cannot be the empty %s", log.BlockHash.String()) + } + if log.BlockNumber == 0 { + return errors.New("block number cannot be zero") + } + if bytes.Equal(log.TxHash.Bytes(), ethcmn.Hash{}.Bytes()) { + return fmt.Errorf("tx hash cannot be the empty %s", log.TxHash.String()) + } + return nil +} diff --git a/x/evm/types/logs_test.go b/x/evm/types/logs_test.go new file mode 100644 index 0000000000..4665d82cf1 --- /dev/null +++ b/x/evm/types/logs_test.go @@ -0,0 +1,166 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + ethcmn "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + + "github.com/cosmos/ethermint/crypto" +) + +func TestTransactionLogsValidate(t *testing.T) { + priv, err := crypto.GenerateKey() + require.NoError(t, err) + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + testCases := []struct { + name string + txLogs TransactionLogs + expPass bool + }{ + { + "valid log", + TransactionLogs{ + Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Logs: []*ethtypes.Log{ + { + Address: addr, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxIndex: 1, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + Index: 1, + Removed: false, + }, + }, + }, + true, + }, + { + "empty hash", + TransactionLogs{ + Hash: ethcmn.Hash{}, + }, + false, + }, + { + "invalid log", + TransactionLogs{ + Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Logs: []*ethtypes.Log{nil}, + }, + false, + }, + { + "hash mismatch log", + TransactionLogs{ + Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Logs: []*ethtypes.Log{ + { + Address: addr, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.BytesToHash([]byte("other_hash")), + TxIndex: 1, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + Index: 1, + Removed: false, + }, + }, + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + err := tc.txLogs.Validate() + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} + +func TestValidateLog(t *testing.T) { + priv, err := crypto.GenerateKey() + require.NoError(t, err) + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + testCases := []struct { + name string + log *ethtypes.Log + expPass bool + }{ + { + "valid log", + ðtypes.Log{ + Address: addr, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxIndex: 1, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + Index: 1, + Removed: false, + }, + true, + }, + { + "nil log", nil, false, + }, + { + "zero address", + ðtypes.Log{ + Address: ethcmn.Address{}, + }, + false, + }, + { + "empty block hash", + ðtypes.Log{ + Address: addr, + BlockHash: ethcmn.Hash{}, + }, + false, + }, + { + "zero block number", + ðtypes.Log{ + Address: addr, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + BlockNumber: 0, + }, + false, + }, + { + "empty tx hash", + ðtypes.Log{ + Address: addr, + BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + BlockNumber: 1, + TxHash: ethcmn.Hash{}, + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + err := ValidateLog(tc.log) + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} diff --git a/x/evm/types/msg.go b/x/evm/types/msg.go index 013f6def45..a35d63d0c7 100644 --- a/x/evm/types/msg.go +++ b/x/evm/types/msg.go @@ -8,10 +8,11 @@ import ( "math/big" "sync/atomic" - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/ethermint/types" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + ethcmn "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" ethcrypto "github.com/ethereum/go-ethereum/crypto" @@ -19,79 +20,136 @@ import ( ) var ( - _ sdk.Msg = EthereumTxMsg{} - _ sdk.Tx = EthereumTxMsg{} + _ sdk.Msg = MsgEthermint{} + _ sdk.Msg = MsgEthereumTx{} + _ sdk.Tx = MsgEthereumTx{} ) var big8 = big.NewInt(8) // message type and route constants const ( - TypeEthereumTxMsg = "ethereum_tx" - RouteEthereumTxMsg = "evm" + // TypeMsgEthereumTx defines the type string of an Ethereum tranasction + TypeMsgEthereumTx = "ethereum" + // TypeMsgEthermint defines the type string of Ethermint message + TypeMsgEthermint = "ethermint" ) -// EthereumTxMsg encapsulates an Ethereum transaction as an SDK message. -type ( - EthereumTxMsg struct { - Data TxData +// MsgEthermint implements a cosmos equivalent structure for Ethereum transactions +type MsgEthermint struct { + AccountNonce uint64 `json:"nonce"` + Price sdk.Int `json:"gasPrice"` + GasLimit uint64 `json:"gas"` + Recipient *sdk.AccAddress `json:"to" rlp:"nil"` // nil means contract creation + Amount sdk.Int `json:"value"` + Payload []byte `json:"input"` + + // From address (formerly derived from signature) + From sdk.AccAddress `json:"from"` +} - // caches - hash atomic.Value - size atomic.Value - from atomic.Value +// NewMsgEthermint returns a reference to a new Ethermint transaction +func NewMsgEthermint( + nonce uint64, to *sdk.AccAddress, amount sdk.Int, + gasLimit uint64, gasPrice sdk.Int, payload []byte, from sdk.AccAddress, +) MsgEthermint { + return MsgEthermint{ + AccountNonce: nonce, + Price: gasPrice, + GasLimit: gasLimit, + Recipient: to, + Amount: amount, + Payload: payload, + From: from, } +} + +func (msg MsgEthermint) String() string { + return fmt.Sprintf("nonce=%d gasPrice=%d gasLimit=%d recipient=%s amount=%d data=0x%x from=%s", + msg.AccountNonce, msg.Price, msg.GasLimit, msg.Recipient, msg.Amount, msg.Payload, msg.From) +} - // TxData implements the Ethereum transaction data structure. It is used - // solely as intended in Ethereum abiding by the protocol. - TxData struct { - AccountNonce uint64 `json:"nonce"` - Price *big.Int `json:"gasPrice"` - GasLimit uint64 `json:"gas"` - Recipient *ethcmn.Address `json:"to" rlp:"nil"` // nil means contract creation - Amount *big.Int `json:"value"` - Payload []byte `json:"input"` - - // signature values - V *big.Int `json:"v"` - R *big.Int `json:"r"` - S *big.Int `json:"s"` - - // hash is only used when marshaling to JSON - Hash *ethcmn.Hash `json:"hash" rlp:"-"` +// Route should return the name of the module +func (msg MsgEthermint) Route() string { return RouterKey } + +// Type returns the action of the message +func (msg MsgEthermint) Type() string { return TypeMsgEthermint } + +// GetSignBytes encodes the message for signing +func (msg MsgEthermint) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) +} + +// ValidateBasic runs stateless checks on the message +func (msg MsgEthermint) ValidateBasic() error { + if msg.Price.IsZero() { + return sdkerrors.Wrapf(types.ErrInvalidValue, "gas price cannot be 0") } - // sigCache is used to cache the derived sender and contains the signer used - // to derive it. - sigCache struct { - signer ethtypes.Signer - from ethcmn.Address + if msg.Price.Sign() == -1 { + return sdkerrors.Wrapf(types.ErrInvalidValue, "gas price cannot be negative %s", msg.Price) } -) -// NewEthereumTxMsg returns a reference to a new Ethereum transaction message. -func NewEthereumTxMsg( - nonce uint64, to ethcmn.Address, amount *big.Int, - gasLimit uint64, gasPrice *big.Int, payload []byte, -) *EthereumTxMsg { + // Amount can be 0 + if msg.Amount.Sign() == -1 { + return sdkerrors.Wrapf(types.ErrInvalidValue, "amount cannot be negative %s", msg.Amount) + } - return newEthereumTxMsg(nonce, &to, amount, gasLimit, gasPrice, payload) + return nil } -// NewEthereumTxMsgContract returns a reference to a new Ethereum transaction -// message designated for contract creation. -func NewEthereumTxMsgContract( - nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, payload []byte, -) *EthereumTxMsg { +// GetSigners defines whose signature is required +func (msg MsgEthermint) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.From} +} + +// To returns the recipient address of the transaction. It returns nil if the +// transaction is a contract creation. +func (msg MsgEthermint) To() *ethcmn.Address { + if msg.Recipient == nil { + return nil + } + + addr := ethcmn.BytesToAddress(msg.Recipient.Bytes()) + return &addr +} + +// MsgEthereumTx encapsulates an Ethereum transaction as an SDK message. +type MsgEthereumTx struct { + Data TxData + + // caches + size atomic.Value + from atomic.Value +} - return newEthereumTxMsg(nonce, nil, amount, gasLimit, gasPrice, payload) +// sigCache is used to cache the derived sender and contains the signer used +// to derive it. +type sigCache struct { + signer ethtypes.Signer + from ethcmn.Address } -func newEthereumTxMsg( +// NewMsgEthereumTx returns a reference to a new Ethereum transaction message. +func NewMsgEthereumTx( nonce uint64, to *ethcmn.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, payload []byte, -) *EthereumTxMsg { +) MsgEthereumTx { + return newMsgEthereumTx(nonce, to, amount, gasLimit, gasPrice, payload) +} + +// NewMsgEthereumTxContract returns a reference to a new Ethereum transaction +// message designated for contract creation. +func NewMsgEthereumTxContract( + nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, payload []byte, +) MsgEthereumTx { + return newMsgEthereumTx(nonce, nil, amount, gasLimit, gasPrice, payload) +} +func newMsgEthereumTx( + nonce uint64, to *ethcmn.Address, amount *big.Int, + gasLimit uint64, gasPrice *big.Int, payload []byte, +) MsgEthereumTx { if len(payload) > 0 { payload = ethcmn.CopyBytes(payload) } @@ -115,24 +173,33 @@ func newEthereumTxMsg( txData.Price.Set(gasPrice) } - return &EthereumTxMsg{Data: txData} + return MsgEthereumTx{Data: txData} +} + +func (msg MsgEthereumTx) String() string { + return msg.Data.String() } -// Route returns the route value of an EthereumTxMsg. -func (msg EthereumTxMsg) Route() string { return RouteEthereumTxMsg } +// Route returns the route value of an MsgEthereumTx. +func (msg MsgEthereumTx) Route() string { return RouterKey } -// Type returns the type value of an EthereumTxMsg. -func (msg EthereumTxMsg) Type() string { return TypeEthereumTxMsg } +// Type returns the type value of an MsgEthereumTx. +func (msg MsgEthereumTx) Type() string { return TypeMsgEthereumTx } // ValidateBasic implements the sdk.Msg interface. It performs basic validation -// checks of a Transaction. If returns an sdk.Error if validation fails. -func (msg EthereumTxMsg) ValidateBasic() sdk.Error { - if msg.Data.Price.Sign() != 1 { - return types.ErrInvalidValue("price must be positive") +// checks of a Transaction. If returns an error if validation fails. +func (msg MsgEthereumTx) ValidateBasic() error { + if msg.Data.Price.Cmp(big.NewInt(0)) == 0 { + return sdkerrors.Wrapf(types.ErrInvalidValue, "gas price cannot be 0") } - if msg.Data.Amount.Sign() != 1 { - return types.ErrInvalidValue("amount must be positive") + if msg.Data.Price.Sign() == -1 { + return sdkerrors.Wrapf(types.ErrInvalidValue, "gas price cannot be negative %s", msg.Data.Price) + } + + // Amount can be 0 + if msg.Data.Amount.Sign() == -1 { + return sdkerrors.Wrapf(types.ErrInvalidValue, "amount cannot be negative %s", msg.Data.Amount) } return nil @@ -140,26 +207,25 @@ func (msg EthereumTxMsg) ValidateBasic() sdk.Error { // To returns the recipient address of the transaction. It returns nil if the // transaction is a contract creation. -func (msg EthereumTxMsg) To() *ethcmn.Address { - if msg.Data.Recipient == nil { - return nil - } - +func (msg MsgEthereumTx) To() *ethcmn.Address { return msg.Data.Recipient } -// GetMsgs returns a single EthereumTxMsg as an sdk.Msg. -func (msg EthereumTxMsg) GetMsgs() []sdk.Msg { +// GetMsgs returns a single MsgEthereumTx as an sdk.Msg. +func (msg MsgEthereumTx) GetMsgs() []sdk.Msg { return []sdk.Msg{msg} } // GetSigners returns the expected signers for an Ethereum transaction message. // For such a message, there should exist only a single 'signer'. // -// NOTE: This method cannot be used as a chain ID is needed to recover the signer -// from the signature. Use 'VerifySig' instead. -func (msg EthereumTxMsg) GetSigners() []sdk.AccAddress { - panic("must use 'VerifySig' with a chain ID to get the signer") +// NOTE: This method panics if 'VerifySig' hasn't been called first. +func (msg MsgEthereumTx) GetSigners() []sdk.AccAddress { + sender := msg.From() + if sender.Empty() { + panic("must use 'VerifySig' with a chain ID to get the signer") + } + return []sdk.AccAddress{sender} } // GetSignBytes returns the Amino bytes of an Ethereum transaction message used @@ -167,13 +233,13 @@ func (msg EthereumTxMsg) GetSigners() []sdk.AccAddress { // // NOTE: This method cannot be used as a chain ID is needed to create valid bytes // to sign over. Use 'RLPSignBytes' instead. -func (msg EthereumTxMsg) GetSignBytes() []byte { +func (msg MsgEthereumTx) GetSignBytes() []byte { panic("must use 'RLPSignBytes' with a chain ID to get the valid bytes to sign") } // RLPSignBytes returns the RLP hash of an Ethereum transaction message with a // given chainID used for signing. -func (msg EthereumTxMsg) RLPSignBytes(chainID *big.Int) ethcmn.Hash { +func (msg MsgEthereumTx) RLPSignBytes(chainID *big.Int) ethcmn.Hash { return rlpHash([]interface{}{ msg.Data.AccountNonce, msg.Data.Price, @@ -186,48 +252,40 @@ func (msg EthereumTxMsg) RLPSignBytes(chainID *big.Int) ethcmn.Hash { } // EncodeRLP implements the rlp.Encoder interface. -func (msg *EthereumTxMsg) EncodeRLP(w io.Writer) error { +func (msg *MsgEthereumTx) EncodeRLP(w io.Writer) error { return rlp.Encode(w, &msg.Data) } // DecodeRLP implements the rlp.Decoder interface. -func (msg *EthereumTxMsg) DecodeRLP(s *rlp.Stream) error { - _, size, _ := s.Kind() - - err := s.Decode(&msg.Data) - if err == nil { - msg.size.Store(ethcmn.StorageSize(rlp.ListSize(size))) +func (msg *MsgEthereumTx) DecodeRLP(s *rlp.Stream) error { + _, size, err := s.Kind() + if err != nil { + // return error if stream is too large + return err } - return err -} - -// Hash hashes the RLP encoding of a transaction. -func (msg *EthereumTxMsg) Hash() ethcmn.Hash { - if hash := msg.hash.Load(); hash != nil { - return hash.(ethcmn.Hash) + if err := s.Decode(&msg.Data); err != nil { + return err } - v := rlpHash(msg) - msg.hash.Store(v) - - return v + msg.size.Store(ethcmn.StorageSize(rlp.ListSize(size))) + return nil } // Sign calculates a secp256k1 ECDSA signature and signs the transaction. It // takes a private key and chainID to sign an Ethereum transaction according to // EIP155 standard. It mutates the transaction as it populates the V, R, S // fields of the Transaction's Signature. -func (msg *EthereumTxMsg) Sign(chainID *big.Int, priv *ecdsa.PrivateKey) { +func (msg *MsgEthereumTx) Sign(chainID *big.Int, priv *ecdsa.PrivateKey) error { txHash := msg.RLPSignBytes(chainID) sig, err := ethcrypto.Sign(txHash[:], priv) if err != nil { - panic(err) + return err } if len(sig) != 65 { - panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig))) + return fmt.Errorf("wrong size for signature: got %d, want 65", len(sig)) } r := new(big.Int).SetBytes(sig[:32]) @@ -247,11 +305,12 @@ func (msg *EthereumTxMsg) Sign(chainID *big.Int, priv *ecdsa.PrivateKey) { msg.Data.V = v msg.Data.R = r msg.Data.S = s + return nil } // VerifySig attempts to verify a Transaction's signature for a given chainID. // A derived address is returned upon success or an error if recovery fails. -func (msg *EthereumTxMsg) VerifySig(chainID *big.Int) (ethcmn.Address, error) { +func (msg *MsgEthereumTx) VerifySig(chainID *big.Int) (ethcmn.Address, error) { signer := ethtypes.NewEIP155Signer(chainID) if sc := msg.from.Load(); sc != nil { @@ -282,75 +341,60 @@ func (msg *EthereumTxMsg) VerifySig(chainID *big.Int) (ethcmn.Address, error) { return sender, nil } -// Cost returns amount + gasprice * gaslimit. -func (msg EthereumTxMsg) Cost() *big.Int { - total := msg.Fee() - total.Add(total, msg.Data.Amount) - return total +// GetGas implements the GasTx interface. It returns the GasLimit of the transaction. +func (msg MsgEthereumTx) GetGas() uint64 { + return msg.Data.GasLimit } // Fee returns gasprice * gaslimit. -func (msg EthereumTxMsg) Fee() *big.Int { +func (msg MsgEthereumTx) Fee() *big.Int { return new(big.Int).Mul(msg.Data.Price, new(big.Int).SetUint64(msg.Data.GasLimit)) } -// ---------------------------------------------------------------------------- -// Auxiliary - -// TxDecoder returns an sdk.TxDecoder that can decode both auth.StdTx and -// EthereumTxMsg transactions. -func TxDecoder(cdc *codec.Codec) sdk.TxDecoder { - return func(txBytes []byte) (sdk.Tx, sdk.Error) { - var tx sdk.Tx - - if len(txBytes) == 0 { - return nil, sdk.ErrTxDecode("txBytes are empty") - } - - err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) - if err != nil { - fmt.Println(err.Error()) - return nil, sdk.ErrTxDecode("failed to decode tx").TraceSDK(err.Error()) - } +// ChainID returns which chain id this transaction was signed for (if at all) +func (msg *MsgEthereumTx) ChainID() *big.Int { + return deriveChainID(msg.Data.V) +} - return tx, nil - } +// Cost returns amount + gasprice * gaslimit. +func (msg MsgEthereumTx) Cost() *big.Int { + total := msg.Fee() + total.Add(total, msg.Data.Amount) + return total } -// recoverEthSig recovers a signature according to the Ethereum specification and -// returns the sender or an error. -// -// Ref: Ethereum Yellow Paper (BYZANTIUM VERSION 69351d5) Appendix F -func recoverEthSig(R, S, Vb *big.Int, sigHash ethcmn.Hash) (ethcmn.Address, error) { - if Vb.BitLen() > 8 { - return ethcmn.Address{}, errors.New("invalid signature") - } +// RawSignatureValues returns the V, R, S signature values of the transaction. +// The return values should not be modified by the caller. +func (msg MsgEthereumTx) RawSignatureValues() (v, r, s *big.Int) { + return msg.Data.V, msg.Data.R, msg.Data.S +} - V := byte(Vb.Uint64() - 27) - if !ethcrypto.ValidateSignatureValues(V, R, S, true) { - return ethcmn.Address{}, errors.New("invalid signature") +// From loads the ethereum sender address from the sigcache and returns an +// sdk.AccAddress from its bytes +func (msg *MsgEthereumTx) From() sdk.AccAddress { + sc := msg.from.Load() + if sc == nil { + return nil } - // encode the signature in uncompressed format - r, s := R.Bytes(), S.Bytes() - sig := make([]byte, 65) - - copy(sig[32-len(r):32], r) - copy(sig[64-len(s):64], s) - sig[64] = V - - // recover the public key from the signature - pub, err := ethcrypto.Ecrecover(sigHash[:], sig) - if err != nil { - return ethcmn.Address{}, err - } + sigCache := sc.(sigCache) - if len(pub) == 0 || pub[0] != 4 { - return ethcmn.Address{}, errors.New("invalid public key") + if len(sigCache.from.Bytes()) == 0 { + return nil } - var addr ethcmn.Address - copy(addr[:], ethcrypto.Keccak256(pub[1:])[12:]) + return sdk.AccAddress(sigCache.from.Bytes()) +} - return addr, nil +// deriveChainID derives the chain id from the given v parameter +func deriveChainID(v *big.Int) *big.Int { + if v.BitLen() <= 64 { + v := v.Uint64() + if v == 27 || v == 28 { + return new(big.Int) + } + return new(big.Int).SetUint64((v - 35) / 2) + } + v = new(big.Int).Sub(v, big.NewInt(35)) + return v.Div(v, big.NewInt(2)) } diff --git a/x/evm/types/msg_test.go b/x/evm/types/msg_test.go index 3fe43838e5..f4c8ff5c8a 100644 --- a/x/evm/types/msg_test.go +++ b/x/evm/types/msg_test.go @@ -6,47 +6,52 @@ import ( "math/big" "testing" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ethermint/crypto" + ethcmn "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" - "github.com/stretchr/testify/require" -) -func TestMsgEthereumTx(t *testing.T) { - addr := GenerateEthAddress() + "github.com/tendermint/tendermint/crypto/secp256k1" +) - msg1 := NewEthereumTxMsg(0, addr, nil, 100000, nil, []byte("test")) - require.NotNil(t, msg1) - require.Equal(t, *msg1.Data.Recipient, addr) +func TestMsgEthermint(t *testing.T) { + addr := newSdkAddress() + fromAddr := newSdkAddress() - msg2 := NewEthereumTxMsgContract(0, nil, 100000, nil, []byte("test")) - require.NotNil(t, msg2) - require.Nil(t, msg2.Data.Recipient) + msg := NewMsgEthermint(0, &addr, sdk.NewInt(1), 100000, sdk.NewInt(2), []byte("test"), fromAddr) + require.NotNil(t, msg) + require.Equal(t, msg.Recipient, &addr) - msg3 := NewEthereumTxMsg(0, addr, nil, 100000, nil, []byte("test")) - require.Equal(t, msg3.Route(), RouteEthereumTxMsg) - require.Equal(t, msg3.Type(), TypeEthereumTxMsg) - require.Panics(t, func() { msg3.GetSigners() }) - require.Panics(t, func() { msg3.GetSignBytes() }) + require.Equal(t, msg.Route(), RouterKey) + require.Equal(t, msg.Type(), TypeMsgEthermint) } -func TestMsgEthereumTxValidation(t *testing.T) { +func TestMsgEthermintValidation(t *testing.T) { testCases := []struct { nonce uint64 - to ethcmn.Address - amount *big.Int + to *sdk.AccAddress + amount sdk.Int gasLimit uint64 - gasPrice *big.Int + gasPrice sdk.Int payload []byte expectPass bool + from sdk.AccAddress }{ - {amount: big.NewInt(100), gasPrice: big.NewInt(100000), expectPass: true}, - {amount: big.NewInt(-1), gasPrice: big.NewInt(100000), expectPass: false}, - {amount: big.NewInt(100), gasPrice: big.NewInt(-1), expectPass: false}, + {amount: sdk.NewInt(100), gasPrice: sdk.NewInt(100000), expectPass: true}, + {amount: sdk.NewInt(0), gasPrice: sdk.NewInt(100000), expectPass: true}, + {amount: sdk.NewInt(-1), gasPrice: sdk.NewInt(100000), expectPass: false}, + {amount: sdk.NewInt(100), gasPrice: sdk.NewInt(-1), expectPass: false}, + {amount: sdk.NewInt(100), gasPrice: sdk.NewInt(0), expectPass: false}, } for i, tc := range testCases { - msg := NewEthereumTxMsg(tc.nonce, tc.to, tc.amount, tc.gasLimit, tc.gasPrice, tc.payload) + msg := NewMsgEthermint(tc.nonce, tc.to, tc.amount, tc.gasLimit, tc.gasPrice, tc.payload, tc.from) if tc.expectPass { require.Nil(t, msg.ValidateBasic(), "test: %v", i) @@ -56,44 +61,106 @@ func TestMsgEthereumTxValidation(t *testing.T) { } } +func TestMsgEthermintEncodingAndDecoding(t *testing.T) { + addr := newSdkAddress() + fromAddr := newSdkAddress() + + msg := NewMsgEthermint(0, &addr, sdk.NewInt(1), 100000, sdk.NewInt(2), []byte("test"), fromAddr) + + raw, err := ModuleCdc.MarshalBinaryBare(msg) + require.NoError(t, err) + + var msg2 MsgEthermint + err = ModuleCdc.UnmarshalBinaryBare(raw, &msg2) + require.NoError(t, err) + + require.Equal(t, msg.AccountNonce, msg2.AccountNonce) + require.Equal(t, msg.Recipient, msg2.Recipient) + require.Equal(t, msg.Amount, msg2.Amount) + require.Equal(t, msg.GasLimit, msg2.GasLimit) + require.Equal(t, msg.Price, msg2.Price) + require.Equal(t, msg.Payload, msg2.Payload) + require.Equal(t, msg.From, msg2.From) +} + +func newSdkAddress() sdk.AccAddress { + tmpKey := secp256k1.GenPrivKey().PubKey() + return sdk.AccAddress(tmpKey.Address().Bytes()) +} + +func TestMsgEthereumTx(t *testing.T) { + addr := GenerateEthAddress() + + msg := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test")) + require.NotNil(t, msg) + require.Equal(t, *msg.Data.Recipient, addr) + require.Equal(t, msg.Route(), RouterKey) + require.Equal(t, msg.Type(), TypeMsgEthereumTx) + require.NotNil(t, msg.To()) + require.Equal(t, msg.GetMsgs(), []sdk.Msg{msg}) + require.Panics(t, func() { msg.GetSigners() }) + require.Panics(t, func() { msg.GetSignBytes() }) + + msg = NewMsgEthereumTxContract(0, nil, 100000, nil, []byte("test")) + require.NotNil(t, msg) + require.Nil(t, msg.Data.Recipient) + require.Nil(t, msg.To()) +} + +func TestMsgEthereumTxValidation(t *testing.T) { + testCases := []struct { + msg string + amount *big.Int + gasPrice *big.Int + expectPass bool + }{ + {msg: "pass", amount: big.NewInt(100), gasPrice: big.NewInt(100000), expectPass: true}, + {msg: "invalid amount", amount: big.NewInt(-1), gasPrice: big.NewInt(100000), expectPass: false}, + {msg: "invalid gas price", amount: big.NewInt(100), gasPrice: big.NewInt(-1), expectPass: false}, + {msg: "invalid gas price", amount: big.NewInt(100), gasPrice: big.NewInt(0), expectPass: false}, + } + + for i, tc := range testCases { + msg := NewMsgEthereumTx(0, nil, tc.amount, 0, tc.gasPrice, nil) + + if tc.expectPass { + require.Nil(t, msg.ValidateBasic(), "valid test %d failed: %s", i, tc.msg) + } else { + require.NotNil(t, msg.ValidateBasic(), "invalid test %d passed: %s", i, tc.msg) + } + } +} + func TestMsgEthereumTxRLPSignBytes(t *testing.T) { addr := ethcmn.BytesToAddress([]byte("test_address")) chainID := big.NewInt(3) - msg := NewEthereumTxMsg(0, addr, nil, 100000, nil, []byte("test")) + msg := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test")) hash := msg.RLPSignBytes(chainID) require.Equal(t, "5BD30E35AD27449390B14C91E6BCFDCAADF8FE44EF33680E3BC200FC0DC083C7", fmt.Sprintf("%X", hash)) } func TestMsgEthereumTxRLPEncode(t *testing.T) { addr := ethcmn.BytesToAddress([]byte("test_address")) - msg := NewEthereumTxMsg(0, addr, nil, 100000, nil, []byte("test")) + msg := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test")) - raw, err := rlp.EncodeToBytes(msg) + raw, err := rlp.EncodeToBytes(&msg) require.NoError(t, err) require.Equal(t, ethcmn.FromHex("E48080830186A0940000000000000000746573745F61646472657373808474657374808080"), raw) } func TestMsgEthereumTxRLPDecode(t *testing.T) { - var msg EthereumTxMsg + var msg MsgEthereumTx raw := ethcmn.FromHex("E48080830186A0940000000000000000746573745F61646472657373808474657374808080") addr := ethcmn.BytesToAddress([]byte("test_address")) - expectedMsg := NewEthereumTxMsg(0, addr, nil, 100000, nil, []byte("test")) + expectedMsg := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test")) err := rlp.Decode(bytes.NewReader(raw), &msg) require.NoError(t, err) require.Equal(t, expectedMsg.Data, msg.Data) } -func TestMsgEthereumTxHash(t *testing.T) { - addr := ethcmn.BytesToAddress([]byte("test_address")) - msg := NewEthereumTxMsg(0, addr, nil, 100000, nil, []byte("test")) - - hash := msg.Hash() - require.Equal(t, "E2AA2E68E7586AE9700F1D3D643330866B6AC2B6CA4C804F7C85ECB11D0B0B29", fmt.Sprintf("%X", hash)) -} - func TestMsgEthereumTxSig(t *testing.T) { chainID := big.NewInt(3) @@ -103,8 +170,9 @@ func TestMsgEthereumTxSig(t *testing.T) { addr2 := ethcmn.BytesToAddress(priv2.PubKey().Address().Bytes()) // require valid signature passes validation - msg := NewEthereumTxMsg(0, addr1, nil, 100000, nil, []byte("test")) - msg.Sign(chainID, priv1.ToECDSA()) + msg := NewMsgEthereumTx(0, &addr1, nil, 100000, nil, []byte("test")) + err := msg.Sign(chainID, priv1.ToECDSA()) + require.Nil(t, err) signer, err := msg.VerifySig(chainID) require.NoError(t, err) @@ -112,24 +180,46 @@ func TestMsgEthereumTxSig(t *testing.T) { require.NotEqual(t, addr2, signer) // require invalid chain ID fail validation - msg = NewEthereumTxMsg(0, addr1, nil, 100000, nil, []byte("test")) - msg.Sign(chainID, priv1.ToECDSA()) + msg = NewMsgEthereumTx(0, &addr1, nil, 100000, nil, []byte("test")) + err = msg.Sign(chainID, priv1.ToECDSA()) + require.Nil(t, err) signer, err = msg.VerifySig(big.NewInt(4)) require.Error(t, err) require.Equal(t, ethcmn.Address{}, signer) } -func TestMsgEthereumTxAmino(t *testing.T) { - addr := GenerateEthAddress() - msg := NewEthereumTxMsg(0, addr, nil, 100000, nil, []byte("test")) +func TestMarshalAndUnmarshalLogs(t *testing.T) { + var cdc = codec.New() + + logs := []*ethtypes.Log{ + { + Address: ethcmn.BytesToAddress([]byte{0x11}), + TxHash: ethcmn.HexToHash("0x01"), + // May need to find workaround since Topics is required to unmarshal from JSON + Topics: []ethcmn.Hash{}, + Removed: true, + }, + {Address: ethcmn.BytesToAddress([]byte{0x01, 0x11}), Topics: []ethcmn.Hash{}}, + } + + raw, err := codec.MarshalJSONIndent(cdc, logs) + require.NoError(t, err) - raw, err := msgCodec.MarshalBinaryBare(msg) + var logs2 []*ethtypes.Log + err = cdc.UnmarshalJSON(raw, &logs2) require.NoError(t, err) - var msg2 EthereumTxMsg + require.Len(t, logs2, 2) + require.Equal(t, logs[0].Address, logs2[0].Address) + require.Equal(t, logs[0].TxHash, logs2[0].TxHash) + require.True(t, logs[0].Removed) + + emptyLogs := []*ethtypes.Log{} + + raw, err = codec.MarshalJSONIndent(cdc, emptyLogs) + require.NoError(t, err) - err = msgCodec.UnmarshalBinaryBare(raw, &msg2) + err = cdc.UnmarshalJSON(raw, &logs2) require.NoError(t, err) - require.Equal(t, msg.Data, msg2.Data) } diff --git a/x/evm/types/params.go b/x/evm/types/params.go new file mode 100644 index 0000000000..a6c5ad3dab --- /dev/null +++ b/x/evm/types/params.go @@ -0,0 +1,73 @@ +package types + +import ( + "fmt" + + "gopkg.in/yaml.v2" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params" + + ethermint "github.com/cosmos/ethermint/types" +) + +const ( + // DefaultParamspace for params keeper + DefaultParamspace = ModuleName +) + +// Parameter keys +var ( + ParamStoreKeyEVMDenom = []byte("EVMDenom") +) + +// ParamKeyTable returns the parameter key table. +func ParamKeyTable() params.KeyTable { + return params.NewKeyTable().RegisterParamSet(&Params{}) +} + +// Params defines the EVM module parameters +type Params struct { + EvmDenom string `json:"evm_denom" yaml:"evm_denom"` +} + +// NewParams creates a new Params instance +func NewParams(evmDenom string) Params { + return Params{ + EvmDenom: evmDenom, + } +} + +// DefaultParams returns default evm parameters +func DefaultParams() Params { + return Params{ + EvmDenom: ethermint.AttoPhoton, + } +} + +// String implements the fmt.Stringer interface +func (p Params) String() string { + out, _ := yaml.Marshal(p) + return string(out) +} + +// ParamSetPairs returns the parameter set pairs. +func (p *Params) ParamSetPairs() params.ParamSetPairs { + return params.ParamSetPairs{ + params.NewParamSetPair(ParamStoreKeyEVMDenom, &p.EvmDenom, validateEVMDenom), + } +} + +// Validate performs basic validation on evm parameters. +func (p Params) Validate() error { + return sdk.ValidateDenom(p.EvmDenom) +} + +func validateEVMDenom(i interface{}) error { + denom, ok := i.(string) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + return sdk.ValidateDenom(denom) +} diff --git a/x/evm/types/params_test.go b/x/evm/types/params_test.go new file mode 100644 index 0000000000..244e360c42 --- /dev/null +++ b/x/evm/types/params_test.go @@ -0,0 +1,52 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParamsValidate(t *testing.T) { + testCases := []struct { + name string + params Params + expError bool + }{ + {"default", DefaultParams(), false}, + { + "valid", + NewParams("ara"), + false, + }, + { + "empty", + Params{}, + true, + }, + { + "invalid evm denom", + Params{ + EvmDenom: "@!#!@$!@5^32", + }, + true, + }, + } + + for _, tc := range testCases { + err := tc.params.Validate() + + if tc.expError { + require.Error(t, err, tc.name) + } else { + require.NoError(t, err, tc.name) + } + } +} + +func TestParamsValidatePriv(t *testing.T) { + require.Error(t, validateEVMDenom(false)) +} + +func TestParams_String(t *testing.T) { + require.Equal(t, "evm_denom: aphoton\n", DefaultParams().String()) +} diff --git a/x/evm/types/querier.go b/x/evm/types/querier.go new file mode 100644 index 0000000000..397c37ca21 --- /dev/null +++ b/x/evm/types/querier.go @@ -0,0 +1,104 @@ +package types + +import ( + "fmt" + + ethtypes "github.com/ethereum/go-ethereum/core/types" +) + +// Supported endpoints +const ( + QueryProtocolVersion = "protocolVersion" + QueryBalance = "balance" + QueryBlockNumber = "blockNumber" + QueryStorage = "storage" + QueryCode = "code" + QueryNonce = "nonce" + QueryHashToHeight = "hashToHeight" + QueryTransactionLogs = "transactionLogs" + QueryBloom = "bloom" + QueryLogs = "logs" + QueryAccount = "account" + QueryExportAccount = "exportAccount" +) + +// QueryResProtocolVersion is response type for protocol version query +type QueryResProtocolVersion struct { + Version string `json:"version"` +} + +func (q QueryResProtocolVersion) String() string { + return q.Version +} + +// QueryResBalance is response type for balance query +type QueryResBalance struct { + Balance string `json:"balance"` +} + +func (q QueryResBalance) String() string { + return q.Balance +} + +// QueryResBlockNumber is response type for block number query +type QueryResBlockNumber struct { + Number int64 `json:"blockNumber"` +} + +func (q QueryResBlockNumber) String() string { + return fmt.Sprint(q.Number) +} + +// QueryResStorage is response type for storage query +type QueryResStorage struct { + Value []byte `json:"value"` +} + +func (q QueryResStorage) String() string { + return string(q.Value) +} + +// QueryResCode is response type for code query +type QueryResCode struct { + Code []byte +} + +func (q QueryResCode) String() string { + return string(q.Code) +} + +// QueryResNonce is response type for Nonce query +type QueryResNonce struct { + Nonce uint64 `json:"nonce"` +} + +func (q QueryResNonce) String() string { + return fmt.Sprint(q.Nonce) +} + +// QueryETHLogs is response type for tx logs query +type QueryETHLogs struct { + Logs []*ethtypes.Log `json:"logs"` +} + +func (q QueryETHLogs) String() string { + return fmt.Sprintf("%+v", q.Logs) +} + +// QueryBloomFilter is response type for tx logs query +type QueryBloomFilter struct { + Bloom ethtypes.Bloom `json:"bloom"` +} + +func (q QueryBloomFilter) String() string { + return string(q.Bloom.Bytes()) +} + +// QueryAccount is response type for querying Ethereum state objects +type QueryResAccount struct { + Balance string `json:"balance"` + CodeHash []byte `json:"codeHash"` + Nonce uint64 `json:"nonce"` +} + +type QueryResExportAccount = GenesisAccount diff --git a/x/evm/types/state_object.go b/x/evm/types/state_object.go index 4fe8233537..d9c4d7e127 100644 --- a/x/evm/types/state_object.go +++ b/x/evm/types/state_object.go @@ -3,73 +3,103 @@ package types import ( "bytes" "fmt" + "io" "math/big" + "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - auth "github.com/cosmos/cosmos-sdk/x/auth" + authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" "github.com/cosmos/ethermint/types" + ethcmn "github.com/ethereum/go-ethereum/common" ethstate "github.com/ethereum/go-ethereum/core/state" ethcrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" ) var ( - _ ethstate.StateObject = (*stateObject)(nil) + _ StateObject = (*stateObject)(nil) emptyCodeHash = ethcrypto.Keccak256(nil) ) -type ( - // stateObject represents an Ethereum account which is being modified. +// StateObject interface for interacting with state object +type StateObject interface { + GetCommittedState(db ethstate.Database, key ethcmn.Hash) ethcmn.Hash + GetState(db ethstate.Database, key ethcmn.Hash) ethcmn.Hash + SetState(db ethstate.Database, key, value ethcmn.Hash) + + Code(db ethstate.Database) []byte + SetCode(codeHash ethcmn.Hash, code []byte) + CodeHash() []byte + + AddBalance(amount *big.Int) + SubBalance(amount *big.Int) + SetBalance(amount *big.Int) + + Balance() *big.Int + ReturnGas(gas *big.Int) + Address() ethcmn.Address + + SetNonce(nonce uint64) + Nonce() uint64 +} + +// stateObject represents an Ethereum account which is being modified. +// +// The usage pattern is as follows: +// First you need to obtain a state object. +// Account values can be accessed and modified through the object. +// Finally, call CommitTrie to write the modified storage trie into a database. +type stateObject struct { + code types.Code // contract bytecode, which gets set when code is loaded + // State objects are used by the consensus core and VM which are + // unable to deal with database-level errors. Any error that occurs + // during a database read is memoized here and will eventually be returned + // by StateDB.Commit. + originStorage Storage // Storage cache of original entries to dedup rewrites + dirtyStorage Storage // Storage entries that need to be flushed to disk + + // DB error + dbErr error + stateDB *CommitStateDB + account *types.EthAccount + + keyToOriginStorageIndex map[ethcmn.Hash]int + keyToDirtyStorageIndex map[ethcmn.Hash]int + + address ethcmn.Address + + // cache flags // - // The usage pattern is as follows: - // First you need to obtain a state object. - // Account values can be accessed and modified through the object. - // Finally, call CommitTrie to write the modified storage trie into a database. - stateObject struct { - address ethcmn.Address - stateDB *CommitStateDB - account *types.Account - - // DB error. - // State objects are used by the consensus core and VM which are - // unable to deal with database-level errors. Any error that occurs - // during a database read is memoized here and will eventually be returned - // by StateDB.Commit. - dbErr error - - code types.Code // contract bytecode, which gets set when code is loaded - - originStorage types.Storage // Storage cache of original entries to dedup rewrites - dirtyStorage types.Storage // Storage entries that need to be flushed to disk - - // cache flags - // - // When an object is marked suicided it will be delete from the trie during - // the "update" phase of the state transition. - dirtyCode bool // true if the code was updated - suicided bool - deleted bool - } -) + // When an object is marked suicided it will be delete from the trie during + // the "update" phase of the state transition. + dirtyCode bool // true if the code was updated + suicided bool + deleted bool +} -func newObject(db *CommitStateDB, accProto auth.Account) *stateObject { - acc, ok := accProto.(*types.Account) +func newStateObject(db *CommitStateDB, accProto authexported.Account) *stateObject { + // func newStateObject(db *CommitStateDB, accProto authexported.Account, balance sdk.Int) *stateObject { + ethermintAccount, ok := accProto.(*types.EthAccount) if !ok { - panic(fmt.Sprintf("invalid account type for state object: %T", acc)) + panic(fmt.Sprintf("invalid account type for state object: %T", accProto)) } - if acc.CodeHash == nil { - acc.CodeHash = emptyCodeHash + // set empty code hash + if ethermintAccount.CodeHash == nil { + ethermintAccount.CodeHash = emptyCodeHash } return &stateObject{ - stateDB: db, - account: acc, - address: ethcmn.BytesToAddress(acc.Address.Bytes()), - originStorage: make(types.Storage), - dirtyStorage: make(types.Storage), + stateDB: db, + account: ethermintAccount, + address: ethermintAccount.EthAddress(), + originStorage: Storage{}, + dirtyStorage: Storage{}, + keyToOriginStorageIndex: make(map[ethcmn.Hash]int), + keyToDirtyStorageIndex: make(map[ethcmn.Hash]int), } } @@ -98,8 +128,18 @@ func (so *stateObject) SetState(db ethstate.Database, key, value ethcmn.Hash) { so.setState(prefixKey, value) } +// setState sets a state with a prefixed key and value to the dirty storage. func (so *stateObject) setState(key, value ethcmn.Hash) { - so.dirtyStorage[key] = value + idx, ok := so.keyToDirtyStorageIndex[key] + if ok { + so.dirtyStorage[idx].Value = value + return + } + + // create new entry + so.dirtyStorage = append(so.dirtyStorage, NewState(key, value)) + idx = len(so.dirtyStorage) - 1 + so.keyToDirtyStorageIndex[key] = idx } // SetCode sets the state object's code. @@ -125,18 +165,19 @@ func (so *stateObject) setCode(codeHash ethcmn.Hash, code []byte) { // funds to the destination account of a transfer. func (so *stateObject) AddBalance(amount *big.Int) { amt := sdk.NewIntFromBigInt(amount) - // EIP158: We must check emptiness for the objects such that the account // clearing (0,0,0 objects) can take effect. - if amt.Sign() == 0 { + + // NOTE: this will panic if amount is nil + if amt.IsZero() { if so.empty() { so.touch() } - return } - newBalance := so.account.Balance().Add(amt) + evmDenom := so.stateDB.GetParams().EvmDenom + newBalance := so.account.GetCoins().AmountOf(evmDenom).Add(amt) so.SetBalance(newBalance.BigInt()) } @@ -144,12 +185,12 @@ func (so *stateObject) AddBalance(amount *big.Int) { // remove funds from the origin account of a transfer. func (so *stateObject) SubBalance(amount *big.Int) { amt := sdk.NewIntFromBigInt(amount) - - if amt.Sign() == 0 { + if amt.IsZero() { return } - newBalance := so.account.Balance().Sub(amt) + evmDenom := so.stateDB.GetParams().EvmDenom + newBalance := so.account.GetCoins().AmountOf(evmDenom).Sub(amt) so.SetBalance(newBalance.BigInt()) } @@ -157,19 +198,20 @@ func (so *stateObject) SubBalance(amount *big.Int) { func (so *stateObject) SetBalance(amount *big.Int) { amt := sdk.NewIntFromBigInt(amount) + evmDenom := so.stateDB.GetParams().EvmDenom so.stateDB.journal.append(balanceChange{ account: &so.address, - prev: so.account.Balance(), + prev: so.account.GetCoins().AmountOf(evmDenom), }) - so.setBalance(amt) + so.setBalance(evmDenom, amt) } -func (so *stateObject) setBalance(amount sdk.Int) { - so.account.SetBalance(amount) +func (so *stateObject) setBalance(denom string, amount sdk.Int) { + so.account.SetBalance(denom, amount) } -// SetNonce sets the state object's nonce (sequence number). +// SetNonce sets the state object's nonce (i.e sequence number of the account). func (so *stateObject) SetNonce(nonce uint64) { so.stateDB.journal.append(nonceChange{ account: &so.address, @@ -180,6 +222,9 @@ func (so *stateObject) SetNonce(nonce uint64) { } func (so *stateObject) setNonce(nonce uint64) { + if so.account == nil { + panic("state object account is empty") + } so.account.Sequence = nonce } @@ -194,37 +239,48 @@ func (so *stateObject) markSuicided() { so.suicided = true } -// commitState commits all dirty storage to a KVStore. +// commitState commits all dirty storage to a KVStore and resets +// the dirty storage slice to the empty state. func (so *stateObject) commitState() { ctx := so.stateDB.ctx - store := ctx.KVStore(so.stateDB.storageKey) + store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), AddressStoragePrefix(so.Address())) - for key, value := range so.dirtyStorage { - delete(so.dirtyStorage, key) + for _, state := range so.dirtyStorage { + // NOTE: key is already prefixed from GetStorageByAddressKey + + // delete empty values from the store + if (state.Value == ethcmn.Hash{}) { + store.Delete(state.Key.Bytes()) + } + + delete(so.keyToDirtyStorageIndex, state.Key) // skip no-op changes, persist actual changes - if value == so.originStorage[key] { + idx, ok := so.keyToOriginStorageIndex[state.Key] + if !ok { continue } - so.originStorage[key] = value + if (state.Value == ethcmn.Hash{}) { + delete(so.keyToOriginStorageIndex, state.Key) + continue + } - // delete empty values - if (value == ethcmn.Hash{}) { - store.Delete(key.Bytes()) + if state.Value == so.originStorage[idx].Value { continue } - store.Set(key.Bytes(), value.Bytes()) + so.originStorage[idx].Value = state.Value + store.Set(state.Key.Bytes(), state.Value.Bytes()) } - - // TODO: Set the account (storage) root (but we probably don't need this) + // clean storage as all entries are dirty + so.dirtyStorage = Storage{} } // commitCode persists the state object's code to the KVStore. func (so *stateObject) commitCode() { ctx := so.stateDB.ctx - store := ctx.KVStore(so.stateDB.codeKey) + store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), KeyPrefixCode) store.Set(so.CodeHash(), so.code) } @@ -239,22 +295,33 @@ func (so stateObject) Address() ethcmn.Address { // Balance returns the state object's current balance. func (so *stateObject) Balance() *big.Int { - return so.account.Balance().BigInt() + evmDenom := so.stateDB.GetParams().EvmDenom + balance := so.account.Balance(evmDenom).BigInt() + if balance == nil { + return zeroBalance + } + return balance } // CodeHash returns the state object's code hash. func (so *stateObject) CodeHash() []byte { + if so.account == nil || len(so.account.CodeHash) == 0 { + return emptyCodeHash + } return so.account.CodeHash } // Nonce returns the state object's current nonce (sequence number). func (so *stateObject) Nonce() uint64 { + if so.account == nil { + return 0 + } return so.account.Sequence } // Code returns the contract code associated with this object, if any. func (so *stateObject) Code(_ ethstate.Database) []byte { - if so.code != nil { + if len(so.code) > 0 { return so.code } @@ -263,14 +330,13 @@ func (so *stateObject) Code(_ ethstate.Database) []byte { } ctx := so.stateDB.ctx - store := ctx.KVStore(so.stateDB.codeKey) + store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), KeyPrefixCode) code := store.Get(so.CodeHash()) if len(code) == 0 { - so.setError(fmt.Errorf("failed to get code hash %x for address: %x", so.CodeHash(), so.Address())) + so.setError(fmt.Errorf("failed to get code hash %x for address %s", so.CodeHash(), so.Address().String())) } - so.code = code return code } @@ -280,37 +346,42 @@ func (so *stateObject) GetState(db ethstate.Database, key ethcmn.Hash) ethcmn.Ha prefixKey := so.GetStorageByAddressKey(key.Bytes()) // if we have a dirty value for this state entry, return it - value, dirty := so.dirtyStorage[prefixKey] + idx, dirty := so.keyToDirtyStorageIndex[prefixKey] if dirty { - return value + return so.dirtyStorage[idx].Value } // otherwise return the entry's original value - return so.GetCommittedState(db, key) + value := so.GetCommittedState(db, key) + return value } // GetCommittedState retrieves a value from the committed account storage trie. -// Note, the key will be prefixed with the address of the state object. +// +// NOTE: the key will be prefixed with the address of the state object. func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) ethcmn.Hash { prefixKey := so.GetStorageByAddressKey(key.Bytes()) // if we have the original value cached, return that - value, cached := so.originStorage[prefixKey] + idx, cached := so.keyToOriginStorageIndex[prefixKey] if cached { - return value + return so.originStorage[idx].Value } // otherwise load the value from the KVStore + state := NewState(prefixKey, ethcmn.Hash{}) + ctx := so.stateDB.ctx - store := ctx.KVStore(so.stateDB.storageKey) + store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), AddressStoragePrefix(so.Address())) rawValue := store.Get(prefixKey.Bytes()) if len(rawValue) > 0 { - value.SetBytes(rawValue) + state.Value.SetBytes(rawValue) } - so.originStorage[prefixKey] = value - return value + so.originStorage = append(so.originStorage, state) + so.keyToOriginStorageIndex[prefixKey] = len(so.originStorage) - 1 + return state.Value } // ---------------------------------------------------------------------------- @@ -322,7 +393,7 @@ func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) e func (so *stateObject) ReturnGas(gas *big.Int) {} func (so *stateObject) deepCopy(db *CommitStateDB) *stateObject { - newStateObj := newObject(db, so.account) + newStateObj := newStateObject(db, so.account) newStateObj.code = so.code newStateObj.dirtyStorage = so.dirtyStorage.Copy() @@ -336,9 +407,18 @@ func (so *stateObject) deepCopy(db *CommitStateDB) *stateObject { // empty returns whether the account is considered empty. func (so *stateObject) empty() bool { - return so.account.Sequence == 0 && - so.account.Balance().Sign() == 0 && - bytes.Equal(so.account.CodeHash, emptyCodeHash) + evmDenom := so.stateDB.GetParams().EvmDenom + balace := so.account.Balance(evmDenom) + return so.account == nil || + (so.account != nil && + so.account.Sequence == 0 && + (balace.BigInt() == nil || balace.IsZero()) && + bytes.Equal(so.account.CodeHash, emptyCodeHash)) +} + +// EncodeRLP implements rlp.Encoder. +func (so *stateObject) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, so.account) } func (so *stateObject) touch() { @@ -364,3 +444,11 @@ func (so stateObject) GetStorageByAddressKey(key []byte) ethcmn.Hash { return ethcrypto.Keccak256Hash(compositeKey) } + +// stateEntry represents a single key value pair from the StateDB's stateObject mappindg. +// This is to prevent non determinism at genesis initialization or export. +type stateEntry struct { + // address key of the state object + address ethcmn.Address + stateObject *stateObject +} diff --git a/x/evm/types/state_object_test.go b/x/evm/types/state_object_test.go new file mode 100644 index 0000000000..6739a2ecdf --- /dev/null +++ b/x/evm/types/state_object_test.go @@ -0,0 +1,135 @@ +package types_test + +import ( + "math/big" + + ethcmn "github.com/ethereum/go-ethereum/common" +) + +func (suite *StateDBTestSuite) TestStateObject_State() { + testCase := []struct { + name string + key ethcmn.Hash + expValue ethcmn.Hash + malleate func() + }{ + { + "no set value, load from KVStore", + ethcmn.BytesToHash([]byte("key")), + ethcmn.Hash{}, + func() {}, + }, + { + "no-op SetState", + ethcmn.BytesToHash([]byte("key")), + ethcmn.Hash{}, + func() { + suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key")), ethcmn.Hash{}) + }, + }, + { + "cached value", + ethcmn.BytesToHash([]byte("key1")), + ethcmn.BytesToHash([]byte("value1")), + func() { + suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key1")), ethcmn.BytesToHash([]byte("value1"))) + }, + }, + { + "update value", + ethcmn.BytesToHash([]byte("key1")), + ethcmn.BytesToHash([]byte("value2")), + func() { + suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key1")), ethcmn.BytesToHash([]byte("value2"))) + }, + }, + { + "update various keys", + ethcmn.BytesToHash([]byte("key1")), + ethcmn.BytesToHash([]byte("value1")), + func() { + suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key1")), ethcmn.BytesToHash([]byte("value1"))) + suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key2")), ethcmn.BytesToHash([]byte("value2"))) + suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key3")), ethcmn.BytesToHash([]byte("value3"))) + }, + }, + } + + for _, tc := range testCase { + tc.malleate() + + value := suite.stateObject.GetState(nil, tc.key) + suite.Require().Equal(tc.expValue, value, tc.name) + } +} + +func (suite *StateDBTestSuite) TestStateObject_AddBalance() { + testCase := []struct { + name string + amount *big.Int + expBalance *big.Int + }{ + {"zero amount", big.NewInt(0), big.NewInt(0)}, + {"positive amount", big.NewInt(10), big.NewInt(10)}, + {"negative amount", big.NewInt(-1), big.NewInt(9)}, + } + + for _, tc := range testCase { + suite.stateObject.AddBalance(tc.amount) + suite.Require().Equal(tc.expBalance, suite.stateObject.Balance(), tc.name) + } +} + +func (suite *StateDBTestSuite) TestStateObject_SubBalance() { + testCase := []struct { + name string + amount *big.Int + expBalance *big.Int + }{ + {"zero amount", big.NewInt(0), big.NewInt(0)}, + {"negative amount", big.NewInt(-10), big.NewInt(10)}, + {"positive amount", big.NewInt(1), big.NewInt(9)}, + } + + for _, tc := range testCase { + suite.stateObject.SubBalance(tc.amount) + suite.Require().Equal(tc.expBalance, suite.stateObject.Balance(), tc.name) + } +} + +func (suite *StateDBTestSuite) TestStateObject_Code() { + testCase := []struct { + name string + expCode []byte + malleate func() + }{ + { + "cached code", + []byte("code"), + func() { + suite.stateObject.SetCode(ethcmn.BytesToHash([]byte("code_hash")), []byte("code")) + }, + }, + { + "empty code hash", + nil, + func() { + suite.stateObject.SetCode(ethcmn.Hash{}, nil) + }, + }, + { + "empty code", + nil, + func() { + suite.stateObject.SetCode(ethcmn.BytesToHash([]byte("code_hash")), nil) + }, + }, + } + + for _, tc := range testCase { + tc.malleate() + + code := suite.stateObject.Code(nil) + suite.Require().Equal(tc.expCode, code, tc.name) + } +} diff --git a/x/evm/types/state_transition.go b/x/evm/types/state_transition.go new file mode 100644 index 0000000000..980bf53f4c --- /dev/null +++ b/x/evm/types/state_transition.go @@ -0,0 +1,216 @@ +package types + +import ( + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// StateTransition defines data to transitionDB in evm +type StateTransition struct { + // TxData fields + AccountNonce uint64 + Price *big.Int + GasLimit uint64 + Recipient *common.Address + Amount *big.Int + Payload []byte + + ChainID *big.Int + Csdb *CommitStateDB + TxHash *common.Hash + Sender common.Address + Simulate bool // i.e CheckTx execution +} + +// GasInfo returns the gas limit, gas consumed and gas refunded from the EVM transition +// execution +type GasInfo struct { + GasLimit uint64 + GasConsumed uint64 + GasRefunded uint64 +} + +// ExecutionResult represents what's returned from a transition +type ExecutionResult struct { + Logs []*ethtypes.Log + Bloom *big.Int + Result *sdk.Result + GasInfo GasInfo +} + +func (st StateTransition) newEVM(ctx sdk.Context, csdb *CommitStateDB, gasLimit uint64, gasPrice *big.Int, config ChainConfig) *vm.EVM { + // Create context for evm + context := vm.Context{ + CanTransfer: core.CanTransfer, + Transfer: core.Transfer, + Origin: st.Sender, + Coinbase: common.Address{}, // there's no benefitiary since we're not mining + BlockNumber: big.NewInt(ctx.BlockHeight()), + Time: big.NewInt(ctx.BlockHeader().Time.Unix()), + Difficulty: big.NewInt(0), // unused. Only required in PoW context + GasLimit: gasLimit, + GasPrice: gasPrice, + } + + return vm.NewEVM(context, csdb, config.EthereumConfig(st.ChainID), vm.Config{}) +} + +// TransitionDb will transition the state by applying the current transaction and +// returning the evm execution result. +// NOTE: State transition checks are run during AnteHandler execution. +func (st StateTransition) TransitionDb(ctx sdk.Context, config ChainConfig) (*ExecutionResult, error) { + contractCreation := st.Recipient == nil + + cost, err := core.IntrinsicGas(st.Payload, contractCreation, true, false) + if err != nil { + return nil, sdkerrors.Wrap(err, "invalid intrinsic gas for transaction") + } + + // This gas limit the the transaction gas limit with intrinsic gas subtracted + gasLimit := st.GasLimit - ctx.GasMeter().GasConsumed() + + csdb := st.Csdb.WithContext(ctx) + if st.Simulate { + // gasLimit is set here because stdTxs incur gaskv charges in the ante handler, but for eth_call + // the cost needs to be the same as an Ethereum transaction sent through the web3 API + consumedGas := ctx.GasMeter().GasConsumed() + gasLimit = st.GasLimit - cost + if consumedGas < cost { + // If Cosmos standard tx ante handler cost is less than EVM intrinsic cost + // gas must be consumed to match to accurately simulate an Ethereum transaction + ctx.GasMeter().ConsumeGas(cost-consumedGas, "Intrinsic gas match") + } + + csdb = st.Csdb.Copy() + } + + // This gas meter is set up to consume gas from gaskv during evm execution and be ignored + currentGasMeter := ctx.GasMeter() + evmGasMeter := sdk.NewInfiniteGasMeter() + csdb.WithContext(ctx.WithGasMeter(evmGasMeter)) + + // Clear cache of accounts to handle changes outside of the EVM + csdb.UpdateAccounts() + + evmDenom := csdb.GetParams().EvmDenom + gasPrice := ctx.MinGasPrices().AmountOf(evmDenom) + if gasPrice.IsNil() { + return nil, errors.New("gas price cannot be nil") + } + + evm := st.newEVM(ctx, csdb, gasLimit, gasPrice.Int, config) + + var ( + ret []byte + leftOverGas uint64 + contractAddress common.Address + recipientLog string + senderRef = vm.AccountRef(st.Sender) + ) + + // Get nonce of account outside of the EVM + currentNonce := csdb.GetNonce(st.Sender) + // Set nonce of sender account before evm state transition for usage in generating Create address + csdb.SetNonce(st.Sender, st.AccountNonce) + + // create contract or execute call + switch contractCreation { + case true: + ret, contractAddress, leftOverGas, err = evm.Create(senderRef, st.Payload, gasLimit, st.Amount) + recipientLog = fmt.Sprintf("contract address %s", contractAddress.String()) + default: + // Increment the nonce for the next transaction (just for evm state transition) + csdb.SetNonce(st.Sender, csdb.GetNonce(st.Sender)+1) + ret, leftOverGas, err = evm.Call(senderRef, *st.Recipient, st.Payload, gasLimit, st.Amount) + recipientLog = fmt.Sprintf("recipient address %s", st.Recipient.String()) + } + + gasConsumed := gasLimit - leftOverGas + + if err != nil { + // Consume gas before returning + ctx.GasMeter().ConsumeGas(gasConsumed, "evm execution consumption") + return nil, err + } + + // Resets nonce to value pre state transition + csdb.SetNonce(st.Sender, currentNonce) + + // Generate bloom filter to be saved in tx receipt data + bloomInt := big.NewInt(0) + + var ( + bloomFilter ethtypes.Bloom + logs []*ethtypes.Log + ) + + if st.TxHash != nil && !st.Simulate { + logs, err = csdb.GetLogs(*st.TxHash) + if err != nil { + return nil, err + } + + bloomInt = ethtypes.LogsBloom(logs) + bloomFilter = ethtypes.BytesToBloom(bloomInt.Bytes()) + } + + if !st.Simulate { + // Finalise state if not a simulated transaction + // TODO: change to depend on config + if err := csdb.Finalise(true); err != nil { + return nil, err + } + } + + // Encode all necessary data into slice of bytes to return in sdk result + resultData := ResultData{ + Bloom: bloomFilter, + Logs: logs, + Ret: ret, + TxHash: *st.TxHash, + } + + if contractCreation { + resultData.ContractAddress = contractAddress + } + + resBz, err := EncodeResultData(resultData) + if err != nil { + return nil, err + } + + resultLog := fmt.Sprintf( + "executed EVM state transition; sender address %s; %s", st.Sender.String(), recipientLog, + ) + + executionResult := &ExecutionResult{ + Logs: logs, + Bloom: bloomInt, + Result: &sdk.Result{ + Data: resBz, + Log: resultLog, + }, + GasInfo: GasInfo{ + GasConsumed: gasConsumed, + GasLimit: gasLimit, + GasRefunded: leftOverGas, + }, + } + + // TODO: Refund unused gas here, if intended in future + + // Consume gas from evm execution + // Out of gas check does not need to be done here since it is done within the EVM execution + ctx.WithGasMeter(currentGasMeter).GasMeter().ConsumeGas(gasConsumed, "EVM execution consumption") + + return executionResult, nil +} diff --git a/x/evm/types/state_transition_test.go b/x/evm/types/state_transition_test.go new file mode 100644 index 0000000000..f096c610cc --- /dev/null +++ b/x/evm/types/state_transition_test.go @@ -0,0 +1,147 @@ +package types_test + +import ( + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/ethermint/crypto" + ethermint "github.com/cosmos/ethermint/types" + "github.com/cosmos/ethermint/x/evm/types" + + ethcmn "github.com/ethereum/go-ethereum/common" + ethcrypto "github.com/ethereum/go-ethereum/crypto" +) + +func (suite *StateDBTestSuite) TestTransitionDb() { + suite.stateDB.SetNonce(suite.address, 123) + + addr := sdk.AccAddress(suite.address.Bytes()) + balance := ethermint.NewPhotonCoin(sdk.NewInt(5000)) + acc := suite.app.AccountKeeper.GetAccount(suite.ctx, addr) + _ = acc.SetCoins(sdk.NewCoins(balance)) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + recipient := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + testCase := []struct { + name string + malleate func() + state types.StateTransition + expPass bool + }{ + { + "passing state transition", + func() {}, + types.StateTransition{ + AccountNonce: 123, + Price: big.NewInt(10), + GasLimit: 11, + Recipient: &recipient, + Amount: big.NewInt(50), + Payload: []byte("data"), + ChainID: big.NewInt(1), + Csdb: suite.stateDB, + TxHash: ðcmn.Hash{}, + Sender: suite.address, + Simulate: suite.ctx.IsCheckTx(), + }, + true, + }, + { + "contract creation", + func() {}, + types.StateTransition{ + AccountNonce: 123, + Price: big.NewInt(10), + GasLimit: 11, + Recipient: nil, + Amount: big.NewInt(10), + Payload: []byte("data"), + ChainID: big.NewInt(1), + Csdb: suite.stateDB, + TxHash: ðcmn.Hash{}, + Sender: suite.address, + Simulate: true, + }, + true, + }, + { + "state transition simulation", + func() {}, + types.StateTransition{ + AccountNonce: 123, + Price: big.NewInt(10), + GasLimit: 11, + Recipient: &recipient, + Amount: big.NewInt(10), + Payload: []byte("data"), + ChainID: big.NewInt(1), + Csdb: suite.stateDB, + TxHash: ðcmn.Hash{}, + Sender: suite.address, + Simulate: true, + }, + true, + }, + { + "fail by sending more than balance", + func() {}, + types.StateTransition{ + AccountNonce: 123, + Price: big.NewInt(10), + GasLimit: 11, + Recipient: &recipient, + Amount: big.NewInt(500000), + Payload: []byte("data"), + ChainID: big.NewInt(1), + Csdb: suite.stateDB, + TxHash: ðcmn.Hash{}, + Sender: suite.address, + Simulate: suite.ctx.IsCheckTx(), + }, + false, + }, + { + "nil gas price", + func() { + invalidGas := sdk.DecCoins{ + {Denom: ethermint.AttoPhoton}, + } + suite.ctx = suite.ctx.WithMinGasPrices(invalidGas) + }, + types.StateTransition{ + AccountNonce: 123, + Price: big.NewInt(10), + GasLimit: 11, + Recipient: &recipient, + Amount: big.NewInt(10), + Payload: []byte("data"), + ChainID: big.NewInt(1), + Csdb: suite.stateDB, + TxHash: ðcmn.Hash{}, + Sender: suite.address, + Simulate: suite.ctx.IsCheckTx(), + }, + false, + }, + } + + for _, tc := range testCase { + tc.malleate() + + _, err = tc.state.TransitionDb(suite.ctx, types.DefaultChainConfig()) + + if tc.expPass { + suite.Require().NoError(err, tc.name) + fromBalance := suite.app.EvmKeeper.GetBalance(suite.ctx, suite.address) + toBalance := suite.app.EvmKeeper.GetBalance(suite.ctx, recipient) + suite.Require().Equal(fromBalance, big.NewInt(4950), tc.name) + suite.Require().Equal(toBalance, big.NewInt(50), tc.name) + } else { + suite.Require().Error(err, tc.name) + } + } +} diff --git a/x/evm/types/statedb.go b/x/evm/types/statedb.go index ceda037545..94fa5b8f5a 100644 --- a/x/evm/types/statedb.go +++ b/x/evm/types/statedb.go @@ -6,21 +6,30 @@ import ( "sort" "sync" + "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/params" + + emint "github.com/cosmos/ethermint/types" ethcmn "github.com/ethereum/go-ethereum/common" ethstate "github.com/ethereum/go-ethereum/core/state" ethtypes "github.com/ethereum/go-ethereum/core/types" + ethvm "github.com/ethereum/go-ethereum/core/vm" ethcrypto "github.com/ethereum/go-ethereum/crypto" ) var ( - _ ethstate.StateDB = (*CommitStateDB)(nil) + _ ethvm.StateDB = (*CommitStateDB)(nil) zeroBalance = sdk.ZeroInt().BigInt() ) +type revision struct { + id int + journalIndex int +} + // CommitStateDB implements the Geth state.StateDB interface. Instead of using // a trie and database for querying and persistence, the Keeper uses KVStores // and an account mapper is used to facilitate state transitions. @@ -33,26 +42,27 @@ type CommitStateDB struct { // StateDB interface. Perhaps there is a better way. ctx sdk.Context - ak auth.AccountKeeper - storageKey sdk.StoreKey - codeKey sdk.StoreKey + storeKey sdk.StoreKey + paramSpace params.Subspace + accountKeeper AccountKeeper - // maps that hold 'live' objects, which will get modified while processing a + // array that hold 'live' objects, which will get modified while processing a // state transition - stateObjects map[ethcmn.Address]*stateObject - stateObjectsDirty map[ethcmn.Address]struct{} + stateObjects []stateEntry + addressToObjectIndex map[ethcmn.Address]int // map from address to the index of the state objects slice + stateObjectsDirty map[ethcmn.Address]struct{} // The refund counter, also used by state transitioning. refund uint64 thash, bhash ethcmn.Hash txIndex int - logs map[ethcmn.Hash][]*ethtypes.Log logSize uint // TODO: Determine if we actually need this as we do not need preimages in // the SDK, but it seems to be used elsewhere in Geth. - preimages map[ethcmn.Hash][]byte + preimages []preimageEntry + hashToPreimageIndex map[ethcmn.Hash]int // map from hash to the index of the preimages slice // DB error. // State objects are used by the consensus core and VM which are @@ -64,7 +74,7 @@ type CommitStateDB struct { // Journal of state modifications. This is the backbone of // Snapshot and RevertToSnapshot. journal *journal - validRevisions []ethstate.Revision + validRevisions []revision nextRevisionID int // mutex for state deep copying @@ -76,24 +86,38 @@ type CommitStateDB struct { // // CONTRACT: Stores used for state must be cache-wrapped as the ordering of the // key/value space matters in determining the merkle root. -func NewCommitStateDB(ctx sdk.Context, ak auth.AccountKeeper, storageKey, codeKey sdk.StoreKey) (*CommitStateDB, error) { +func NewCommitStateDB( + ctx sdk.Context, storeKey sdk.StoreKey, paramSpace params.Subspace, ak AccountKeeper, +) *CommitStateDB { return &CommitStateDB{ - ctx: ctx, - ak: ak, - storageKey: storageKey, - codeKey: codeKey, - stateObjects: make(map[ethcmn.Address]*stateObject), - stateObjectsDirty: make(map[ethcmn.Address]struct{}), - logs: make(map[ethcmn.Hash][]*ethtypes.Log), - preimages: make(map[ethcmn.Hash][]byte), - journal: newJournal(), - }, nil + ctx: ctx, + storeKey: storeKey, + paramSpace: paramSpace, + accountKeeper: ak, + stateObjects: []stateEntry{}, + addressToObjectIndex: make(map[ethcmn.Address]int), + stateObjectsDirty: make(map[ethcmn.Address]struct{}), + preimages: []preimageEntry{}, + hashToPreimageIndex: make(map[ethcmn.Hash]int), + journal: newJournal(), + } +} + +// WithContext returns a Database with an updated sdk context +func (csdb *CommitStateDB) WithContext(ctx sdk.Context) *CommitStateDB { + csdb.ctx = ctx + return csdb } // ---------------------------------------------------------------------------- // Setters // ---------------------------------------------------------------------------- +// SetParams sets the evm parameters to the param space. +func (csdb *CommitStateDB) SetParams(params Params) { + csdb.paramSpace.SetParamSet(csdb.ctx, ¶ms) +} + // SetBalance sets the balance of an account. func (csdb *CommitStateDB) SetBalance(addr ethcmn.Address, amount *big.Int) { so := csdb.GetOrNewStateObject(addr) @@ -142,6 +166,32 @@ func (csdb *CommitStateDB) SetCode(addr ethcmn.Address, code []byte) { } } +// ---------------------------------------------------------------------------- +// Transaction logs +// Required for upgrade logic or ease of querying. +// NOTE: we use BinaryLengthPrefixed since the tx logs are also included on Result data, +// which can't use BinaryBare. +// ---------------------------------------------------------------------------- + +// SetLogs sets the logs for a transaction in the KVStore. +func (csdb *CommitStateDB) SetLogs(hash ethcmn.Hash, logs []*ethtypes.Log) error { + store := prefix.NewStore(csdb.ctx.KVStore(csdb.storeKey), KeyPrefixLogs) + bz, err := MarshalLogs(logs) + if err != nil { + return err + } + + store.Set(hash.Bytes(), bz) + csdb.logSize = uint(len(logs)) + return nil +} + +// DeleteLogs removes the logs from the KVStore. It is used during journal.Revert. +func (csdb *CommitStateDB) DeleteLogs(hash ethcmn.Hash) { + store := prefix.NewStore(csdb.ctx.KVStore(csdb.storeKey), KeyPrefixLogs) + store.Delete(hash.Bytes()) +} + // AddLog adds a new log to the state and sets the log metadata from the state. func (csdb *CommitStateDB) AddLog(log *ethtypes.Log) { csdb.journal.append(addLogChange{txhash: csdb.thash}) @@ -150,18 +200,29 @@ func (csdb *CommitStateDB) AddLog(log *ethtypes.Log) { log.BlockHash = csdb.bhash log.TxIndex = uint(csdb.txIndex) log.Index = csdb.logSize - csdb.logs[csdb.thash] = append(csdb.logs[csdb.thash], log) - csdb.logSize++ + + logs, err := csdb.GetLogs(csdb.thash) + if err != nil { + // panic on unmarshal error + panic(err) + } + + if err = csdb.SetLogs(csdb.thash, append(logs, log)); err != nil { + // panic on marshal error + panic(err) + } } // AddPreimage records a SHA3 preimage seen by the VM. func (csdb *CommitStateDB) AddPreimage(hash ethcmn.Hash, preimage []byte) { - if _, ok := csdb.preimages[hash]; !ok { + if _, ok := csdb.hashToPreimageIndex[hash]; !ok { csdb.journal.append(addPreimageChange{hash: hash}) pi := make([]byte, len(preimage)) copy(pi, preimage) - csdb.preimages[hash] = pi + + csdb.preimages = append(csdb.preimages, preimageEntry{hash: hash, preimage: pi}) + csdb.hashToPreimageIndex[hash] = len(csdb.preimages) - 1 } } @@ -186,6 +247,12 @@ func (csdb *CommitStateDB) SubRefund(gas uint64) { // Getters // ---------------------------------------------------------------------------- +// GetParams returns the total set of evm parameters. +func (csdb *CommitStateDB) GetParams() (params Params) { + csdb.paramSpace.GetParamSet(csdb.ctx, ¶ms) + return params +} + // GetBalance retrieves the balance from the given address or 0 if object not // found. func (csdb *CommitStateDB) GetBalance(addr ethcmn.Address) *big.Int { @@ -207,6 +274,16 @@ func (csdb *CommitStateDB) GetNonce(addr ethcmn.Address) uint64 { return 0 } +// TxIndex returns the current transaction index set by Prepare. +func (csdb *CommitStateDB) TxIndex() int { + return csdb.txIndex +} + +// BlockHash returns the current block hash set by Prepare. +func (csdb *CommitStateDB) BlockHash() ethcmn.Hash { + return csdb.bhash +} + // GetCode returns the code for a given account. func (csdb *CommitStateDB) GetCode(addr ethcmn.Address) []byte { so := csdb.getStateObject(addr) @@ -228,7 +305,6 @@ func (csdb *CommitStateDB) GetCodeSize(addr ethcmn.Address) int { return len(so.code) } - // TODO: we may need to cache these lookups directly return len(so.Code(nil)) } @@ -263,19 +339,32 @@ func (csdb *CommitStateDB) GetCommittedState(addr ethcmn.Address, hash ethcmn.Ha return ethcmn.Hash{} } -// GetLogs returns the current logs for a given hash in the state. -func (csdb *CommitStateDB) GetLogs(hash ethcmn.Hash) []*ethtypes.Log { - return csdb.logs[hash] +// GetLogs returns the current logs for a given transaction hash from the KVStore. +func (csdb *CommitStateDB) GetLogs(hash ethcmn.Hash) ([]*ethtypes.Log, error) { + store := prefix.NewStore(csdb.ctx.KVStore(csdb.storeKey), KeyPrefixLogs) + bz := store.Get(hash.Bytes()) + if len(bz) == 0 { + // return nil error if logs are not found + return []*ethtypes.Log{}, nil + } + + return UnmarshalLogs(bz) } -// Logs returns all the current logs in the state. -func (csdb *CommitStateDB) Logs() []*ethtypes.Log { - var logs []*ethtypes.Log - for _, lgs := range csdb.logs { - logs = append(logs, lgs...) +// AllLogs returns all the current logs in the state. +func (csdb *CommitStateDB) AllLogs() []*ethtypes.Log { + store := csdb.ctx.KVStore(csdb.storeKey) + iterator := sdk.KVStorePrefixIterator(store, KeyPrefixLogs) + defer iterator.Close() + + allLogs := []*ethtypes.Log{} + for ; iterator.Valid(); iterator.Next() { + var logs []*ethtypes.Log + ModuleCdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &logs) + allLogs = append(allLogs, logs...) } - return logs + return allLogs } // GetRefund returns the current value of the refund counter. @@ -285,7 +374,12 @@ func (csdb *CommitStateDB) GetRefund() uint64 { // Preimages returns a list of SHA3 preimages that have been submitted. func (csdb *CommitStateDB) Preimages() map[ethcmn.Hash][]byte { - return csdb.preimages + preimages := map[ethcmn.Hash][]byte{} + + for _, pe := range csdb.preimages { + preimages[pe.hash] = pe.preimage + } + return preimages } // HasSuicided returns if the given account for the specified address has been @@ -313,50 +407,52 @@ func (csdb *CommitStateDB) StorageTrie(addr ethcmn.Address) ethstate.Trie { // in the cache, it will either be removed, or have it's code set and/or it's // state (storage) updated. In addition, the state object (account) itself will // be written. Finally, the root hash (version) will be returned. -func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (root ethcmn.Hash, err error) { +func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (ethcmn.Hash, error) { defer csdb.clearJournalAndRefund() // remove dirty state object entries based on the journal - for addr := range csdb.journal.dirties { - csdb.stateObjectsDirty[addr] = struct{}{} + for _, dirty := range csdb.journal.dirties { + csdb.stateObjectsDirty[dirty.address] = struct{}{} } // set the state objects - for addr, so := range csdb.stateObjects { - _, isDirty := csdb.stateObjectsDirty[addr] + for _, stateEntry := range csdb.stateObjects { + _, isDirty := csdb.stateObjectsDirty[stateEntry.address] switch { - case so.suicided || (isDirty && deleteEmptyObjects && so.empty()): + case stateEntry.stateObject.suicided || (isDirty && deleteEmptyObjects && stateEntry.stateObject.empty()): // If the state object has been removed, don't bother syncing it and just // remove it from the store. - csdb.deleteStateObject(so) + csdb.deleteStateObject(stateEntry.stateObject) case isDirty: // write any contract code associated with the state object - if so.code != nil && so.dirtyCode { - so.commitCode() - so.dirtyCode = false + if stateEntry.stateObject.code != nil && stateEntry.stateObject.dirtyCode { + stateEntry.stateObject.commitCode() + stateEntry.stateObject.dirtyCode = false } // update the object in the KVStore - csdb.updateStateObject(so) + if err := csdb.updateStateObject(stateEntry.stateObject); err != nil { + return ethcmn.Hash{}, err + } } - delete(csdb.stateObjectsDirty, addr) + delete(csdb.stateObjectsDirty, stateEntry.address) } // NOTE: Ethereum returns the trie merkle root here, but as commitment // actually happens in the BaseApp at EndBlocker, we do not know the root at // this time. - return + return ethcmn.Hash{}, nil } -// Finalize finalizes the state objects (accounts) state by setting their state, +// Finalise finalizes the state objects (accounts) state by setting their state, // removing the csdb destructed objects and clearing the journal as well as the // refunds. -func (csdb *CommitStateDB) Finalize(deleteEmptyObjects bool) { - for addr := range csdb.journal.dirties { - so, exist := csdb.stateObjects[addr] +func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) error { + for _, dirty := range csdb.journal.dirties { + idx, exist := csdb.addressToObjectIndex[dirty.address] if !exist { // ripeMD is 'touched' at block 1714175, in tx: // 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2 @@ -370,20 +466,24 @@ func (csdb *CommitStateDB) Finalize(deleteEmptyObjects bool) { continue } - if so.suicided || (deleteEmptyObjects && so.empty()) { - csdb.deleteStateObject(so) + stateEntry := csdb.stateObjects[idx] + if stateEntry.stateObject.suicided || (deleteEmptyObjects && stateEntry.stateObject.empty()) { + csdb.deleteStateObject(stateEntry.stateObject) } else { // Set all the dirty state storage items for the state object in the // KVStore and finally set the account in the account mapper. - so.commitState() - csdb.updateStateObject(so) + stateEntry.stateObject.commitState() + if err := csdb.updateStateObject(stateEntry.stateObject); err != nil { + return err + } } - csdb.stateObjectsDirty[addr] = struct{}{} + csdb.stateObjectsDirty[dirty.address] = struct{}{} } // invalidate journal because reverting across transactions is not allowed csdb.clearJournalAndRefund() + return nil } // IntermediateRoot returns the current root hash of the state. It is called in @@ -393,21 +493,42 @@ func (csdb *CommitStateDB) Finalize(deleteEmptyObjects bool) { // NOTE: The SDK has not concept or method of getting any intermediate merkle // root as commitment of the merkle-ized tree doesn't happen until the // BaseApps' EndBlocker. -func (csdb *CommitStateDB) IntermediateRoot(deleteEmptyObjects bool) ethcmn.Hash { - csdb.Finalize(deleteEmptyObjects) +func (csdb *CommitStateDB) IntermediateRoot(deleteEmptyObjects bool) (ethcmn.Hash, error) { + if err := csdb.Finalise(deleteEmptyObjects); err != nil { + return ethcmn.Hash{}, err + } - return ethcmn.Hash{} + return ethcmn.Hash{}, nil } // updateStateObject writes the given state object to the store. -func (csdb *CommitStateDB) updateStateObject(so *stateObject) { - csdb.ak.SetAccount(csdb.ctx, so.account) +func (csdb *CommitStateDB) updateStateObject(so *stateObject) error { + evmDenom := csdb.GetParams().EvmDenom + // NOTE: we don't use sdk.NewCoin here to avoid panic on test importer's genesis + newBalance := sdk.Coin{Denom: evmDenom, Amount: sdk.NewIntFromBigInt(so.Balance())} + if !newBalance.IsValid() { + return fmt.Errorf("invalid balance %s", newBalance) + } + + coins := so.account.GetCoins() + balance := coins.AmountOf(newBalance.Denom) + if balance.IsZero() || !balance.Equal(newBalance.Amount) { + coins = coins.Add(newBalance) + } + + if err := so.account.SetCoins(coins); err != nil { + return err + } + + csdb.accountKeeper.SetAccount(csdb.ctx, so.account) + // return csdb.bankKeeper.SetBalance(csdb.ctx, so.account.Address, newBalance) + return nil } // deleteStateObject removes the given state object from the state store. func (csdb *CommitStateDB) deleteStateObject(so *stateObject) { so.deleted = true - csdb.ak.RemoveAccount(csdb.ctx, so.account) + csdb.accountKeeper.RemoveAccount(csdb.ctx, so.account) } // ---------------------------------------------------------------------------- @@ -421,9 +542,9 @@ func (csdb *CommitStateDB) Snapshot() int { csdb.validRevisions = append( csdb.validRevisions, - ethstate.Revision{ - ID: id, - JournalIndex: csdb.journal.length(), + revision{ + id: id, + journalIndex: csdb.journal.length(), }, ) @@ -434,14 +555,14 @@ func (csdb *CommitStateDB) Snapshot() int { func (csdb *CommitStateDB) RevertToSnapshot(revID int) { // find the snapshot in the stack of valid snapshots idx := sort.Search(len(csdb.validRevisions), func(i int) bool { - return csdb.validRevisions[i].ID >= revID + return csdb.validRevisions[i].id >= revID }) - if idx == len(csdb.validRevisions) || csdb.validRevisions[idx].ID != revID { + if idx == len(csdb.validRevisions) || csdb.validRevisions[idx].id != revID { panic(fmt.Errorf("revision ID %v cannot be reverted", revID)) } - snapshot := csdb.validRevisions[idx].JournalIndex + snapshot := csdb.validRevisions[idx].journalIndex // replay the journal to undo changes and remove invalidated snapshots csdb.journal.revert(csdb, snapshot) @@ -501,20 +622,50 @@ func (csdb *CommitStateDB) Suicide(addr ethcmn.Address) bool { // Reset clears out all ephemeral state objects from the state db, but keeps // the underlying account mapper and store keys to avoid reloading data for the // next operations. -func (csdb *CommitStateDB) Reset(root ethcmn.Hash) error { - csdb.stateObjects = make(map[ethcmn.Address]*stateObject) +func (csdb *CommitStateDB) Reset(_ ethcmn.Hash) error { + csdb.stateObjects = []stateEntry{} + csdb.addressToObjectIndex = make(map[ethcmn.Address]int) csdb.stateObjectsDirty = make(map[ethcmn.Address]struct{}) csdb.thash = ethcmn.Hash{} csdb.bhash = ethcmn.Hash{} csdb.txIndex = 0 - csdb.logs = make(map[ethcmn.Hash][]*ethtypes.Log) csdb.logSize = 0 - csdb.preimages = make(map[ethcmn.Hash][]byte) + csdb.preimages = []preimageEntry{} + csdb.hashToPreimageIndex = make(map[ethcmn.Hash]int) csdb.clearJournalAndRefund() return nil } +// UpdateAccounts updates the nonce and coin balances of accounts +func (csdb *CommitStateDB) UpdateAccounts() { + for _, stateEntry := range csdb.stateObjects { + currAcc := csdb.accountKeeper.GetAccount(csdb.ctx, sdk.AccAddress(stateEntry.address.Bytes())) + emintAcc, ok := currAcc.(*emint.EthAccount) + if !ok { + continue + } + + evmDenom := csdb.GetParams().EvmDenom + balance := sdk.Coin{ + Denom: evmDenom, + Amount: emintAcc.GetCoins().AmountOf(evmDenom), + } + + if stateEntry.stateObject.Balance() != balance.Amount.BigInt() && balance.IsValid() || + stateEntry.stateObject.Nonce() != emintAcc.GetSequence() { + stateEntry.stateObject.account = emintAcc + } + } +} + +// ClearStateObjects clears cache of state objects to handle account changes outside of the EVM +func (csdb *CommitStateDB) ClearStateObjects() { + csdb.stateObjects = []stateEntry{} + csdb.addressToObjectIndex = make(map[ethcmn.Address]int) + csdb.stateObjectsDirty = make(map[ethcmn.Address]struct{}) +} + func (csdb *CommitStateDB) clearJournalAndRefund() { csdb.journal = newJournal() csdb.validRevisions = csdb.validRevisions[:0] @@ -542,42 +693,48 @@ func (csdb *CommitStateDB) Prepare(thash, bhash ethcmn.Hash, txi int) { func (csdb *CommitStateDB) CreateAccount(addr ethcmn.Address) { newobj, prevobj := csdb.createObject(addr) if prevobj != nil { - newobj.setBalance(sdk.NewIntFromBigInt(prevobj.Balance())) + evmDenom := csdb.GetParams().EvmDenom + newobj.setBalance(evmDenom, sdk.NewIntFromBigInt(prevobj.Balance())) } } // Copy creates a deep, independent copy of the state. // // NOTE: Snapshots of the copied state cannot be applied to the copy. -func (csdb *CommitStateDB) Copy() ethstate.StateDB { +func (csdb *CommitStateDB) Copy() *CommitStateDB { csdb.lock.Lock() defer csdb.lock.Unlock() // copy all the basic fields, initialize the memory ones state := &CommitStateDB{ - ctx: csdb.ctx, - ak: csdb.ak, - storageKey: csdb.storageKey, - codeKey: csdb.codeKey, - stateObjects: make(map[ethcmn.Address]*stateObject, len(csdb.journal.dirties)), - stateObjectsDirty: make(map[ethcmn.Address]struct{}, len(csdb.journal.dirties)), - refund: csdb.refund, - logs: make(map[ethcmn.Hash][]*ethtypes.Log, len(csdb.logs)), - logSize: csdb.logSize, - preimages: make(map[ethcmn.Hash][]byte), - journal: newJournal(), + ctx: csdb.ctx, + storeKey: csdb.storeKey, + paramSpace: csdb.paramSpace, + accountKeeper: csdb.accountKeeper, + stateObjects: []stateEntry{}, + addressToObjectIndex: make(map[ethcmn.Address]int), + stateObjectsDirty: make(map[ethcmn.Address]struct{}), + refund: csdb.refund, + logSize: csdb.logSize, + preimages: make([]preimageEntry, len(csdb.preimages)), + hashToPreimageIndex: make(map[ethcmn.Hash]int, len(csdb.hashToPreimageIndex)), + journal: newJournal(), } // copy the dirty states, logs, and preimages - for addr := range csdb.journal.dirties { + for _, dirty := range csdb.journal.dirties { // There is a case where an object is in the journal but not in the // stateObjects: OOG after touch on ripeMD prior to Byzantium. Thus, we // need to check for nil. // // Ref: https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527 - if object, exist := csdb.stateObjects[addr]; exist { - state.stateObjects[addr] = object.deepCopy(state) - state.stateObjectsDirty[addr] = struct{}{} + if idx, exist := csdb.addressToObjectIndex[dirty.address]; exist { + state.stateObjects = append(state.stateObjects, stateEntry{ + address: dirty.address, + stateObject: csdb.stateObjects[idx].stateObject.deepCopy(state), + }) + state.addressToObjectIndex[dirty.address] = len(state.stateObjects) - 1 + state.stateObjectsDirty[dirty.address] = struct{}{} } } @@ -585,59 +742,59 @@ func (csdb *CommitStateDB) Copy() ethstate.StateDB { // copied, the loop above will be a no-op, since the copy's journal is empty. // Thus, here we iterate over stateObjects, to enable copies of copies. for addr := range csdb.stateObjectsDirty { - if _, exist := state.stateObjects[addr]; !exist { - state.stateObjects[addr] = csdb.stateObjects[addr].deepCopy(state) + if idx, exist := state.addressToObjectIndex[addr]; !exist { + state.setStateObject(csdb.stateObjects[idx].stateObject.deepCopy(state)) state.stateObjectsDirty[addr] = struct{}{} } } - // copy logs - for hash, logs := range csdb.logs { - cpy := make([]*ethtypes.Log, len(logs)) - for i, l := range logs { - cpy[i] = new(ethtypes.Log) - *cpy[i] = *l - } - state.logs[hash] = cpy - } - // copy pre-images - for hash, preimage := range csdb.preimages { - state.preimages[hash] = preimage + for i, preimageEntry := range csdb.preimages { + state.preimages[i] = preimageEntry + state.hashToPreimageIndex[preimageEntry.hash] = i } return state } -// ForEachStorage iterates over each storage items, all invokes the provided -// callback on each key, value pair . -func (csdb *CommitStateDB) ForEachStorage(addr ethcmn.Address, cb func(key, value ethcmn.Hash) bool) { +// ForEachStorage iterates over each storage items, all invoke the provided +// callback on each key, value pair. +func (csdb *CommitStateDB) ForEachStorage(addr ethcmn.Address, cb func(key, value ethcmn.Hash) (stop bool)) error { so := csdb.getStateObject(addr) if so == nil { - return + return nil } - store := csdb.ctx.KVStore(csdb.storageKey) - iter := sdk.KVStorePrefixIterator(store, so.Address().Bytes()) + store := csdb.ctx.KVStore(csdb.storeKey) + prefix := AddressStoragePrefix(so.Address()) + iterator := sdk.KVStorePrefixIterator(store, prefix) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + key := ethcmn.BytesToHash(iterator.Key()) + value := ethcmn.BytesToHash(iterator.Value()) - for ; iter.Valid(); iter.Next() { - key := ethcmn.BytesToHash(iter.Key()) - value := iter.Value() + if idx, dirty := so.keyToDirtyStorageIndex[key]; dirty { + // check if iteration stops + if cb(key, so.dirtyStorage[idx].Value) { + break + } - if value, dirty := so.dirtyStorage[key]; dirty { - cb(key, value) continue } - cb(key, ethcmn.BytesToHash(value)) + // check if iteration stops + if cb(key, value) { + return nil + } } - iter.Close() + return nil } // GetOrNewStateObject retrieves a state object or create a new state object if // nil. -func (csdb *CommitStateDB) GetOrNewStateObject(addr ethcmn.Address) ethstate.StateObject { +func (csdb *CommitStateDB) GetOrNewStateObject(addr ethcmn.Address) StateObject { so := csdb.getStateObject(addr) if so == nil || so.deleted { so, _ = csdb.createObject(addr) @@ -651,8 +808,9 @@ func (csdb *CommitStateDB) GetOrNewStateObject(addr ethcmn.Address) ethstate.Sta func (csdb *CommitStateDB) createObject(addr ethcmn.Address) (newObj, prevObj *stateObject) { prevObj = csdb.getStateObject(addr) - acc := csdb.ak.NewAccountWithAddress(csdb.ctx, sdk.AccAddress(addr.Bytes())) - newObj = newObject(csdb, acc) + acc := csdb.accountKeeper.NewAccountWithAddress(csdb.ctx, sdk.AccAddress(addr.Bytes())) + + newObj = newStateObject(csdb, acc) newObj.setNonce(0) // sets the object to dirty if prevObj == nil { @@ -675,29 +833,57 @@ func (csdb *CommitStateDB) setError(err error) { // getStateObject attempts to retrieve a state object given by the address. // Returns nil and sets an error if not found. func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *stateObject) { - // prefer 'live' (cached) objects - if so := csdb.stateObjects[addr]; so != nil { - if so.deleted { - return nil - } + if idx, found := csdb.addressToObjectIndex[addr]; found { + // prefer 'live' (cached) objects + if so := csdb.stateObjects[idx].stateObject; so != nil { + if so.deleted { + return nil + } - return so + return so + } } // otherwise, attempt to fetch the account from the account mapper - acc := csdb.ak.GetAccount(csdb.ctx, addr.Bytes()) + acc := csdb.accountKeeper.GetAccount(csdb.ctx, sdk.AccAddress(addr.Bytes())) if acc == nil { - csdb.setError(fmt.Errorf("no account found for address: %X", addr.Bytes())) + csdb.setError(fmt.Errorf("no account found for address: %s", addr.String())) return nil } // insert the state object into the live set - so := newObject(csdb, acc) + so := newStateObject(csdb, acc) csdb.setStateObject(so) return so } func (csdb *CommitStateDB) setStateObject(so *stateObject) { - csdb.stateObjects[so.Address()] = so + if idx, found := csdb.addressToObjectIndex[so.Address()]; found { + // update the existing object + csdb.stateObjects[idx].stateObject = so + return + } + + // append the new state object to the stateObjects slice + se := stateEntry{ + address: so.Address(), + stateObject: so, + } + + csdb.stateObjects = append(csdb.stateObjects, se) + csdb.addressToObjectIndex[se.address] = len(csdb.stateObjects) - 1 +} + +// RawDump returns a raw state dump. +// +// TODO: Implement if we need it, especially for the RPC API. +func (csdb *CommitStateDB) RawDump() ethstate.Dump { + return ethstate.Dump{} +} + +type preimageEntry struct { + // hash key of the preimage entry + hash ethcmn.Hash + preimage []byte } diff --git a/x/evm/types/statedb_test.go b/x/evm/types/statedb_test.go new file mode 100644 index 0000000000..8904e7a243 --- /dev/null +++ b/x/evm/types/statedb_test.go @@ -0,0 +1,697 @@ +package types_test + +import ( + "fmt" + "math/big" + "testing" + + "github.com/stretchr/testify/suite" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + + ethcmn "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + ethcrypto "github.com/ethereum/go-ethereum/crypto" + + "github.com/cosmos/ethermint/app" + "github.com/cosmos/ethermint/crypto" + ethermint "github.com/cosmos/ethermint/types" + "github.com/cosmos/ethermint/x/evm/types" + + abci "github.com/tendermint/tendermint/abci/types" +) + +type StateDBTestSuite struct { + suite.Suite + + ctx sdk.Context + app *app.EthermintApp + stateDB *types.CommitStateDB + address ethcmn.Address + stateObject types.StateObject +} + +func TestStateDBTestSuite(t *testing.T) { + suite.Run(t, new(StateDBTestSuite)) +} + +func (suite *StateDBTestSuite) SetupTest() { + checkTx := false + + suite.app = app.Setup(checkTx) + suite.ctx = suite.app.BaseApp.NewContext(checkTx, abci.Header{Height: 1}) + suite.stateDB = suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx) + + privkey, err := crypto.GenerateKey() + suite.Require().NoError(err) + + suite.address = ethcmn.BytesToAddress(privkey.PubKey().Address().Bytes()) + + balance := sdk.NewCoins(ethermint.NewPhotonCoin(sdk.ZeroInt())) + acc := ðermint.EthAccount{ + BaseAccount: auth.NewBaseAccount(sdk.AccAddress(suite.address.Bytes()), balance, nil, 0, 0), + CodeHash: ethcrypto.Keccak256(nil), + } + + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + suite.stateObject = suite.stateDB.GetOrNewStateObject(suite.address) +} + +func (suite *StateDBTestSuite) TestParams() { + params := suite.stateDB.GetParams() + suite.Require().Equal(types.DefaultParams(), params) + params.EvmDenom = "ara" + suite.stateDB.SetParams(params) + newParams := suite.stateDB.GetParams() + suite.Require().Equal(newParams, params) +} + +func (suite *StateDBTestSuite) TestBloomFilter() { + // Prepare db for logs + tHash := ethcmn.BytesToHash([]byte{0x1}) + suite.stateDB.Prepare(tHash, ethcmn.Hash{}, 0) + contractAddress := ethcmn.BigToAddress(big.NewInt(1)) + log := ethtypes.Log{Address: contractAddress} + + testCase := []struct { + name string + malleate func() + numLogs int + isBloom bool + }{ + { + "no logs", + func() {}, + 0, + false, + }, + { + "add log", + func() { + suite.stateDB.AddLog(&log) + }, + 1, + false, + }, + { + "bloom", + func() {}, + 0, + true, + }, + } + + for _, tc := range testCase { + tc.malleate() + logs, err := suite.stateDB.GetLogs(tHash) + if !tc.isBloom { + suite.Require().NoError(err, tc.name) + suite.Require().Len(logs, tc.numLogs, tc.name) + if len(logs) != 0 { + suite.Require().Equal(log, *logs[0], tc.name) + } + } else { + // get logs bloom from the log + bloomInt := ethtypes.LogsBloom(logs) + bloomFilter := ethtypes.BytesToBloom(bloomInt.Bytes()) + suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress), tc.name) + suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))), tc.name) + } + } +} + +func (suite *StateDBTestSuite) TestStateDB_Balance() { + testCase := []struct { + name string + malleate func() + balance *big.Int + }{ + { + "set balance", + func() { + suite.stateDB.SetBalance(suite.address, big.NewInt(100)) + }, + big.NewInt(100), + }, + { + "sub balance", + func() { + suite.stateDB.SubBalance(suite.address, big.NewInt(100)) + }, + big.NewInt(0), + }, + { + "add balance", + func() { + suite.stateDB.AddBalance(suite.address, big.NewInt(200)) + }, + big.NewInt(200), + }, + } + + for _, tc := range testCase { + tc.malleate() + suite.Require().Equal(tc.balance, suite.stateDB.GetBalance(suite.address), tc.name) + } +} + +func (suite *StateDBTestSuite) TestStateDBNonce() { + nonce := uint64(123) + suite.stateDB.SetNonce(suite.address, nonce) + suite.Require().Equal(nonce, suite.stateDB.GetNonce(suite.address)) +} + +func (suite *StateDBTestSuite) TestStateDB_Error() { + nonce := suite.stateDB.GetNonce(ethcmn.Address{}) + suite.Require().Equal(0, int(nonce)) + suite.Require().Error(suite.stateDB.Error()) +} + +func (suite *StateDBTestSuite) TestStateDB_Database() { + suite.Require().Nil(suite.stateDB.Database()) +} + +func (suite *StateDBTestSuite) TestStateDB_State() { + key := ethcmn.BytesToHash([]byte("foo")) + val := ethcmn.BytesToHash([]byte("bar")) + suite.stateDB.SetState(suite.address, key, val) + + testCase := []struct { + name string + address ethcmn.Address + key ethcmn.Hash + value ethcmn.Hash + }{ + { + "found state", + suite.address, + ethcmn.BytesToHash([]byte("foo")), + ethcmn.BytesToHash([]byte("bar")), + }, + { + "state not found", + suite.address, + ethcmn.BytesToHash([]byte("key")), + ethcmn.Hash{}, + }, + { + "object not found", + ethcmn.Address{}, + ethcmn.BytesToHash([]byte("foo")), + ethcmn.Hash{}, + }, + } + for _, tc := range testCase { + value := suite.stateDB.GetState(tc.address, tc.key) + suite.Require().Equal(tc.value, value, tc.name) + } +} + +func (suite *StateDBTestSuite) TestStateDB_Code() { + testCase := []struct { + name string + address ethcmn.Address + code []byte + malleate func() + }{ + { + "no stored code for state object", + suite.address, + nil, + func() {}, + }, + { + "existing address", + suite.address, + []byte("code"), + func() { + suite.stateDB.SetCode(suite.address, []byte("code")) + }, + }, + { + "state object not found", + ethcmn.Address{}, + nil, + func() {}, + }, + } + + for _, tc := range testCase { + tc.malleate() + + suite.Require().Equal(tc.code, suite.stateDB.GetCode(tc.address), tc.name) + suite.Require().Equal(len(tc.code), suite.stateDB.GetCodeSize(tc.address), tc.name) + } +} + +func (suite *StateDBTestSuite) TestStateDB_Logs() { + testCase := []struct { + name string + log ethtypes.Log + }{ + { + "state db log", + ethtypes.Log{ + Address: suite.address, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.Hash{}, + TxIndex: 1, + BlockHash: ethcmn.Hash{}, + Index: 1, + Removed: false, + }, + }, + } + + for _, tc := range testCase { + hash := ethcmn.BytesToHash([]byte("hash")) + logs := []*ethtypes.Log{&tc.log} + + err := suite.stateDB.SetLogs(hash, logs) + suite.Require().NoError(err, tc.name) + dbLogs, err := suite.stateDB.GetLogs(hash) + suite.Require().NoError(err, tc.name) + suite.Require().Equal(logs, dbLogs, tc.name) + + suite.stateDB.DeleteLogs(hash) + dbLogs, err = suite.stateDB.GetLogs(hash) + suite.Require().NoError(err, tc.name) + suite.Require().Empty(dbLogs, tc.name) + + suite.stateDB.AddLog(&tc.log) + suite.Require().Equal(logs, suite.stateDB.AllLogs(), tc.name) + + //resets state but checking to see if storekey still persists. + err = suite.stateDB.Reset(hash) + suite.Require().NoError(err, tc.name) + suite.Require().Equal(logs, suite.stateDB.AllLogs(), tc.name) + } +} + +func (suite *StateDBTestSuite) TestStateDB_Preimage() { + hash := ethcmn.BytesToHash([]byte("hash")) + preimage := []byte("preimage") + + suite.stateDB.AddPreimage(hash, preimage) + suite.Require().Equal(preimage, suite.stateDB.Preimages()[hash]) +} + +func (suite *StateDBTestSuite) TestStateDB_Refund() { + testCase := []struct { + name string + addAmount uint64 + subAmount uint64 + expRefund uint64 + expPanic bool + }{ + { + "refund 0", + 0, 0, 0, + false, + }, + { + "refund positive amount", + 100, 0, 100, + false, + }, + { + "refund panic", + 100, 200, 100, + true, + }, + } + + for _, tc := range testCase { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + + suite.stateDB.AddRefund(tc.addAmount) + suite.Require().Equal(tc.addAmount, suite.stateDB.GetRefund()) + + if tc.expPanic { + suite.Panics(func() { + suite.stateDB.SubRefund(tc.subAmount) + }) + } else { + suite.stateDB.SubRefund(tc.subAmount) + suite.Require().Equal(tc.expRefund, suite.stateDB.GetRefund()) + } + }) + } +} + +func (suite *StateDBTestSuite) TestStateDB_CreateAccount() { + prevBalance := big.NewInt(12) + + testCase := []struct { + name string + address ethcmn.Address + malleate func() + }{ + { + "existing account", + suite.address, + func() { + suite.stateDB.AddBalance(suite.address, prevBalance) + }, + }, + { + "new account", + ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b4c1"), + func() { + prevBalance = big.NewInt(0) + }, + }, + } + + for _, tc := range testCase { + tc.malleate() + + suite.stateDB.CreateAccount(tc.address) + suite.Require().True(suite.stateDB.Exist(tc.address), tc.name) + suite.Require().Equal(prevBalance, suite.stateDB.GetBalance(tc.address), tc.name) + } +} + +func (suite *StateDBTestSuite) TestStateDB_ClearStateObj() { + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + suite.stateDB.CreateAccount(addr) + suite.Require().True(suite.stateDB.Exist(addr)) + + suite.stateDB.ClearStateObjects() + suite.Require().False(suite.stateDB.Exist(addr)) +} + +func (suite *StateDBTestSuite) TestStateDB_Reset() { + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + + suite.stateDB.CreateAccount(addr) + suite.Require().True(suite.stateDB.Exist(addr)) + + err = suite.stateDB.Reset(ethcmn.BytesToHash(nil)) + suite.Require().NoError(err) + suite.Require().False(suite.stateDB.Exist(addr)) +} + +func (suite *StateDBTestSuite) TestSuiteDB_Prepare() { + thash := ethcmn.BytesToHash([]byte("thash")) + bhash := ethcmn.BytesToHash([]byte("bhash")) + txi := 1 + + suite.stateDB.Prepare(thash, bhash, txi) + + suite.Require().Equal(txi, suite.stateDB.TxIndex()) + suite.Require().Equal(bhash, suite.stateDB.BlockHash()) +} + +func (suite *StateDBTestSuite) TestSuiteDB_CopyState() { + testCase := []struct { + name string + log ethtypes.Log + }{ + { + "copy state", + ethtypes.Log{ + Address: suite.address, + Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Data: []byte("data"), + BlockNumber: 1, + TxHash: ethcmn.Hash{}, + TxIndex: 1, + BlockHash: ethcmn.Hash{}, + Index: 1, + Removed: false, + }, + }, + } + + for _, tc := range testCase { + hash := ethcmn.BytesToHash([]byte("hash")) + logs := []*ethtypes.Log{&tc.log} + + err := suite.stateDB.SetLogs(hash, logs) + suite.Require().NoError(err, tc.name) + + copyDB := suite.stateDB.Copy() + + copiedDBLogs, err := copyDB.GetLogs(hash) + suite.Require().NoError(err, tc.name) + suite.Require().Equal(logs, copiedDBLogs, tc.name) + suite.Require().Equal(suite.stateDB.Exist(suite.address), copyDB.Exist(suite.address), tc.name) + } +} + +func (suite *StateDBTestSuite) TestSuiteDB_Empty() { + suite.Require().True(suite.stateDB.Empty(suite.address)) + + suite.stateDB.SetBalance(suite.address, big.NewInt(100)) + suite.Require().False(suite.stateDB.Empty(suite.address)) +} + +func (suite *StateDBTestSuite) TestSuiteDB_Suicide() { + testCase := []struct { + name string + amount *big.Int + expPass bool + delete bool + }{ + { + "suicide zero balance", + big.NewInt(0), + false, false, + }, + { + "suicide with balance", + big.NewInt(100), + true, false, + }, + { + "delete", + big.NewInt(0), + true, true, + }, + } + + for _, tc := range testCase { + if tc.delete { + _, err := suite.stateDB.Commit(tc.delete) + suite.Require().NoError(err, tc.name) + suite.Require().False(suite.stateDB.Exist(suite.address), tc.name) + continue + } + + if tc.expPass { + suite.stateDB.SetBalance(suite.address, tc.amount) + suicide := suite.stateDB.Suicide(suite.address) + suite.Require().True(suicide, tc.name) + suite.Require().True(suite.stateDB.HasSuicided(suite.address), tc.name) + } else { + //Suicide only works for an account with non-zero balance/nonce + priv, err := crypto.GenerateKey() + suite.Require().NoError(err) + + addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) + suicide := suite.stateDB.Suicide(addr) + suite.Require().False(suicide, tc.name) + suite.Require().False(suite.stateDB.HasSuicided(addr), tc.name) + } + } +} + +func (suite *StateDBTestSuite) TestCommitStateDB_Commit() { + testCase := []struct { + name string + malleate func() + deleteObjs bool + expPass bool + }{ + { + "commit suicided", + func() { + ok := suite.stateDB.Suicide(suite.address) + suite.Require().True(ok) + }, + true, true, + }, + { + "commit with dirty value", + func() { + suite.stateDB.SetCode(suite.address, []byte("code")) + }, + false, true, + }, + } + + for _, tc := range testCase { + tc.malleate() + + hash, err := suite.stateDB.Commit(tc.deleteObjs) + suite.Require().Equal(ethcmn.Hash{}, hash) + + if !tc.expPass { + suite.Require().Error(err, tc.name) + continue + } + + suite.Require().NoError(err, tc.name) + acc := suite.app.AccountKeeper.GetAccount(suite.ctx, sdk.AccAddress(suite.address.Bytes())) + + if tc.deleteObjs { + suite.Require().Nil(acc, tc.name) + continue + } + + suite.Require().NotNil(acc, tc.name) + ethAcc, ok := acc.(*ethermint.EthAccount) + suite.Require().True(ok) + suite.Require().Equal(ethcrypto.Keccak256([]byte("code")), ethAcc.CodeHash) + } +} + +func (suite *StateDBTestSuite) TestCommitStateDB_Finalize() { + testCase := []struct { + name string + malleate func() + deleteObjs bool + expPass bool + }{ + { + "finalize suicided", + func() { + ok := suite.stateDB.Suicide(suite.address) + suite.Require().True(ok) + }, + true, true, + }, + { + "finalize, not suicided", + func() { + suite.stateDB.AddBalance(suite.address, big.NewInt(5)) + }, + false, true, + }, + { + "finalize, dirty storage", + func() { + suite.stateDB.SetState(suite.address, ethcmn.BytesToHash([]byte("key")), ethcmn.BytesToHash([]byte("value"))) + }, + false, true, + }, + } + + for _, tc := range testCase { + tc.malleate() + + err := suite.stateDB.Finalise(tc.deleteObjs) + + if !tc.expPass { + suite.Require().Error(err, tc.name) + hash := suite.stateDB.GetCommittedState(suite.address, ethcmn.BytesToHash([]byte("key"))) + suite.Require().NotEqual(ethcmn.Hash{}, hash, tc.name) + continue + } + + suite.Require().NoError(err, tc.name) + acc := suite.app.AccountKeeper.GetAccount(suite.ctx, sdk.AccAddress(suite.address.Bytes())) + + if tc.deleteObjs { + suite.Require().Nil(acc, tc.name) + continue + } + + suite.Require().NotNil(acc, tc.name) + } +} +func (suite *StateDBTestSuite) TestCommitStateDB_GetCommittedState() { + hash := suite.stateDB.GetCommittedState(ethcmn.Address{}, ethcmn.BytesToHash([]byte("key"))) + suite.Require().Equal(ethcmn.Hash{}, hash) +} + +func (suite *StateDBTestSuite) TestCommitStateDB_Snapshot() { + id := suite.stateDB.Snapshot() + suite.Require().NotPanics(func() { + suite.stateDB.RevertToSnapshot(id) + }) + + suite.Require().Panics(func() { + suite.stateDB.RevertToSnapshot(-1) + }, "invalid revision should panic") +} + +func (suite *StateDBTestSuite) TestCommitStateDB_ForEachStorage() { + var storage types.Storage + + testCase := []struct { + name string + malleate func() + callback func(key, value ethcmn.Hash) (stop bool) + expValues []ethcmn.Hash + }{ + { + "aggregate state", + func() { + for i := 0; i < 5; i++ { + suite.stateDB.SetState(suite.address, ethcmn.BytesToHash([]byte(fmt.Sprintf("key%d", i))), ethcmn.BytesToHash([]byte(fmt.Sprintf("value%d", i)))) + } + }, + func(key, value ethcmn.Hash) bool { + storage = append(storage, types.NewState(key, value)) + return false + }, + []ethcmn.Hash{ + ethcmn.BytesToHash([]byte("value0")), + ethcmn.BytesToHash([]byte("value1")), + ethcmn.BytesToHash([]byte("value2")), + ethcmn.BytesToHash([]byte("value3")), + ethcmn.BytesToHash([]byte("value4")), + }, + }, + { + "filter state", + func() { + suite.stateDB.SetState(suite.address, ethcmn.BytesToHash([]byte("key")), ethcmn.BytesToHash([]byte("value"))) + suite.stateDB.SetState(suite.address, ethcmn.BytesToHash([]byte("filterkey")), ethcmn.BytesToHash([]byte("filtervalue"))) + }, + func(key, value ethcmn.Hash) bool { + if value == ethcmn.BytesToHash([]byte("filtervalue")) { + storage = append(storage, types.NewState(key, value)) + return true + } + return false + }, + []ethcmn.Hash{ + ethcmn.BytesToHash([]byte("filtervalue")), + }, + }, + } + + for _, tc := range testCase { + suite.Run(tc.name, func() { + suite.SetupTest() // reset + tc.malleate() + suite.stateDB.Finalise(false) + + err := suite.stateDB.ForEachStorage(suite.address, tc.callback) + suite.Require().NoError(err) + suite.Require().Equal(len(tc.expValues), len(storage), fmt.Sprintf("Expected values:\n%v\nStorage Values\n%v", tc.expValues, storage)) + + vals := make([]ethcmn.Hash, len(storage)) + for i := range storage { + vals[i] = storage[i].Value + } + + suite.Require().ElementsMatch(tc.expValues, vals) + }) + storage = types.Storage{} + } +} diff --git a/x/evm/types/storage.go b/x/evm/types/storage.go new file mode 100644 index 0000000000..cf7afaf27e --- /dev/null +++ b/x/evm/types/storage.go @@ -0,0 +1,72 @@ +package types + +import ( + "bytes" + "fmt" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + ethcmn "github.com/ethereum/go-ethereum/common" +) + +// Storage represents the account Storage map as a slice of single key value +// State pairs. This is to prevent non determinism at genesis initialization or export. +type Storage []State + +// Validate performs a basic validation of the Storage fields. +func (s Storage) Validate() error { + seenStorage := make(map[string]bool) + for i, state := range s { + if seenStorage[state.Key.String()] { + return sdkerrors.Wrapf(ErrInvalidState, "duplicate state key %d", i) + } + + if err := state.Validate(); err != nil { + return err + } + + seenStorage[state.Key.String()] = true + } + return nil +} + +// String implements the stringer interface +func (s Storage) String() string { + var str string + for _, state := range s { + str += fmt.Sprintf("%s: %s\n", state.Key.String(), state.Value.String()) + } + + return str +} + +// Copy returns a copy of storage. +func (s Storage) Copy() Storage { + cpy := make(Storage, len(s)) + copy(cpy, s) + + return cpy +} + +// State represents a single Storage key value pair item. +type State struct { + Key ethcmn.Hash `json:"key"` + Value ethcmn.Hash `json:"value"` +} + +// Validate performs a basic validation of the State fields. +func (s State) Validate() error { + if bytes.Equal(s.Key.Bytes(), ethcmn.Hash{}.Bytes()) { + return sdkerrors.Wrap(ErrInvalidState, "state key hash cannot be empty") + } + // NOTE: state value can be empty + return nil +} + +// NewState creates a new State instance +func NewState(key, value ethcmn.Hash) State { + return State{ + Key: key, + Value: value, + } +} diff --git a/x/evm/types/storage_test.go b/x/evm/types/storage_test.go new file mode 100644 index 0000000000..b230026797 --- /dev/null +++ b/x/evm/types/storage_test.go @@ -0,0 +1,84 @@ +package types + +import ( + "testing" + + ethcmn "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestStorageValidate(t *testing.T) { + testCases := []struct { + name string + storage Storage + expPass bool + }{ + { + "valid storage", + Storage{ + NewState(ethcmn.BytesToHash([]byte{1, 2, 3}), ethcmn.BytesToHash([]byte{1, 2, 3})), + }, + true, + }, + { + "empty storage key bytes", + Storage{ + {Key: ethcmn.Hash{}}, + }, + false, + }, + { + "duplicated storage key", + Storage{ + {Key: ethcmn.BytesToHash([]byte{1, 2, 3})}, + {Key: ethcmn.BytesToHash([]byte{1, 2, 3})}, + }, + false, + }, + } + + for _, tc := range testCases { + tc := tc + err := tc.storage.Validate() + if tc.expPass { + require.NoError(t, err, tc.name) + } else { + require.Error(t, err, tc.name) + } + } +} + +func TestStorageCopy(t *testing.T) { + testCases := []struct { + name string + storage Storage + }{ + { + "single storage", + Storage{ + NewState(ethcmn.BytesToHash([]byte{1, 2, 3}), ethcmn.BytesToHash([]byte{1, 2, 3})), + }, + }, + { + "empty storage key value bytes", + Storage{ + {Key: ethcmn.Hash{}, Value: ethcmn.Hash{}}, + }, + }, + { + "empty storage", + Storage{}, + }, + } + + for _, tc := range testCases { + tc := tc + require.Equal(t, tc.storage, tc.storage.Copy(), tc.name) + } +} + +func TestStorageString(t *testing.T) { + storage := Storage{NewState(ethcmn.BytesToHash([]byte("key")), ethcmn.BytesToHash([]byte("value")))} + str := "0x00000000000000000000000000000000000000000000000000000000006b6579: 0x00000000000000000000000000000000000000000000000000000076616c7565\n" + require.Equal(t, str, storage.String()) +} diff --git a/x/evm/types/tx_data.go b/x/evm/types/tx_data.go new file mode 100644 index 0000000000..93f0b05568 --- /dev/null +++ b/x/evm/types/tx_data.go @@ -0,0 +1,177 @@ +package types + +import ( + "fmt" + "math/big" + + "github.com/cosmos/ethermint/utils" + + ethcmn "github.com/ethereum/go-ethereum/common" +) + +// TxData implements the Ethereum transaction data structure. It is used +// solely as intended in Ethereum abiding by the protocol. +type TxData struct { + AccountNonce uint64 `json:"nonce"` + Price *big.Int `json:"gasPrice"` + GasLimit uint64 `json:"gas"` + Recipient *ethcmn.Address `json:"to" rlp:"nil"` // nil means contract creation + Amount *big.Int `json:"value"` + Payload []byte `json:"input"` + + // signature values + V *big.Int `json:"v"` + R *big.Int `json:"r"` + S *big.Int `json:"s"` + + // hash is only used when marshaling to JSON + Hash *ethcmn.Hash `json:"hash" rlp:"-"` +} + +// encodableTxData implements the Ethereum transaction data structure. It is used +// solely as intended in Ethereum abiding by the protocol. +type encodableTxData struct { + AccountNonce uint64 `json:"nonce"` + Price string `json:"gasPrice"` + GasLimit uint64 `json:"gas"` + Recipient *ethcmn.Address `json:"to" rlp:"nil"` // nil means contract creation + Amount string `json:"value"` + Payload []byte `json:"input"` + + // signature values + V string `json:"v"` + R string `json:"r"` + S string `json:"s"` + + // hash is only used when marshaling to JSON + Hash *ethcmn.Hash `json:"hash" rlp:"-"` +} + +func (td TxData) String() string { + if td.Recipient != nil { + return fmt.Sprintf("nonce=%d price=%s gasLimit=%d recipient=%s amount=%s data=0x%x v=%s r=%s s=%s", + td.AccountNonce, td.Price, td.GasLimit, td.Recipient.Hex(), td.Amount, td.Payload, td.V, td.R, td.S) + } + + return fmt.Sprintf("nonce=%d price=%s gasLimit=%d recipient=nil amount=%s data=0x%x v=%s r=%s s=%s", + td.AccountNonce, td.Price, td.GasLimit, td.Amount, td.Payload, td.V, td.R, td.S) +} + +// MarshalAmino defines custom encoding scheme for TxData +func (td TxData) MarshalAmino() ([]byte, error) { + gasPrice, err := utils.MarshalBigInt(td.Price) + if err != nil { + return nil, err + } + + amount, err := utils.MarshalBigInt(td.Amount) + if err != nil { + return nil, err + } + + v, err := utils.MarshalBigInt(td.V) + if err != nil { + return nil, err + } + + r, err := utils.MarshalBigInt(td.R) + if err != nil { + return nil, err + } + + s, err := utils.MarshalBigInt(td.S) + if err != nil { + return nil, err + } + + e := encodableTxData{ + AccountNonce: td.AccountNonce, + Price: gasPrice, + GasLimit: td.GasLimit, + Recipient: td.Recipient, + Amount: amount, + Payload: td.Payload, + V: v, + R: r, + S: s, + Hash: td.Hash, + } + + return ModuleCdc.MarshalBinaryBare(e) +} + +// UnmarshalAmino defines custom decoding scheme for TxData +func (td *TxData) UnmarshalAmino(data []byte) error { + var e encodableTxData + err := ModuleCdc.UnmarshalBinaryBare(data, &e) + if err != nil { + return err + } + + td.AccountNonce = e.AccountNonce + td.GasLimit = e.GasLimit + td.Recipient = e.Recipient + td.Payload = e.Payload + td.Hash = e.Hash + + price, err := utils.UnmarshalBigInt(e.Price) + if err != nil { + return err + } + + if td.Price != nil { + td.Price.Set(price) + } else { + td.Price = price + } + + amt, err := utils.UnmarshalBigInt(e.Amount) + if err != nil { + return err + } + + if td.Amount != nil { + td.Amount.Set(amt) + } else { + td.Amount = amt + } + + v, err := utils.UnmarshalBigInt(e.V) + if err != nil { + return err + } + + if td.V != nil { + td.V.Set(v) + } else { + td.V = v + } + + r, err := utils.UnmarshalBigInt(e.R) + if err != nil { + return err + } + + if td.R != nil { + td.R.Set(r) + } else { + td.R = r + } + + s, err := utils.UnmarshalBigInt(e.S) + if err != nil { + return err + } + + if td.S != nil { + td.S.Set(s) + } else { + td.S = s + } + + return nil +} + +// TODO: Implement JSON marshaling/ unmarshaling for this type + +// TODO: Implement YAML marshaling/ unmarshaling for this type diff --git a/x/evm/types/tx_data_test.go b/x/evm/types/tx_data_test.go new file mode 100644 index 0000000000..4e9384396c --- /dev/null +++ b/x/evm/types/tx_data_test.go @@ -0,0 +1,56 @@ +package types + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/require" + + ethcmn "github.com/ethereum/go-ethereum/common" +) + +func TestMarshalAndUnmarshalData(t *testing.T) { + addr := GenerateEthAddress() + hash := ethcmn.BigToHash(big.NewInt(2)) + + txData := TxData{ + AccountNonce: 2, + Price: big.NewInt(3), + GasLimit: 1, + Recipient: &addr, + Amount: big.NewInt(4), + Payload: []byte("test"), + V: big.NewInt(5), + R: big.NewInt(6), + S: big.NewInt(7), + Hash: &hash, + } + + bz, err := txData.MarshalAmino() + require.NoError(t, err) + require.NotNil(t, bz) + + var txData2 TxData + err = txData2.UnmarshalAmino(bz) + require.NoError(t, err) + + require.Equal(t, txData, txData2) +} + +func TestMsgEthereumTxAmino(t *testing.T) { + addr := GenerateEthAddress() + msg := NewMsgEthereumTx(5, &addr, big.NewInt(1), 100000, big.NewInt(3), []byte("test")) + + msg.Data.V = big.NewInt(1) + msg.Data.R = big.NewInt(2) + msg.Data.S = big.NewInt(3) + + raw, err := ModuleCdc.MarshalBinaryBare(msg) + require.NoError(t, err) + + var msg2 MsgEthereumTx + + err = ModuleCdc.UnmarshalBinaryBare(raw, &msg2) + require.NoError(t, err) + require.Equal(t, msg, msg2) +} diff --git a/x/evm/types/utils.go b/x/evm/types/utils.go index 85024cf7ac..552fd57b06 100644 --- a/x/evm/types/utils.go +++ b/x/evm/types/utils.go @@ -2,24 +2,32 @@ package types import ( "fmt" + "math/big" + "strings" "github.com/cosmos/ethermint/crypto" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + ethcmn "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" ethcrypto "github.com/ethereum/go-ethereum/crypto" - ethsha "github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/rlp" "github.com/pkg/errors" + "golang.org/x/crypto/sha3" ) -// GenerateAddress generates an Ethereum address. +// GenerateEthAddress generates an Ethereum address. func GenerateEthAddress() ethcmn.Address { priv, err := crypto.GenerateKey() if err != nil { panic(err) } - return ethcrypto.PubkeyToAddress(priv.PublicKey) + return ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) } // ValidateSigner attempts to validate a signer for a given slice of bytes over @@ -38,10 +46,110 @@ func ValidateSigner(signBytes, sig []byte, signer ethcmn.Address) error { } func rlpHash(x interface{}) (hash ethcmn.Hash) { - hasher := ethsha.NewKeccak256() - + hasher := sha3.NewLegacyKeccak256() + //nolint:gosec,errcheck rlp.Encode(hasher, x) hasher.Sum(hash[:0]) - return + return hash +} + +// ResultData represents the data returned in an sdk.Result +type ResultData struct { + ContractAddress ethcmn.Address `json:"contract_address"` + Bloom ethtypes.Bloom `json:"bloom"` + Logs []*ethtypes.Log `json:"logs"` + Ret []byte `json:"ret"` + TxHash ethcmn.Hash `json:"tx_hash"` +} + +// String implements fmt.Stringer interface. +func (rd ResultData) String() string { + return strings.TrimSpace(fmt.Sprintf(`ResultData: + ContractAddress: %s + Bloom: %s + Logs: %v + Ret: %v + TxHash: %s +`, rd.ContractAddress.String(), rd.Bloom.Big().String(), rd.Logs, rd.Ret, rd.TxHash.String())) +} + +// EncodeResultData takes all of the necessary data from the EVM execution +// and returns the data as a byte slice encoded with amino +func EncodeResultData(data ResultData) ([]byte, error) { + return ModuleCdc.MarshalBinaryLengthPrefixed(data) +} + +// DecodeResultData decodes an amino-encoded byte slice into ResultData +func DecodeResultData(in []byte) (ResultData, error) { + var data ResultData + err := ModuleCdc.UnmarshalBinaryLengthPrefixed(in, &data) + if err != nil { + return ResultData{}, err + } + return data, nil +} + +// ---------------------------------------------------------------------------- +// Auxiliary + +// TxDecoder returns an sdk.TxDecoder that can decode both auth.StdTx and +// MsgEthereumTx transactions. +func TxDecoder(cdc *codec.Codec) sdk.TxDecoder { + return func(txBytes []byte) (sdk.Tx, error) { + var tx sdk.Tx + + if len(txBytes) == 0 { + return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "tx bytes are empty") + } + + // sdk.Tx is an interface. The concrete message types + // are registered by MakeTxCodec + // TODO: switch to UnmarshalBinaryBare on SDK v0.40.0 + err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrTxDecode, err.Error()) + } + + return tx, nil + } +} + +// recoverEthSig recovers a signature according to the Ethereum specification and +// returns the sender or an error. +// +// Ref: Ethereum Yellow Paper (BYZANTIUM VERSION 69351d5) Appendix F +// nolint: gocritic +func recoverEthSig(R, S, Vb *big.Int, sigHash ethcmn.Hash) (ethcmn.Address, error) { + if Vb.BitLen() > 8 { + return ethcmn.Address{}, errors.New("invalid signature") + } + + V := byte(Vb.Uint64() - 27) + if !ethcrypto.ValidateSignatureValues(V, R, S, true) { + return ethcmn.Address{}, errors.New("invalid signature") + } + + // encode the signature in uncompressed format + r, s := R.Bytes(), S.Bytes() + sig := make([]byte, 65) + + copy(sig[32-len(r):32], r) + copy(sig[64-len(s):64], s) + sig[64] = V + + // recover the public key from the signature + pub, err := ethcrypto.Ecrecover(sigHash[:], sig) + if err != nil { + return ethcmn.Address{}, err + } + + if len(pub) == 0 || pub[0] != 4 { + return ethcmn.Address{}, errors.New("invalid public key") + } + + var addr ethcmn.Address + copy(addr[:], ethcrypto.Keccak256(pub[1:])[12:]) + + return addr, nil } diff --git a/x/evm/types/utils_test.go b/x/evm/types/utils_test.go new file mode 100644 index 0000000000..32d9b8bbfd --- /dev/null +++ b/x/evm/types/utils_test.go @@ -0,0 +1,36 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + ethcmn "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" +) + +func TestEvmDataEncoding(t *testing.T) { + addr := ethcmn.HexToAddress("0x5dE8a020088a2D6d0a23c204FFbeD02790466B49") + bloom := ethtypes.BytesToBloom([]byte{0x1, 0x3}) + ret := []byte{0x5, 0x8} + + data := ResultData{ + ContractAddress: addr, + Bloom: bloom, + Logs: []*ethtypes.Log{{ + Data: []byte{1, 2, 3, 4}, + BlockNumber: 17, + }}, + Ret: ret, + } + + enc, err := EncodeResultData(data) + require.NoError(t, err) + + res, err := DecodeResultData(enc) + require.NoError(t, err) + require.Equal(t, addr, res.ContractAddress) + require.Equal(t, bloom, res.Bloom) + require.Equal(t, data.Logs, res.Logs) + require.Equal(t, ret, res.Ret) +} diff --git a/x/faucet/alias.go b/x/faucet/alias.go new file mode 100644 index 0000000000..f280118edb --- /dev/null +++ b/x/faucet/alias.go @@ -0,0 +1,24 @@ +package faucet + +import ( + "github.com/cosmos/ethermint/x/faucet/keeper" + "github.com/cosmos/ethermint/x/faucet/types" +) + +const ( + ModuleName = types.ModuleName + RouterKey = types.RouterKey + StoreKey = types.StoreKey + QuerierRoute = types.QuerierRoute +) + +var ( + NewKeeper = keeper.NewKeeper + NewQuerier = keeper.NewQuerier + ModuleCdc = types.ModuleCdc + RegisterCodec = types.RegisterCodec +) + +type ( + Keeper = keeper.Keeper +) diff --git a/x/faucet/client/cli/query.go b/x/faucet/client/cli/query.go new file mode 100644 index 0000000000..0a928c7cfb --- /dev/null +++ b/x/faucet/client/cli/query.go @@ -0,0 +1,53 @@ +package cli + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" + + "github.com/cosmos/ethermint/x/faucet/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// GetQueryCmd defines evm module queries through the cli +func GetQueryCmd(cdc *codec.Codec) *cobra.Command { + faucetQueryCmd := &cobra.Command{ + Use: types.ModuleName, + Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + faucetQueryCmd.AddCommand(flags.GetCommands( + GetCmdFunded(cdc), + )...) + return faucetQueryCmd +} + +// GetCmdFunded queries the total amount funded by the faucet. +func GetCmdFunded(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "funded", + Short: "Gets storage for an account at a given key", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + res, height, err := cliCtx.Query(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryFunded)) + if err != nil { + return err + } + + var out sdk.Coins + cdc.MustUnmarshalJSON(res, &out) + cliCtx = cliCtx.WithHeight(height) + return cliCtx.PrintOutput(out) + }, + } +} diff --git a/x/faucet/client/cli/tx.go b/x/faucet/client/cli/tx.go new file mode 100644 index 0000000000..2faf648c91 --- /dev/null +++ b/x/faucet/client/cli/tx.go @@ -0,0 +1,71 @@ +package cli + +import ( + "bufio" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + + "github.com/cosmos/ethermint/x/faucet/types" +) + +// GetTxCmd return faucet sub-command for tx +func GetTxCmd(cdc *codec.Codec) *cobra.Command { + faucetTxCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "faucet transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + faucetTxCmd.AddCommand(flags.PostCommands( + GetCmdRequest(cdc), + )...) + + return faucetTxCmd +} + +// GetCmdRequest is the CLI command to fund an address with the requested coins +func GetCmdRequest(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "request [amount] [other-recipient (optional)]", + Short: "request an address with the requested coins", + Args: cobra.RangeArgs(1, 2), + RunE: func(cmd *cobra.Command, args []string) error { + inBuf := bufio.NewReader(cmd.InOrStdin()) + cliCtx := context.NewCLIContext().WithCodec(cdc) + txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(authclient.GetTxEncoder(cdc)) + + amount, err := sdk.ParseCoins(args[0]) + if err != nil { + return err + } + + var recipient sdk.AccAddress + if len(args) == 1 { + recipient = cliCtx.GetFromAddress() + } else { + recipient, err = sdk.AccAddressFromBech32(args[1]) + } + + if err != nil { + return err + } + + msg := types.NewMsgFund(amount, cliCtx.GetFromAddress(), recipient) + if err := msg.ValidateBasic(); err != nil { + return err + } + + return authclient.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + }, + } +} diff --git a/x/faucet/client/rest/tx.go b/x/faucet/client/rest/tx.go new file mode 100644 index 0000000000..f475bf9636 --- /dev/null +++ b/x/faucet/client/rest/tx.go @@ -0,0 +1,83 @@ +package rest + +import ( + "fmt" + "net/http" + + "github.com/gorilla/mux" + + "github.com/cosmos/cosmos-sdk/client/context" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/rest" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + + "github.com/cosmos/ethermint/x/faucet/types" +) + +// RegisterRoutes register REST endpoints for the faucet module +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { + r.HandleFunc(fmt.Sprintf("/%s/request", types.ModuleName), requestHandler(cliCtx)).Methods("POST") + r.HandleFunc(fmt.Sprintf("/%s/funded", types.ModuleName), fundedHandlerFn(cliCtx)).Methods("GET") +} + +// PostRequestBody defines fund request's body. +type PostRequestBody struct { + BaseReq rest.BaseReq `json:"base_req" yaml:"base_req"` + Amount sdk.Coins `json:"amount" yaml:"amount"` + Recipient string `json:"receipient" yaml:"receipient"` +} + +func requestHandler(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req PostRequestBody + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request") + return + } + + baseReq := req.BaseReq.Sanitize() + if !baseReq.ValidateBasic(w) { + return + } + + sender, err := sdk.AccAddressFromBech32(baseReq.From) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + var recipient sdk.AccAddress + if req.Recipient == "" { + recipient = sender + } else { + recipient, err = sdk.AccAddressFromBech32(req.Recipient) + } + + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + msg := types.NewMsgFund(req.Amount, sender, recipient) + err = msg.ValidateBasic() + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + authclient.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg}) + } +} + +func fundedHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, _ *http.Request) { + res, height, err := cliCtx.Query(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryFunded)) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + cliCtx = cliCtx.WithHeight(height) + rest.PostProcessResponse(w, cliCtx, res) + } +} diff --git a/x/faucet/genesis.go b/x/faucet/genesis.go new file mode 100644 index 0000000000..6e3f7888ef --- /dev/null +++ b/x/faucet/genesis.go @@ -0,0 +1,35 @@ +package faucet + +import ( + "fmt" + + "github.com/cosmos/ethermint/x/faucet/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + + abci "github.com/tendermint/tendermint/abci/types" +) + +// InitGenesis initializes genesis state based on exported genesis +func InitGenesis(ctx sdk.Context, k Keeper, data types.GenesisState) []abci.ValidatorUpdate { + if acc := k.GetFaucetAccount(ctx); acc == nil { + panic(fmt.Sprintf("%s module account has not been set", ModuleName)) + } + + k.SetEnabled(ctx, data.EnableFaucet) + k.SetTimout(ctx, data.Timeout) + k.SetCap(ctx, data.FaucetCap) + k.SetMaxPerRequest(ctx, data.MaxAmountPerRequest) + + return []abci.ValidatorUpdate{} +} + +// ExportGenesis exports genesis state +func ExportGenesis(ctx sdk.Context, k Keeper) types.GenesisState { + return types.GenesisState{ + EnableFaucet: k.IsEnabled(ctx), + Timeout: k.GetTimeout(ctx), + FaucetCap: k.GetCap(ctx), + MaxAmountPerRequest: k.GetMaxPerRequest(ctx), + } +} diff --git a/x/faucet/handler.go b/x/faucet/handler.go new file mode 100644 index 0000000000..9124d70435 --- /dev/null +++ b/x/faucet/handler.go @@ -0,0 +1,43 @@ +package faucet + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/cosmos/ethermint/x/faucet/types" +) + +// NewHandler returns a handler for faucet messages. +func NewHandler(keeper Keeper) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { + ctx = ctx.WithEventManager(sdk.NewEventManager()) + switch msg := msg.(type) { + case types.MsgFund: + return handleMsgFund(ctx, keeper, msg) + default: + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg) + } + } +} + +// handleMsgFund handles a message to fund an address +func handleMsgFund(ctx sdk.Context, keeper Keeper, msg types.MsgFund) (*sdk.Result, error) { + err := keeper.Fund(ctx, msg.Amount, msg.Recipient) + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()), + sdk.NewAttribute(types.AttributeRecipient, msg.Recipient.String()), + ), + ) + + return &sdk.Result{ + Events: ctx.EventManager().Events(), + }, nil +} diff --git a/x/faucet/keeper/keeper.go b/x/faucet/keeper/keeper.go new file mode 100644 index 0000000000..80a4a087a2 --- /dev/null +++ b/x/faucet/keeper/keeper.go @@ -0,0 +1,209 @@ +package keeper + +import ( + "errors" + "fmt" + "time" + + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" + + "github.com/cosmos/ethermint/x/faucet/types" +) + +// Keeper defines the faucet Keeper. +type Keeper struct { + cdc *codec.Codec + storeKey sdk.StoreKey + supplyKeeper types.SupplyKeeper + + // History of users and their funding timeouts. They are reset if the app is reinitialized. + timeouts map[string]time.Time +} + +// NewKeeper creates a new faucet Keeper instance. +func NewKeeper( + cdc *codec.Codec, storeKey sdk.StoreKey, supplyKeeper types.SupplyKeeper, +) Keeper { + return Keeper{ + cdc: cdc, + storeKey: storeKey, + supplyKeeper: supplyKeeper, + timeouts: make(map[string]time.Time), + } +} + +// Logger returns a module-specific logger. +func (k Keeper) Logger(ctx sdk.Context) log.Logger { + return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) +} + +// GetFaucetAccount returns the faucet ModuleAccount +func (k Keeper) GetFaucetAccount(ctx sdk.Context) supplyexported.ModuleAccountI { + return k.supplyKeeper.GetModuleAccount(ctx, types.ModuleName) +} + +// Fund checks for timeout and max thresholds and then mints coins and transfers +// coins to the recipient. +func (k Keeper) Fund(ctx sdk.Context, amount sdk.Coins, recipient sdk.AccAddress) error { + if !k.IsEnabled(ctx) { + return errors.New("faucet is not enabled. Restart the application and set faucet's 'enable_faucet' genesis field to true") + } + + if err := k.rateLimit(ctx, recipient.String()); err != nil { + return err + } + + totalRequested := sdk.ZeroInt() + for _, coin := range amount { + totalRequested = totalRequested.Add(coin.Amount) + } + + maxPerReq := k.GetMaxPerRequest(ctx) + if totalRequested.GT(maxPerReq) { + return fmt.Errorf("canot fund more than %s per request. requested %s", maxPerReq, totalRequested) + } + + funded := k.GetFunded(ctx) + totalFunded := sdk.ZeroInt() + for _, coin := range funded { + totalFunded = totalFunded.Add(coin.Amount) + } + + cap := k.GetCap(ctx) + + if totalFunded.Add(totalRequested).GT(cap) { + return fmt.Errorf("maximum cap of %s reached. Cannot continue funding", cap) + } + + if err := k.supplyKeeper.MintCoins(ctx, types.ModuleName, amount); err != nil { + return err + } + + if err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, recipient, amount); err != nil { + return err + } + + k.SetFunded(ctx, funded.Add(amount...)) + + k.Logger(ctx).Info(fmt.Sprintf("funded %s to %s", amount, recipient)) + return nil +} + +func (k Keeper) GetTimeout(ctx sdk.Context) time.Duration { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.TimeoutKey) + if len(bz) == 0 { + return time.Duration(0) + } + + var timeout time.Duration + k.cdc.MustUnmarshalBinaryBare(bz, &timeout) + + return timeout +} + +func (k Keeper) SetTimout(ctx sdk.Context, timeout time.Duration) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryBare(timeout) + store.Set(types.TimeoutKey, bz) +} + +func (k Keeper) IsEnabled(ctx sdk.Context) bool { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.EnableFaucetKey) + if len(bz) == 0 { + return false + } + + var enabled bool + k.cdc.MustUnmarshalBinaryBare(bz, &enabled) + return enabled +} + +func (k Keeper) SetEnabled(ctx sdk.Context, enabled bool) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryBare(enabled) + store.Set(types.EnableFaucetKey, bz) +} + +func (k Keeper) GetCap(ctx sdk.Context) sdk.Int { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.CapKey) + if len(bz) == 0 { + return sdk.ZeroInt() + } + + var cap sdk.Int + k.cdc.MustUnmarshalBinaryBare(bz, &cap) + + return cap +} + +func (k Keeper) SetCap(ctx sdk.Context, cap sdk.Int) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryBare(cap) + store.Set(types.CapKey, bz) +} + +func (k Keeper) GetMaxPerRequest(ctx sdk.Context) sdk.Int { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.MaxPerRequestKey) + if len(bz) == 0 { + return sdk.ZeroInt() + } + + var maxPerReq sdk.Int + k.cdc.MustUnmarshalBinaryBare(bz, &maxPerReq) + + return maxPerReq +} + +func (k Keeper) SetMaxPerRequest(ctx sdk.Context, maxPerReq sdk.Int) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryBare(maxPerReq) + store.Set(types.MaxPerRequestKey, bz) +} + +func (k Keeper) GetFunded(ctx sdk.Context) sdk.Coins { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.FundedKey) + if len(bz) == 0 { + return nil + } + + var funded sdk.Coins + k.cdc.MustUnmarshalBinaryBare(bz, &funded) + + return funded +} + +func (k Keeper) SetFunded(ctx sdk.Context, funded sdk.Coins) { + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshalBinaryBare(funded) + store.Set(types.FundedKey, bz) +} + +func (k Keeper) rateLimit(ctx sdk.Context, address string) error { + // first time requester, can send request + lastRequest, ok := k.timeouts[address] + if !ok { + k.timeouts[address] = time.Now().UTC() + return nil + } + + defaultTimeout := k.GetTimeout(ctx) + sinceLastRequest := time.Since(lastRequest) + + if defaultTimeout > sinceLastRequest { + wait := defaultTimeout - sinceLastRequest + return fmt.Errorf("%s has requested funds within the last %s, wait %s before trying again", address, defaultTimeout.String(), wait.String()) + } + + // user able to send funds since they have waited for period + k.timeouts[address] = time.Now().UTC() + return nil +} diff --git a/x/faucet/keeper/querier.go b/x/faucet/keeper/querier.go new file mode 100644 index 0000000000..f63a928c90 --- /dev/null +++ b/x/faucet/keeper/querier.go @@ -0,0 +1,34 @@ +package keeper + +import ( + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/ethermint/x/faucet/types" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// NewQuerier is the module level router for state queries +func NewQuerier(k Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err error) { + switch path[0] { + case types.QueryFunded: + return queryFunded(ctx, req, k) + default: + return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown query endpoint") + } + } +} + +func queryFunded(ctx sdk.Context, _ abci.RequestQuery, k Keeper) ([]byte, error) { + funded := k.GetFunded(ctx) + + bz, err := codec.MarshalJSONIndent(k.cdc, funded) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) + } + + return bz, nil +} diff --git a/x/faucet/module.go b/x/faucet/module.go new file mode 100644 index 0000000000..a770610a7a --- /dev/null +++ b/x/faucet/module.go @@ -0,0 +1,137 @@ +package faucet + +import ( + "encoding/json" + "fmt" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/cosmos/ethermint/x/faucet/client/cli" + "github.com/cosmos/ethermint/x/faucet/client/rest" + "github.com/cosmos/ethermint/x/faucet/types" +) + +// type check to ensure the interface is properly implemented +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// AppModuleBasic defines the basic application module used by the faucet module. +type AppModuleBasic struct{} + +// Name returns the faucet module's name. +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +// RegisterCodec registers the faucet module's types for the given codec. +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) +} + +// DefaultGenesis returns default genesis state as raw bytes for the faucet +// module. +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return types.ModuleCdc.MustMarshalJSON(types.DefaultGenesisState()) +} + +// ValidateGenesis performs genesis state validation for the faucet module. +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + var genesisState types.GenesisState + if err := types.ModuleCdc.UnmarshalJSON(bz, &genesisState); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + + return genesisState.Validate() +} + +// RegisterRESTRoutes registers the REST routes for the faucet module. +func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { + rest.RegisterRoutes(ctx, rtr) +} + +// GetTxCmd returns the root tx command for the faucet module. +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetTxCmd(cdc) +} + +// GetQueryCmd returns no root query command for the faucet module. +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetQueryCmd(cdc) +} + +type AppModule struct { + AppModuleBasic + keeper Keeper +} + +// NewAppModule creates a new AppModule Object +func NewAppModule(k Keeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + keeper: k, + } +} + +// Name returns the faucet module's name. +func (AppModule) Name() string { + return ModuleName +} + +// RegisterInvariants registers the faucet module invariants. +func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} + +// Route returns the message routing key for the faucet module. +func (AppModule) Route() string { + return RouterKey +} + +// NewHandler returns an sdk.Handler for the faucet module. +func (am AppModule) NewHandler() sdk.Handler { + return NewHandler(am.keeper) +} + +// QuerierRoute returns the faucet module's querier route name. +func (AppModule) QuerierRoute() string { + return QuerierRoute +} + +// NewQuerierHandler returns the faucet module sdk.Querier. +func (am AppModule) NewQuerierHandler() sdk.Querier { + return NewQuerier(am.keeper) +} + +// InitGenesis performs genesis initialization for the faucet module. It returns +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + types.ModuleCdc.MustUnmarshalJSON(data, &genesisState) + InitGenesis(ctx, am.keeper, genesisState) + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the exported genesis state as raw bytes for the faucet +// module. +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + gs := ExportGenesis(ctx, am.keeper) + return types.ModuleCdc.MustMarshalJSON(gs) +} + +// BeginBlock returns the begin blocker for the faucet module. +func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { +} + +// EndBlock returns the end blocker for the faucet module. It returns no validator +// updates. +func (AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} diff --git a/x/faucet/types/codec.go b/x/faucet/types/codec.go new file mode 100644 index 0000000000..0378955bd3 --- /dev/null +++ b/x/faucet/types/codec.go @@ -0,0 +1,17 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +// ModuleCdc is the codec for the module +var ModuleCdc = codec.New() + +func init() { + RegisterCodec(ModuleCdc) +} + +// RegisterCodec registers concrete types on the Amino codec +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(MsgFund{}, "ethermint/MsgFund", nil) +} diff --git a/x/faucet/types/errors.go b/x/faucet/types/errors.go new file mode 100644 index 0000000000..ec36514a3f --- /dev/null +++ b/x/faucet/types/errors.go @@ -0,0 +1,10 @@ +package types + +import ( + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +var ( + // ErrWithdrawTooOften withdraw too often + ErrWithdrawTooOften = sdkerrors.Register(ModuleName, 2, "each address can withdraw only once") +) diff --git a/x/faucet/types/events.go b/x/faucet/types/events.go new file mode 100644 index 0000000000..f43e62a5ec --- /dev/null +++ b/x/faucet/types/events.go @@ -0,0 +1,11 @@ +package types + +// Faucet module events +const ( + AttributeRecipient string = "recipient" +) + +// Supported endpoints +const ( + QueryFunded = "funded" +) diff --git a/x/faucet/types/expected_keepers.go b/x/faucet/types/expected_keepers.go new file mode 100644 index 0000000000..17fb55096f --- /dev/null +++ b/x/faucet/types/expected_keepers.go @@ -0,0 +1,20 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" +) + +// SupplyKeeper is required for mining coin +type SupplyKeeper interface { + MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + SendCoinsFromModuleToAccount( + ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins, + ) error + GetModuleAccount(ctx sdk.Context, moduleName string) supplyexported.ModuleAccountI +} + +// StakingKeeper is required for getting Denom +type StakingKeeper interface { + BondDenom(ctx sdk.Context) string +} diff --git a/x/faucet/types/genesis.go b/x/faucet/types/genesis.go new file mode 100644 index 0000000000..6721482e96 --- /dev/null +++ b/x/faucet/types/genesis.go @@ -0,0 +1,45 @@ +package types + +import ( + "fmt" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// GenesisState defines the application's genesis state. It contains all the +// information required and accounts to initialize the blockchain. +type GenesisState struct { + // enable faucet funding + EnableFaucet bool `json:"enable_faucet" yaml:"enable_faucet"` + // addresses can send requests every duration + Timeout time.Duration `json:"timeout" yaml:"timeout"` + // max total amount to be funded by the faucet + FaucetCap sdk.Int `json:"faucet_cap" yaml:"faucet_cap"` + // max amount per request (i.e sum of all requested coin amounts). + MaxAmountPerRequest sdk.Int `json:"max_amount_per_request" yaml:"max_amount_per_request"` +} + +// Validate performs a basic validation of the GenesisState fields. +func (gs GenesisState) Validate() error { + if gs.Timeout < 0 { + return fmt.Errorf("timeout cannot be negative: %s", gs.Timeout) + } + if gs.FaucetCap.IsNegative() { + return fmt.Errorf("faucet cap cannot be negative: %d", gs.FaucetCap) + } + if gs.MaxAmountPerRequest.IsNegative() { + return fmt.Errorf("max amount per request cannot be negative: %d", gs.MaxAmountPerRequest) + } + return nil +} + +// DefaultGenesisState sets default evm genesis config +func DefaultGenesisState() GenesisState { + return GenesisState{ + EnableFaucet: false, + Timeout: 20 * time.Minute, + FaucetCap: sdk.NewInt(1000000000), + MaxAmountPerRequest: sdk.NewInt(1000), + } +} diff --git a/x/faucet/types/key.go b/x/faucet/types/key.go new file mode 100644 index 0000000000..6b1adc75d1 --- /dev/null +++ b/x/faucet/types/key.go @@ -0,0 +1,23 @@ +package types + +const ( + // ModuleName is the name of the module + ModuleName = "faucet" + + // StoreKey to be used when creating the KVStore + StoreKey = ModuleName + + // RouterKey uses module name for tx routing + RouterKey = ModuleName + + // QuerierRoute uses module name for query routing + QuerierRoute = ModuleName +) + +var ( + EnableFaucetKey = []byte{0x01} + TimeoutKey = []byte{0x02} + CapKey = []byte{0x03} + MaxPerRequestKey = []byte{0x04} + FundedKey = []byte{0x05} +) diff --git a/x/faucet/types/msgs.go b/x/faucet/types/msgs.go new file mode 100644 index 0000000000..04f914155b --- /dev/null +++ b/x/faucet/types/msgs.go @@ -0,0 +1,52 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// MsgFund funds a recipient address +type MsgFund struct { + Amount sdk.Coins `json:"amount" yaml:"amount"` + Sender sdk.AccAddress `json:"sender" yaml:"sender"` + Recipient sdk.AccAddress `json:"receipient" yaml:"receipient"` +} + +// NewMsgFund is a constructor function for NewMsgFund +func NewMsgFund(amount sdk.Coins, sender, recipient sdk.AccAddress) MsgFund { + return MsgFund{ + Amount: amount, + Sender: sender, + Recipient: recipient, + } +} + +// Route should return the name of the module +func (msg MsgFund) Route() string { return RouterKey } + +// Type should return the action +func (msg MsgFund) Type() string { return "fund" } + +// ValidateBasic runs stateless checks on the message +func (msg MsgFund) ValidateBasic() error { + if !msg.Amount.IsValid() { + return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String()) + } + if msg.Sender.Empty() { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "sender %s", msg.Sender.String()) + } + if msg.Recipient.Empty() { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "recipient %s", msg.Recipient.String()) + } + return nil +} + +// GetSignBytes encodes the message for signing +func (msg MsgFund) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) +} + +// GetSigners defines whose signature is required +func (msg MsgFund) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Sender} +}