diff --git a/.gitattributes b/.gitattributes index a0c478da1fc..c22d136ec50 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,5 @@ *.gno linguist-language=Go *.pb.go linguist-generated merge=ours -diff go.sum linguist-generated text +gnovm/stdlibs/native.go linguist-generated +gnovm/tests/stdlibs/native.go linguist-generated diff --git a/.github/.fossa.yml b/.github/.fossa.yml new file mode 100644 index 00000000000..d639b393e98 --- /dev/null +++ b/.github/.fossa.yml @@ -0,0 +1,15 @@ +version: 3 + +# https://github.com/fossas/fossa-cli/blob/master/docs/references/files/fossa-yml.md + +project: + id: github.com/gnolang/gno + name: gno + +targets: + only: + - type: gomod + +paths: + exclude: + - ./misc/ diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 98ff9ec12fa..ade701ad977 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,39 +1,67 @@ # CODEOWNERS: https://help.github.com/articles/about-codeowners/ # Primary repo maintainers. -* @gnolang/tech-staff - +* @gnolang/tech-staff # Tendermint2 (Gno version). -tm2/* @gnolang/tech-staff -tm2/pkg @jaekwon @moul +/tm2/* @gnolang/tech-staff +/tm2/pkg @jaekwon @piux2 @moul @zivkovicmilos +/tm2/pkg/crypto @jaekwon @moul @gnolang/security # TODO: add per package exceptions - +# ... # Docs & Content. -docs/ @gnolang/tech-staff -*.md @gnolang/tech-staff -# TODO: add non-tech people here. - +/docs/ @gnolang/devrels +/misc/docusaurus/ @gnolang/devrels +/README.md @gnolang/devrels +/.gitpod.yml @gnolang/devrels # Gno examples and default contracts. -examples/* @gnolang/tech-staff +/examples/ @gnolang/tech-staff @gnolang/devrels +/examples/gno.land/r/gnoland/ @moul +/examples/gno.land/r/gov/ @moul +/examples/gno.land/r/sys/ @moul +/examples/gno.land/r/worx/ @moul +/examples/gno.land/r/x @gnolang/devrels # TODO: add people from the community here. - +/examples/gno.land/r/jaekwon/ @jaekwon +/examples/gno.land/r/manfred/ @moul # Gno.land. -gno.land/* @gnolang/tech-staff - +/gno.land/ @moul +/gno.land/pkg/integration @gfanton +#... # GnoVM/Gnolang. -gnovm/* @gnolang/tech-staff -gnovm/stdlibs @jaekwon @moul -gnovm/pkg/gnolang @jaekwon @moul +/gnovm/ @gnolang/tech-staff +/gnovm/stdlibs @jaekwon @thehowl +/gnovm/tests @jaekwon @piux2 @thehowl @moul +/gnovm/cmd/gno @jaekwon @thehowl @harry-hov @moul +/gnovm/pkg/gnolang @jaekwon @piux2 @thehowl @moul +/gnovm/pkg/doc @thehowl +/gnovm/pkg/gnomod @harry-hov +/gnovm/pkg/integration @gfanton +#/gnovm/pkg/gnoenv +#/gnovm/pkg/repl + +# Contribs +/contribs/ @gnolang/tech-staff +/contribs/gnodev @gfanton +/contribs/gnokeykc @moul +/contribs/gnomd @moul +#... +# Misc +/misc/ @gnolang/tech-staff +/misc/loop @moul @gnolang/devops +/misc/deployments @moul @gnolang/devops +/misc/genstd @thehowl +#... # Special files. -PLAN.md @jaekwon @moul -PHILOSOPHY.md @jaekwon @moul -CONTRIBUTING.md @jaekwon @moul -LICENSE.md @jaekwon @moul -.github/CODEOWNERS @jaekwon @moul +/PLAN.md @jaekwon @moul +/PHILOSOPHY.md @jaekwon +/CONTRIBUTING.md @jaekwon @moul @gnolang/tech-staff +/LICENSE.md @jaekwon +/.github/ @moul @gnolang/tech-staff +/.github/CODEOWNERS @jaekwon @moul diff --git a/.github/codecov.yml b/.github/codecov.yml index 65609743a74..ecd223f0e84 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -52,3 +52,6 @@ flag_management: - name: gno.land paths: - gno.land + - name: misc + paths: + - misc diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e26f6131f19..543364c78ba 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,6 +8,10 @@ updates: interval: "daily" labels: - "github_actions" + groups: + actions: + patterns: + - "*" # Maintain dependencies for top level Go modules - package-ecosystem: gomod @@ -17,6 +21,18 @@ updates: interval: weekly labels: - "dependencies" + groups: + golang-x: + patterns: + - "golang.org/x/*" + dbs: + patterns: + - "github.com/linxGnu/grocksdb" + - "go.etcd.io/bbolt" + - "github.com/dgraph-io/badger/v3" + everything-else: + patterns: + - "*" open-pull-requests-limit: 10 pull-request-branch-name: separator: "-" diff --git a/.github/golangci.yml b/.github/golangci.yml index 992398956b1..6f2d808932f 100644 --- a/.github/golangci.yml +++ b/.github/golangci.yml @@ -1,9 +1,20 @@ run: - timeout: 5m + concurrency: 8 + timeout: 10m + issue-exit-code: 1 tests: true skip-dirs-use-default: true + modules-download-mode: readonly + allow-parallel-runners: false + go: "" + +output: + uniq-by-line: false + path-prefix: "" + sort-results: true linters: + fast: false disable-all: true enable: - whitespace # Tool for detection of leading and trailing whitespace @@ -21,12 +32,12 @@ linters: - gofmt # Whether the code was gofmt-ed - goimports # Unused imports - goconst # Repeated strings that could be replaced by a constant - #- forcetypeassert # Finds forced type assertions - dogsled # Checks assignments with too many blank identifiers (e.g. x, , , _, := f()) - #- dupl # Code clone detection - errname # Checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13 - gofumpt # Stricter gofmt + - unused # Checks Go code for unused constants, variables, functions and types + - gomodguard # Enforces an allow and block list for direct Go module dependencies linters-settings: gofmt: @@ -42,9 +53,31 @@ linters-settings: checks: [ "all", "-ST1022", "-ST1003" ] errorlint: asserts: false + gocritic: + enabled-tags: + - diagnostic + - experimental + - opinionated + - performance + - style + gomodguard: + blocked: + modules: + - log/slog: + recommendations: + - golang.org/x/exp + reason: "the minimum go version for the monorepo is 1.20" + versions: + - go.uber.org/zap/exp: + version: "> 0.1.0" + reason: "this version of zap/exp is a requirement until we upgrade to go 1.21 (https://github.com/uber-go/zap/blob/master/exp/CHANGELOG.md)" issues: whole-files: true + max-issues-per-linter: 0 + max-same-issues: 0 + new: false + fix: false exclude-rules: - path: _test\.go linters: diff --git a/.github/labeler.yml b/.github/labeler.yml index 67614da1f3b..4efd0c7fda6 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -9,3 +9,6 @@ ":package: :mountain: gno.land": - "gno.land/**/*" + +":book: documentation": +- docs/**/*" diff --git a/.github/workflows/auto-author-assign.yml b/.github/workflows/auto-author-assign.yml index 8902a128b5d..c7f209687c4 100644 --- a/.github/workflows/auto-author-assign.yml +++ b/.github/workflows/auto-author-assign.yml @@ -15,4 +15,4 @@ jobs: assign-author: runs-on: ubuntu-latest steps: - - uses: toshimaru/auto-author-assign@v1.6.2 + - uses: toshimaru/auto-author-assign@v2.0.1 diff --git a/.github/workflows/codegen.yml b/.github/workflows/codegen.yml new file mode 100644 index 00000000000..f941dd69855 --- /dev/null +++ b/.github/workflows/codegen.yml @@ -0,0 +1,36 @@ +name: code generation + +on: + push: + branches: [ "master" ] + pull_request: + paths: + - 'gnovm/stdlibs/**' + - 'gnovm/tests/stdlibs/**' + - 'misc/genstd' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + generated: + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: 1.21.x + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check generated files are up to date + run: | + go generate -x ./... + if [ "$(git status -s)" != "" ]; then + echo "command 'go generate' creates file that differ from git tree, please run 'go generate' and commit:" + git status -s + exit 1 + fi + diff --git a/.github/workflows/contribs.yml b/.github/workflows/contribs.yml new file mode 100644 index 00000000000..e3bada1e195 --- /dev/null +++ b/.github/workflows/contribs.yml @@ -0,0 +1,36 @@ +name: contribs + +on: + push: + branches: [ "master" ] + pull_request: + paths: + - "contribs/**" + - ".github/workflows/contribs.yml" + - "gnovm/**.go" + - "gno.land/**.go" + - "tm2/**.go" + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + install: + strategy: + fail-fast: false + matrix: + goversion: # two latest versions + - "1.21.x" + program: + - "gnomd" + - "gnodev" + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.goversion }} + - run: make install ${{ matrix.program }} + working-directory: contribs diff --git a/.github/workflows/db-tests.yml b/.github/workflows/db-tests.yml index f8da78cc3d8..b259a0bcf74 100644 --- a/.github/workflows/db-tests.yml +++ b/.github/workflows/db-tests.yml @@ -47,6 +47,15 @@ jobs: wget http://ftp.us.debian.org/debian/pool/main/l/leveldb/libleveldb-dev_1.22-3_amd64.deb sudo dpkg -i *.deb + - name: Set environment variables for debug mode + if: env.ACTIONS_STEP_DEBUG == 'true' + run: | + export LOG_PATH_DIR=${{ runner.temp }}/logs + mkdir -p $LOG_PATH_DIR + + echo "LOG_LEVEL=debug" >> $GITHUB_ENV + echo "LOG_PATH_DIR=$LOG_PATH_DIR" >> $GITHUB_ENV + # test ./pkgs/db - name: test ./tm2/pkg/db run: go test -tags ${{ matrix.tags }} ./tm2/pkg/db/... diff --git a/.github/workflows/dependabot-tidy.yml b/.github/workflows/dependabot-tidy.yml new file mode 100644 index 00000000000..8234de91924 --- /dev/null +++ b/.github/workflows/dependabot-tidy.yml @@ -0,0 +1,41 @@ +name: Dependabot Tidy Go Mods + +on: + pull_request: + paths: + - '.github/workflows/**' + - '**/go.mod' + - '**/go.sum' + +jobs: + tidy_go_mods: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' }} + permissions: + contents: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: 1.21.x + + - name: Tidy all Go mods + env: + VERIFY_MOD_SUMS: false + run: | + # Ensure Make is installed + make --version + + # Run the tidy target + make tidy + + - name: Commit changes, if any + uses: stefanzweifel/git-auto-commit-action@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + skip_dirty_check: false # Enable dirty check, and skip unnecessary committing + commit_message: "Run 'go mod tidy' via GitHub Actions" diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 139c2569dc4..f2d1896a61c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -51,7 +51,7 @@ jobs: - name: Log in to GitHub Container Registry if: (github.event_name != 'pull_request') - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} diff --git a/.github/workflows/docusaurus.yml b/.github/workflows/docusaurus.yml new file mode 100644 index 00000000000..5cdaa2455c0 --- /dev/null +++ b/.github/workflows/docusaurus.yml @@ -0,0 +1,29 @@ +name: docusaurus build +on: + pull_request: + paths: + - "docs/**" + push: + branches: [ "master" ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Install Docusaurus Dependencies + run: | + cd misc/docusaurus + yarn install + + - name: Build Docusaurus Docs + run: | + cd misc/docusaurus + yarn build diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index d4b3079d612..35917d38e99 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -32,8 +32,7 @@ jobs: with: go-version: ${{ matrix.goversion }} - run: go install -v ./gnovm/cmd/gno - - run: go run ./gnovm/cmd/gno precompile --verbose ./examples - - run: go run ./gnovm/cmd/gno build --verbose ./examples + - run: go run ./gnovm/cmd/gno precompile --verbose --gobuild ./examples test: strategy: fail-fast: false @@ -49,8 +48,16 @@ jobs: - uses: actions/setup-go@v4 with: go-version: ${{ matrix.goversion }} + - name: Set environment variables for debug mode + if: env.ACTIONS_STEP_DEBUG == 'true' + run: | + export LOG_PATH_DIR=${{ runner.temp }}/logs + mkdir -p $LOG_PATH_DIR + + echo "LOG_LEVEL=debug" >> $GITHUB_ENV + echo "LOG_PATH_DIR=$LOG_PATH_DIR" >> $GITHUB_ENV - run: go install -v ./gnovm/cmd/gno - - run: go run ./gnovm/cmd/gno test --verbose ./examples + - run: go run ./gnovm/cmd/gno test --verbose ./examples/... lint: strategy: fail-fast: false @@ -73,3 +80,22 @@ jobs: - run: go run ./gnovm/cmd/gno lint --verbose ./examples/gno.land/r/gnoland - run: go run ./gnovm/cmd/gno lint --verbose ./examples/gno.land/r/system # TODO: track coverage + mod-tidy: + strategy: + fail-fast: false + matrix: + go-version: [ "1.21.x" ] + # unittests: TODO: matrix with contracts + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go-version }} + - uses: actions/checkout@v4 + - run: | + GNO_CMD="$(pwd)/gnovm/cmd/gno" + # Find all directories containing gno.mod file + find ./examples -name "gno.mod" -execdir go run "$GNO_CMD" mod tidy \; + # Check if there are changes after running gno mod tidy + git diff --exit-code || (echo "Some gno.mod files are not tidy, please run 'make tidy'." && exit 1) diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml new file mode 100644 index 00000000000..52de6a09b80 --- /dev/null +++ b/.github/workflows/fossa.yml @@ -0,0 +1,50 @@ +name: Dependency License Scanning + +on: + workflow_dispatch: + pull_request: + paths: + - ".github/.fossa.yml" + - ".github/workflows/fossa.yml" + schedule: + - cron: '0 0 * * 6' # At 00:00 on saturdays + +permissions: + contents: read + +jobs: + fossa: + name: Fossa + runs-on: ubuntu-latest + if: github.repository == 'gnolang/gno' + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + # we don't know what commit the last tag was it's safer to get entire repo so previousStableVersion resolves + fetch-depth: 0 + + - name: Move .fossa.yml to root dir + run: mv .github/.fossa.yml . + + - name: Cache Coursier cache + uses: coursier/cache-action@v6.4.4 + + - name: Set up JDK 17 + uses: coursier/setup-action@v1.3.0 + with: + jvm: temurin:1.17 + + - name: Set up fossa CLI + run: "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install-latest.sh | bash" + + - name: FOSSA analyze + run: fossa analyze + env: + FOSSA_API_KEY: "${{secrets.FOSSA_API_KEY}}" + + - name: FOSSA test + run: fossa test + env: + FOSSA_API_KEY: "${{secrets.FOSSA_API_KEY}}" + diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 8f57bec80a1..dea849fbb4d 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -21,10 +21,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - run: "cd misc/devdeps && make install" - - run: "cd misc/gendocs && make gen" - - run: "find docs/ -type f -ls" - - uses: actions/configure-pages@v3 + - uses: actions/setup-go@v4 + with: + go-version: "1.21.x" + - run: "cd misc/gendocs && make install gen" + - uses: actions/configure-pages@v4 id: pages - uses: actions/upload-pages-artifact@v2 with: diff --git a/.github/workflows/gnoland.yml b/.github/workflows/gnoland.yml index 9fdfa3011cc..7c7d3747ee9 100644 --- a/.github/workflows/gnoland.yml +++ b/.github/workflows/gnoland.yml @@ -37,7 +37,6 @@ jobs: - gnokey - gnoweb - gnofaucet - - gnotxsync runs-on: ubuntu-latest timeout-minutes: 5 steps: @@ -60,7 +59,7 @@ jobs: - _test.gnoland - _test.gnokey - _test.pkgs - #- _test.gnoweb # this test should be rewritten to run an inmemory localnode + - _test.gnoweb runs-on: ubuntu-latest timeout-minutes: 15 steps: @@ -68,19 +67,45 @@ jobs: - uses: actions/setup-go@v4 with: go-version: ${{ matrix.goversion }} + - name: Set environment variables for debug mode + if: ${{ runner.debug == 1 }} + run: | + export LOG_PATH_DIR=${{ runner.temp }}/logs-go${{ matrix.goversion }}-${{ matrix.args }} + mkdir -p $LOG_PATH_DIR + + echo "LOG_LEVEL=debug" >> $GITHUB_ENV + echo "LOG_PATH_DIR=$LOG_PATH_DIR" >> $GITHUB_ENV - name: test working-directory: gno.land run: | export GOPATH=$HOME/go export GOTEST_FLAGS="-v -p 1 -timeout=30m -coverprofile=coverage.out -covermode=atomic" make ${{ matrix.args }} - - if: ${{ runner.os == 'Linux' && matrix.goversion == '1.21.x' }} + - uses: actions/upload-artifact@v3 + if: ${{ runner.os == 'Linux' && matrix.goversion == '1.21.x' }} + with: + name: ${{runner.os}}-coverage-gnoland-${{ matrix.args}}-${{matrix.goversion}} + path: ./gno.land/coverage.out + - name: Upload Debug Logs + uses: actions/upload-artifact@v3 + if: ${{ always() && runner.debug == 1 }} + with: + name: logs-test-go${{ matrix.goversion }}-${{ matrix.args }} + path: ${{ env.LOG_PATH_DIR }}/**/* + + upload-coverage: + needs: test + runs-on: ubuntu-latest + steps: + - name: Download all previous coverage artifacts + uses: actions/download-artifact@v3 + with: + path: ${{ runner.temp }}/coverage + - name: Upload combined coverage to Codecov uses: codecov/codecov-action@v3 with: + directory: ${{ runner.temp }}/coverage token: ${{ secrets.CODECOV_TOKEN }} - name: gno.land - flags: gno.land-${{matrix.args}} - files: ./gno.land/coverage.out fail_ci_if_error: ${{ github.repository == 'gnolang/gno' }} docker-integration: diff --git a/.github/workflows/gnovm.yml b/.github/workflows/gnovm.yml index 034aa109767..15e0f886e1b 100644 --- a/.github/workflows/gnovm.yml +++ b/.github/workflows/gnovm.yml @@ -67,23 +67,82 @@ jobs: - _test.gnolang.other runs-on: ubuntu-latest timeout-minutes: 15 + env: + COVERAGE_DIR: "/tmp/coverage" steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: go-version: ${{ matrix.goversion }} + - name: Set environment variables for debug mode + if: ${{ runner.debug == 1 }} + run: | + export LOG_PATH_DIR=${{ runner.temp }}/logs-go${{ matrix.goversion }}-${{ matrix.args }} + mkdir -p $LOG_PATH_DIR + + echo "LOG_LEVEL=debug" >> $GITHUB_ENV + echo "LOG_PATH_DIR=$LOG_PATH_DIR" >> $GITHUB_ENV - name: test working-directory: gnovm + env: + TXTARCOVERDIR: ${{ env.COVERAGE_DIR }} run: | + mkdir -p $COVERAGE_DIR + + # Setup testing environements variables export GOPATH=$HOME/go - export GOTEST_FLAGS="-v -p 1 -timeout=30m -coverprofile=coverage.out -covermode=atomic" + export GOTEST_FLAGS="-v -p 1 -timeout=30m -covermode=atomic -test.gocoverdir=$COVERAGE_DIR" + + # Run target test make ${{ matrix.args }} - - if: ${{ runner.os == 'Linux' && matrix.goversion == '1.21.x' }} + - uses: actions/upload-artifact@v3 + if: ${{ runner.os == 'Linux' && matrix.goversion == '1.21.x' }} + with: + name: ${{runner.os}}-coverage-gnovm-${{ matrix.args}}-${{matrix.goversion}} + path: ${{ env.COVERAGE_DIR }} + - name: Upload Debug Logs + uses: actions/upload-artifact@v3 + if: ${{ always() && runner.debug == 1 }} + with: + name: logs-test-go${{ matrix.goversion }}-${{ matrix.args }} + path: ${{ env.LOG_PATH_DIR }}/**/* + + upload-coverage: + needs: test + runs-on: ubuntu-latest + env: + COVERAGE_DATA: /tmp/coverage/coverage-raw + COVERAGE_OUTPUT: /tmp/coverage/coverage-out + COVERAGE_PROFILE: /tmp/coverage/coverage.txt + steps: + - run: mkdir -p $COVERAGE_DATA $COVERAGE_OUTPUT + - name: Download all previous coverage data artifacts + uses: actions/download-artifact@v3 + with: + path: ${{ env.COVERAGE_DATA }} + - uses: actions/setup-go@v4 + with: + go-version: "1.21.x" + - name: Merge coverages + working-directory: ${{ env.COVERAGE_DATA }} + run: | + # Create coverage directory list separate by comma + export COVERAGE_DIRS="$(ls | tr '\n' ',' | sed s/,$//)" + + # Merge all coverage data directories from previous tests + go tool covdata merge -v 1 -i="$COVERAGE_DIRS" -o $COVERAGE_OUTPUT + + # Print coverage percent for debug purpose if needed + echo 'coverage results:' + go tool covdata percent -i=$COVERAGE_OUTPUT + + # Generate coverage profile + go tool covdata textfmt -v 1 -i=$COVERAGE_OUTPUT -o $COVERAGE_PROFILE + + - name: Upload combined coverage to Codecov uses: codecov/codecov-action@v3 with: + files: ${{ env.COVERAGE_PROFILE }} token: ${{ secrets.CODECOV_TOKEN }} - name: gnovm - verbose: true - flags: gnovm-${{matrix.args}} - files: ./gnovm/coverage.out fail_ci_if_error: ${{ github.repository == 'gnolang/gno' }} + diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000000..b417e904e7c --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,84 @@ +name: lint + +on: + push: + branches: [ "master" ] + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: 1.21.x + + - name: Lint + uses: golangci/golangci-lint-action@v3 + with: + # sync with misc/devdeps/go.mod + version: v1.54 + args: + --config=./.github/golangci.yml + fmt: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: 1.21.x + + - name: Install make + run: sudo apt-get install -y make + + # prefill dependencies so that mod messages don't show up in make output + - name: Fetch dependencies + run: go mod download -modfile ./misc/devdeps/go.mod -x + + # inspired by: + # https://github.com/Jerome1337/gofmt-action/blob/d5eabd189843f1d568286a54578159978b7c0fb1/entrypoint.sh + - name: Check gofumpt / goimports + run: | + output="$(GOFMT_FLAGS=-l GOIMPORTS_FLAGS=-l make -s fmt)" + if [ ! -z "$output" ]; then + echo "The following files are not properly formatted; run 'make fmt imports' to format them." + echo "$output" + exit 1 + else + echo 'Succeeded.' + fi + mod_tidy_check: + runs-on: ubuntu-latest + if: ${{ github.actor != 'dependabot[bot]' }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: 1.21.x + + - name: Install make + run: sudo apt-get install -y make + + - name: Check go.mods + env: + VERIFY_MOD_SUMS: true + run: | + # Ensure Make is installed + make --version + + # Run the tidy target + make tidy diff --git a/.github/workflows/misc.yml b/.github/workflows/misc.yml index a7c20d61ffd..52329e9c7d6 100644 --- a/.github/workflows/misc.yml +++ b/.github/workflows/misc.yml @@ -1,35 +1,59 @@ +# tests the "misc" directory & tools +# (not meant for miscellaneous workflows) name: misc on: + pull_request: + paths: + - "misc/genstd/**.go" + - "misc/Makefile" + - ".github/workflows/misc.yml" + # Until the codecov issue is resolved, it's essential to run the tests for gnovm, tm2, misc, and gno.land concurrently. + - "gnovm/**" + - "tm2/**" + - "gno.land/**" + - "examples/**" + - ".github/workflows/**" push: branches: [ "master" ] - pull_request: concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: - lint: + build: + strategy: + fail-fast: false + matrix: + goversion: + - "1.21.x" + program: + - "genstd" runs-on: ubuntu-latest + timeout-minutes: 5 steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Install Go uses: actions/setup-go@v4 with: - go-version: 1.21.x + go-version: ${{ matrix.goversion }} + - name: Checkout code + uses: actions/checkout@v4 - - name: Lint - uses: golangci/golangci-lint-action@v3 - with: - # sync with misc/devdeps/go.mod - version: v1.54 - args: - --config=./.github/golangci.yml - fmt: + - name: go install + working-directory: misc + run: go install ./${{ matrix.program }} + + test: + strategy: + fail-fast: false + matrix: + goversion: + - "1.21.x" + args: + - _test.genstd runs-on: ubuntu-latest + timeout-minutes: 15 steps: - name: Checkout code uses: actions/checkout@v4 @@ -37,42 +61,29 @@ jobs: - name: Install Go uses: actions/setup-go@v4 with: - go-version: 1.21.x + go-version: ${{ matrix.goversion }} - - name: Install make - run: sudo apt-get install -y make + - name: Set environment variables for debug mode + if: env.ACTIONS_STEP_DEBUG == 'true' + run: | + export LOG_PATH_DIR=${{ runner.temp }}/logs + mkdir -p $LOG_PATH_DIR - # prefill dependencies so that mod messages don't show up in make output - - name: Fetch dependencies - run: go mod download -modfile ./misc/devdeps/go.mod -x + echo "LOG_LEVEL=debug" >> $GITHUB_ENV + echo "LOG_PATH_DIR=$LOG_PATH_DIR" >> $GITHUB_ENV - # inspired by: - # https://github.com/Jerome1337/gofmt-action/blob/d5eabd189843f1d568286a54578159978b7c0fb1/entrypoint.sh - - name: Check gofumpt + - name: Test + working-directory: misc run: | - output="$(GOFMT_FLAGS=-l make -s fmt)" - if [ ! -z "$output" ]; then - echo "The following files are not properly formatted; run 'make fmt' to format them." - echo "$output" - exit 1 - else - echo 'Succeeded.' - fi - modtidy: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 + export GOPATH=$HOME/go + export GOTEST_FLAGS="-v -p 1 -timeout=30m -coverprofile=coverage.out -covermode=atomic" + make ${{ matrix.args }} - - name: Install Go - uses: actions/setup-go@v4 + - if: runner.os == 'Linux' + uses: codecov/codecov-action@v3 with: - go-version: 1.21.x - - - name: Check go.mods - run: | - sums="$(sha256sum go.mod misc/devdeps/go.mod)" - for path in . ./misc/devdeps; do - env -C $path go mod tidy -v || exit 1 - done - echo "$sums" | sha256sum -c + token: ${{ secrets.CODECOV_TOKEN }} + name: misc + flags: misc,misc-${{matrix.args}},go-${{ matrix.goversion }} + files: ./misc/coverage.out + fail_ci_if_error: ${{ github.repository == 'gnolang/gno' }} diff --git a/.github/workflows/monthly-snapshots.yml b/.github/workflows/monthly-snapshots.yml new file mode 100644 index 00000000000..85307074218 --- /dev/null +++ b/.github/workflows/monthly-snapshots.yml @@ -0,0 +1,22 @@ +name: Monthly Snapshots + +on: + schedule: + - cron: '0 0 1 * *' + workflow_dispatch: + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Generate tag name + id: tag_name + run: echo "::set-output name=tag_name::v0.0.1-dev.$(date +'%Y.%m.%d')" + - name: Release + uses: softprops/action-gh-release@v1 + with: + generate_release_notes: true + prerelease: true + tag_name: '${{ steps.tag_name.outputs.tag_name }}' diff --git a/.github/workflows/tm2.yml b/.github/workflows/tm2.yml index fc744ccc2ef..bbe379bb069 100644 --- a/.github/workflows/tm2.yml +++ b/.github/workflows/tm2.yml @@ -42,6 +42,7 @@ jobs: working-directory: tm2 run: GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go install ${{ matrix.program }} + test: strategy: fail-fast: false @@ -55,25 +56,51 @@ jobs: - _test.pkg.bft - _test.pkg.others runs-on: ubuntu-latest - timeout-minutes: 15 + timeout-minutes: 21 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: go-version: ${{ matrix.goversion }} + - name: Set environment variables for debug mode + if: ${{ runner.debug == 1 }} + run: | + export LOG_PATH_DIR=${{ runner.temp }}/logs-go${{ matrix.goversion }}-${{ matrix.args }} + mkdir -p $LOG_PATH_DIR + + echo "LOG_LEVEL=debug" >> $GITHUB_ENV + echo "LOG_PATH_DIR=$LOG_PATH_DIR" >> $GITHUB_ENV - name: test working-directory: tm2 run: | export GOPATH=$HOME/go - export GOTEST_FLAGS="-v -p 1 -timeout=30m -coverprofile=coverage.out -covermode=atomic" + export GOTEST_FLAGS="-v -p 1 -timeout=20m -coverprofile=coverage.out -covermode=atomic" make ${{ matrix.args }} touch coverage.out - - if: ${{ runner.os == 'Linux' && matrix.goversion == '1.21.x' }} + - uses: actions/upload-artifact@v3 + if: ${{ runner.os == 'Linux' && matrix.goversion == '1.21.x' }} + with: + name: ${{runner.os}}-coverage-tm2-${{ matrix.args}}-${{matrix.goversion}} + path: ./tm2/coverage.out + - name: Upload Debug Logs + uses: actions/upload-artifact@v3 + if: ${{ always() && runner.debug == 1 }} + with: + name: logs-test-go${{ matrix.goversion }}-${{ matrix.args }} + path: ${{ env.LOG_PATH_DIR }}/**/* + + upload-coverage: + needs: test + runs-on: ubuntu-latest + steps: + - name: Download all previous coverage artifacts + uses: actions/download-artifact@v3 + with: + path: ${{ runner.temp }}/coverage + - name: Upload combined coverage to Codecov uses: codecov/codecov-action@v3 with: + directory: ${{ runner.temp }}/coverage token: ${{ secrets.CODECOV_TOKEN }} - name: tm2 - verbose: true - flags: tm2-${{matrix.args}} - files: ./tm2/coverage.out fail_ci_if_error: ${{ github.repository == 'gnolang/gno' }} + diff --git a/.mailmap b/.mailmap new file mode 100644 index 00000000000..2c81b9938ea --- /dev/null +++ b/.mailmap @@ -0,0 +1,12 @@ +# man 5 gitmailmap +# git log --mailmap --pretty=short | grep ^Author: | sort -u +Jae Kwon <53785+jaekwon@users.noreply.github.com> Jae Kwon +Jae Kwon <53785+jaekwon@users.noreply.github.com> Jae Kwon +Jae Kwon <53785+jaekwon@users.noreply.github.com> jaekwon +Jae Kwon <53785+jaekwon@users.noreply.github.com> Naut Jae +Thomas Bruyelle Thomas Bruyelle +Thomas Bruyelle Thomas Bruyelle +Miloš Živković Miloš Živković +Hariom Verma Hariom Verma +Giancarlos Salas Giancarlos Salas +Morgan Morgan diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d1e23f18273..4a2e8485d06 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,7 @@ -# Contributing to GNO +# Contributing to Gno -Thank you for looking to contribute to the GNO project. -We appreciate every open-source contribution, as it helps us improve and enhance gno for the benefit of the community. +Thank you for looking to contribute to the Gno project. +We appreciate every open-source contribution, as it helps us improve and enhance Gno for the benefit of the community. This document outlines some basic pointers on making your future contribution a great experience. It outlines basic PR etiquette employed by the core gno team. It lays out coding styles, simple how-to guides and tools to get you up and @@ -20,7 +20,7 @@ Likewise, if you have an idea on how to improve this guide, go for it as well. - [Testing](#testing) - [Running locally](#running-locally) - [Running test workflows](#running-test-workflows) - - [Testing GNO code](#testing-gno-code) + - [Testing Gno code](#testing-gno-code) - [Repository Structure](#repository-structure) - [How do I?](#how-do-i) - [How do I submit changes?](#how-do-i-submit-changes) @@ -41,9 +41,9 @@ Likewise, if you have an idea on how to improve this guide, go for it as well. - **[Discord](https://discord.gg/YFtMjWwUN7)** - we are very active on Discord. Join today and start discussing all things gno with fellow engineers and enthusiasts. -- **[Awesome GNO](https://github.com/gnolang/awesome-gno)** - check out the list of compiled resources for helping you +- **[Awesome Gno](https://github.com/gnolang/awesome-gno)** - check out the list of compiled resources for helping you understand the gno ecosystem -- **[Active Staging](https://gno.land/)** - use the currently available staging environment to play around with a +- **[Active Staging](https://staging.gno.land/)** - use the currently available staging environment to play around with a production network. If you want to interact with a local instance, refer to the [Local Setup](#local-setup) guide. - **[Twitter](https://twitter.com/_gnoland)** - follow us on Twitter to get the latest scoop - **[Telegram](https://t.me/gnoland)** - join our official Telegram group to start a conversation about gno @@ -52,28 +52,35 @@ Likewise, if you have an idea on how to improve this guide, go for it as well. ### Environment -The gno repository is primarily based on Golang (Go), and Gnolang (Gno). +The gno repository is primarily based on Go (Golang) and Gno. The primary tech stack for working on the repository: - Go (version 1.20+) - make (for using Makefile configurations) -- Docker (for using the official Docker setup files) It is recommended to work on a Unix environment, as most of the tooling is built around ready-made tools in Unix (WSL2 for Windows / Linux / macOS). For Gno, there is no specific tooling that needs to be installed, that’s not already provided with the repo itself. -You can utilize the `gno` command to facilitate Gnolang support when writing Smart Contracts in Gno, by installing it +You can utilize the `gno` command to facilitate Gno support when writing Smart Contracts in Gno, by installing it with `make install_gno`. +If you are working on Go source code on this repository, `pkg.go.dev` will not +render our documentation as it has a license it does not recognise. Instead, use +the `go doc` command, or use our statically-generated documentation: +https://gnolang.github.io/gno/github.com/gnolang/gno.html + Additionally, you can also configure your editor to recognize `.gno` files as `.go` files, to get the benefit of syntax highlighting. -Currently, we support a [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=harry-hov.gno) extension -(eventually official in the future) for Gnolang. +#### Visual Studio Code + +There currently is an unofficial [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=harry-hov.gno) +extension (primarily developed by a core team member) for working with `*.gno` +files. -#### ViM Support +#### ViM Support (without LSP) Add to your `.vimrc` file: @@ -97,16 +104,111 @@ To use *gofumpt* instead of *gofmt*, as hinted in the comment, you may either ha cexpr system('go run -modfile /misc/devdeps/go.mod mvdan.cc/gofumpt -w ' . expand('%')) ``` +##### ViM Linting Support + +To integrate GNO linting in Vim, you can use Vim's `:make` command with a custom `makeprg` and `errorformat` to run the GNO linter and parse its output. Add the following configuration to your `.vimrc` file: + +```vim +autocmd FileType gno setlocal makeprg=gno\ lint\ % +autocmd FileType gno setlocal errorformat=%f:%l:\ %m + +" Optional: Key binding to run :make on the current file +autocmd FileType gno nnoremap :make +``` + +### ViM Support (with LSP) + There is an experimental and unofficial [Gno Language Server](https://github.com/jdkato/gnols) developed by the community, with an installation guide for Neovim. +For ViM purists, you have to install the [`vim-lsp`](https://github.com/prabirshrestha/vim-lsp) +plugin and then register the LSP server in your `.vimrc` file: + +```vim +augroup gno_autocmd + autocmd! + autocmd BufNewFile,BufRead *.gno + \ set filetype=gno | + \ set syntax=go +augroup END + +if (executable('gnols')) + au User lsp_setup call lsp#register_server({ + \ 'name': 'gnols', + \ 'cmd': ['gnols'], + \ 'allowlist': ['gno'], + \ 'config': {}, + \ 'workspace_config': { + \ 'root' : '/path/to/gno_repo', + \ 'gno' : '/path/to/gno_bin', + \ 'precompileOnSave' : v:true, + \ 'buildOnSave' : v:false, + \ }, + \ 'languageId': {server_info->'gno'}, + \ }) +else + echomsg 'gnols binary not found: LSP disabled for Gno files' +endif + +function! s:on_lsp_buffer_enabled() abort + " Autocompletion + setlocal omnifunc=lsp#complete + " Format on save + autocmd BufWritePre LspDocumentFormatSync + " Some optional mappings + nmap i (lsp-hover) + " Following mappings are not supported yet by gnols + " nmap gd (lsp-definition) + " nmap rr (lsp-rename) +endfunction +augroup lsp_install + au! + autocmd User lsp_buffer_enabled call s:on_lsp_buffer_enabled() +augroup END +``` + +Note that unlike the previous ViM setup without LSP, here it is required by +`vim-lsp` to have a specific `filetype=gno`. Syntax highlighting is preserved +thanks to `syntax=go`. + +Inside `lsp#register_server()`, you also have to replace +`workspace_config.root` and `workspace_config.gno` with the correct directories +from your machine. + +Additionally, it's not possible to use `gofumpt` for code formatting with +`gnols` for now. + #### Emacs Support 1. Install [go-mode.el](https://github.com/dominikh/go-mode.el). 2. Add to your emacs configuration file: ```lisp -(add-to-list 'auto-mode-alist '("\\.gno\\'" . go-mode)) +(define-derived-mode gno-mode go-mode "GNO" + "Major mode for GNO files, an alias for go-mode." + (setq-local tab-width 8)) +(define-derived-mode gno-dot-mod-mode go-dot-mod-mode "GNO Mod" + "Major mode for GNO mod files, an alias for go-dot-mod-mode." + ) +``` + +3. To integrate GNO linting with Flycheck, add the following to your Emacs configuration: +```lisp +(require 'flycheck) + +(flycheck-define-checker gno-lint + "A GNO syntax checker using the gno lint tool." + :command ("gno" "lint" source-original) + :error-patterns (;; ./file.gno:32: error message (code=1) + (error line-start (file-name) ":" line ": " (message) " (code=" (id (one-or-more digit)) ")." line-end)) + ;; Ensure the file is saved, to work around + ;; https://github.com/python/mypy/issues/4746. + :predicate (lambda () + (and (not (bound-and-true-p polymode-mode)) + (flycheck-buffer-saved-p))) + :modes gno-mode) + +(add-to-list 'flycheck-checkers 'gno-lint) ``` #### Sublime Text @@ -122,9 +224,36 @@ Clone the repo: `git clone https://github.com/gnolang/gno.git` Build / install base commands: -`make build ` +`make install` + +If you haven't already, you may need to add the directory where [`go install` +places its binaries](https://pkg.go.dev/cmd/go#hdr-Compile_and_install_packages_and_dependencies) +to your `PATH`. If you haven't configured `GOBIN` or `GOPATH` differently, this +command should suffice: -That’s it! +``` +echo 'export PATH="$HOME/go/bin:$PATH"' >> ~/.profile +source ~/.profile # reload ~/.profile in the current shell +``` + +After that, you should be good to go to use `gno` and `gnokey`, straight from +your command line! The following commands should list the help messages for +each: + +```console +$ gno --help +USAGE + [flags] [...] + +Runs the gno development toolkit +[...] +$ gnokey --help +USAGE + [flags] [...] + +Manages private keys for the node +[...] +``` ### Testing @@ -151,7 +280,7 @@ To run the entire test suite through workflow files, run the following command: act -v -j go-test -#### Testing GNO code +#### Testing Gno code If you wish to test a `.gno` Realm or Package, you can utilize the `gno` tool. @@ -169,24 +298,35 @@ subcommands by running: gno --help +#### Adding new tests + +Most packages will follow the convention established with Go: each package +contains within its file many files suffixed with `_test.go` which test its +functionality. As a general rule, you should follow this convention, and in +every PR you make you should ensure all the code you added is appropriately +covered by tests ([Codecov](https://about.codecov.io/) will loudly complain in +your PR's comments if you don't). + +Additionally, we have a few testing systems that stray from this general rule; +at the time of writing, these are for integration tests and language tests. You +can find more documentation about them [on this guide](docs/testing-guide.md). + ### Repository Structure The repository structure can seem tricky at first, but it’s simple if you consider the philosophy that the gno project -employs (check out [PHILOSOPHY.md](https://github.com/gnolang/gno/blob/master/PHILOSOPHY.md)). +employs (check out [PHILOSOPHY.md](./PHILOSOPHY.md)). The gno project currently favors a mono-repo structure, as it’s easier to manage contributions and keep everyone aligned. In the future, this may change, but in the meantime the majority of gno resources and source code will be centralized here. -- `cmd` - contains the base command implementations for tools like `gnokey`, `gnotxport`, etc. The actual underlying - logic is located within the `pkgs` subdirectories. - `examples` - contains the example `.gno` realms and packages. This is the central point for adding user-defined realms and packages. -- `gnoland` - contains the base source code for bootstrapping the Gnoland node -- `pkgs` - contains the dev-audited packages used throughout the gno codebase -- `stdlibs` - contains the standard library packages used (imported) in `.gno` Smart Contracts. These packages are - themselves `.gno` files. -- `tests` - contains the standard language tests for Gnolang +- `gno.land` - contains the base source code for bootstrapping the Gnoland node, + using `tm2` and `gnovm`. +- `gnovm` - contains the implementation of the Gno programming language and its + Virtual Machine, together with their standard libraries and tests. +- `tm2` - contains a fork of the [Tendermint consensus engine](https://docs.tendermint.com/v0.34/introduction/what-is-tendermint.html) with different expectations. ## How do I? diff --git a/Dockerfile b/Dockerfile index 70e2d01bf04..feff6d55ad2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,14 +10,14 @@ RUN go build -o ./build/gnokey ./gno.land/cmd/gnokey RUN go build -o ./build/gnofaucet ./gno.land/cmd/gnofaucet RUN go build -o ./build/gnoweb ./gno.land/cmd/gnoweb RUN go build -o ./build/gno ./gnovm/cmd/gno -RUN go build -o ./build/gnotxsync ./gno.land/cmd/gnotxsync RUN ls -la ./build ADD . /opt/gno/src/ RUN rm -rf /opt/gno/src/.git # runtime-base + runtime-tls FROM debian:stable-slim AS runtime-base -ENV PATH="${PATH}:/opt/gno/bin" +ENV PATH="${PATH}:/opt/gno/bin" \ + GNOROOT="/opt/gno/src" WORKDIR /opt/gno/src FROM runtime-base AS runtime-tls RUN apt-get update && apt-get install -y expect ca-certificates && update-ca-certificates @@ -42,10 +42,6 @@ COPY --from=build /opt/build/build/gnofaucet /opt/gno/bin/ ENTRYPOINT ["gnofaucet"] EXPOSE 5050 -FROM runtime-tls AS gnotxsync-slim -COPY --from=build /opt/build/build/gnotxsync /opt/gno/bin/ -ENTRYPOINT ["gnotxsync"] - FROM runtime-tls AS gnoweb-slim COPY --from=build /opt/build/build/gnoweb /opt/gno/bin/ COPY --from=build /opt/gno/src/gno.land/cmd/gnoweb /opt/gno/src/gnoweb diff --git a/LICENSE.md b/LICENSE.md index e85e3c4536b..5c705e516c8 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,8 @@ -Each sub-project folder has its own license terms. +Each project subdirectory has its own license terms. -* gnovm, gno.land - copyleft derived from AGPL3.0 -* tm2 - Apache2.0 +Gno Network Public License 3.0 (copyleft; derivative work, forked from AGPL 3.0) +- [Gno.land](gno.land/LICENSE.md) +- [GnoVM](gnovm/LICENSE.md) + +Apache 2.0 +- [TM2](tm2/LICENSE.md) diff --git a/Makefile b/Makefile index 358094e5c4a..15fef433b75 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,37 @@ .PHONY: help help: @echo "Available make commands:" - @cat Makefile | grep '^[a-z][^:]*:' | cut -d: -f1 | sort | sed 's/^/ /' + @cat Makefile | grep '^[a-z][^:]*:' | grep -v 'install_' | cut -d: -f1 | sort | sed 's/^/ /' rundep=go run -modfile misc/devdeps/go.mod .PHONY: install -install: install_gnokey install_gno +install: install.gnokey install.gno + @if ! command -v gnodev > /dev/null; then \ + echo ------------------------------; \ + echo "For local realm development, gnodev is recommended: https://docs.gno.land/gno-tooling/cli/gno-tooling-gnodev"; \ + echo "You can install it by calling 'make install.gnodev'"; \ + fi # shortcuts to frequently used commands from sub-components. -install_gnokey: +.PHONY: install.gnokey +install.gnokey: $(MAKE) --no-print-directory -C ./gno.land install.gnokey @echo "[+] 'gnokey' is installed. more info in ./gno.land/." -install_gno: +.PHONY: install.gno +install.gno: $(MAKE) --no-print-directory -C ./gnovm install @echo "[+] 'gno' is installed. more info in ./gnovm/." +.PHONY: install.gnodev +install.gnodev: + $(MAKE) --no-print-directory -C ./contribs install.gnodev + @echo "[+] 'gnodev' is installed." + +# old aliases +.PHONY: install_gnokey +install_gnokey: install.gnokey +.PHONY: install_gno +install_gno: install.gno .PHONY: test test: test.components test.docker @@ -25,6 +42,7 @@ test.components: $(MAKE) --no-print-directory -C gnovm test $(MAKE) --no-print-directory -C gno.land test $(MAKE) --no-print-directory -C examples test + $(MAKE) --no-print-directory -C misc test .PHONY: test.docker test.docker: @@ -36,11 +54,15 @@ test.docker: .PHONY: fmt fmt: - $(MAKE) --no-print-directory -C tm2 fmt - $(MAKE) --no-print-directory -C gnovm fmt - $(MAKE) --no-print-directory -C gno.land fmt + $(MAKE) --no-print-directory -C tm2 fmt imports + $(MAKE) --no-print-directory -C gnovm fmt imports + $(MAKE) --no-print-directory -C gno.land fmt imports $(MAKE) --no-print-directory -C examples fmt .PHONY: lint lint: $(rundep) github.com/golangci/golangci-lint/cmd/golangci-lint run --config .github/golangci.yml + +.PHONY: tidy +tidy: + $(MAKE) --no-print-directory -C misc tidy diff --git a/PHILOSOPHY.md b/PHILOSOPHY.md index c55d479f741..fb50860ed40 100644 --- a/PHILOSOPHY.md +++ b/PHILOSOPHY.md @@ -5,7 +5,7 @@ * Readability is paramount - beautiful is better than fast. * Minimal code - keep total footprint small. * Minimal dependencies - all dependencies must get audited, and become part of the repo. - * Modular dependencies - whereever reasonable, make components modular. + * Modular dependencies - wherever reasonable, make components modular. * Finished - software projects that don't become finished are projects that are forever vulnerable. One of the primary goals of the Gno language and related works is to become finished within a reasonable timeframe. @@ -33,7 +33,11 @@ ## CLI Philosophy * No envs. - * No short flags. + * No short flags, with the following exceptions: + * `-h` for showing help + * `-v` for being verbose + * mimicking the short flags of Go commands + * after software maturity * No /bin/ calls. * No process forks. * Struct-based command options. diff --git a/README.md b/README.md index 99634f90a0d..987a24f5b66 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,76 @@ # Gno -At first, there was Bitcoin, out of entropy soup of the greater All. -Then, there was Ethereum, which was created in the likeness of Bitcoin, -but made Turing complete. +> At first, there was Bitcoin, out of entropy soup of the greater All. +> Then, there was Ethereum, which was created in the likeness of Bitcoin, +> but made Turing complete. +> +> Among these were Tendermint and Cosmos to engineer robust PoS and IBC. +> Then came Gno upon Cosmos and there spring forth Gnoland, +> simulated by the Gnomes of the Greater Resistance. + +Gno is an interpreted and fully-deterministic implementation of the Go +programming language, designed to build succinct and composable smart contracts. +The first blockchain to use it is Gno.land, a +[Proof of Contribution](./docs/concepts/proof-of-contribution.md)-based chain, backed by +a variation of the [Tendermint](https://docs.tendermint.com/v0.34/introduction/what-is-tendermint.html) +consensus engine. -Among these were Tendermint and Cosmos to engineer robust PoS and IBC. -Then came Gno upon Cosmos and there spring forth Gnoland, -simulated by the Gnomes of the Greater Resistance. +## Getting started -## Discover +If you haven't already, take a moment to check out our [website](https://gno.land/). -* [examples](./examples) - smart-contract examples and guides for new Gno developers. -* [gnovm](./gnovm) - GnoVM and Gnolang. -* [gno.land](./gno.land) - Gno.land blockchain and tools. -* [tm2](./tm2) - Tendermint2. +> The website is a deployment of our [gnoweb](./gno.land/cmd/gnoweb) frontend; you +> can use it to check out +> [some](https://test3.gno.land/r/demo/boards) +> [example](https://test3.gno.land/r/gnoland/blog) +> [contracts](https://test3.gno.land/r/demo/users). +> +> Use the `[source]` button in the header to inspect the program's source; use +> the `[help]` button to view how you can use [`gnokey`](./gno.land/cmd/gnokey) +> to interact with the chain from your command line. -## Getting started +If you have already played around with the website, use our +[Getting Started](https://github.com/gnolang/getting-started) guide to learn how +to write and deploy your first smart contract. No local set-up required! -Start your journey with Gno.land by: -- using the [`gnoweb`](./gno.land/cmd/gnoweb) interface on the [latest testnet (test3.gno.land)](https://test3.gno.land/), -- sending transactions with [`gnokey`](./gno.land/cmd/gnokey), -- writing smart-contracts with [`gno` (ex `gnodev`)](./gnovm/cmd/gno). +Once you're done, learn how to set up your local environment with the +[quickstart guide](./examples/gno.land/r/demo/boards/README.md) and the +[contributing guide](./CONTRIBUTING.md). -Also, see the [quickstart guide](https://github.com/gnolang/gno/blob/master/examples/gno.land/r/demo/boards/README.md). +You can discover additional details about current tools and Gno documentation on +our [official documentation](https://docs.gno.land). Additionally, the [awesome-gno](https://github.com/gnolang/awesome-gno) +repository offers more resources to dig into. We are eager to see your first PR! -## Contact +## Gno Playground + + play.gno.land + +

- * Discord: https://discord.gg/YFtMjWwUN7 <-- join now - * Gnoland: https://gno.land/r/demo/boards:testboard - * Telegram: https://t.me/gnoland - * Twitter: https://twitter.com/_gnoland +[Gno Playground](https://play.gno.land), available at [play.gno.land](https://play.gno.land), is a web app that allows users to write, share, and deploy Gno code. Developers can seamlessly test, debug, and deploy realms and packages on Gno.land, while being able to collaborate with peers to work on projects together and seek assistance. A key feature of Gno Playground is the ability to get started without the need to install any tools or manage any services, offering immediate access and convenience for users. + +## Repository structure + +* [examples](./examples) - Smart-contract examples and guides for new Gno developers. +* [gnovm](./gnovm) - GnoVM and Gnolang. +* [gno.land](./gno.land) - Gno.land blockchain and tools. +* [tm2](./tm2) - Tendermint2. +* [docs](./docs) - Official documentation, deployed under [docs.gno.land](https://docs.gno.land). +* [contribs](./contribs) - Collection of enhanced tools for Gno. + +## Socials & Contact + +* [**Discord**](https://discord.gg/YFtMjWwUN7): good for general chat-based + conversations, as well as for asking support on developing with Gno. +* [**Reddit**](https://www.reddit.com/r/gnoland): more "permanent" and + forum-style discussions. Feel free to post anything Gno-related, as well as + any question related to Gno programming! +* [**Telegram**](https://t.me/gnoland): unofficial Telegram group. +* [**Twitter**](https://twitter.com/_gnoland): official Twitter account. Follow + us to know about new developments, events & official announcements about Gno! +* [**YouTube**](https://www.youtube.com/@_gnoland): here we post all of our + video content, like workshops, talks and public development calls. Follow + along on our development journey!
Short doc about all the commands @@ -38,12 +79,10 @@ Also, see the [quickstart guide](https://github.com/gnolang/gno/blob/master/exam * [gnokey](./gno.land/cmd/gnokey) - key manipulation, also general interaction with gnoland * [gnoland](./gno.land/cmd/gnoland) - runs the blockchain node * [gnoweb](./gno.land/cmd/gnoweb) - serves gno website, along with user-defined content - * [logos](./misc/logos) - intended to be used as a browser Developer commands: * [gno](./gnovm/cmd/gno) - handy tool for developing gno packages & realms - * [gnotxsync](./gno.land/cmd/gnotxsync) - importing/exporting transactions from local blockchain node storage * [goscan](./misc/goscan) - dumps imports from specified file’s AST * [genproto](./misc/genproto) - helper for generating .proto implementations * [gnofaucet](./gno.land/cmd/gnofaucet) - serves GNOT faucet @@ -52,28 +91,28 @@ Also, see the [quickstart guide](https://github.com/gnolang/gno/blob/master/exam
CI/CD/Tools badges and links GitHub Actions: - + * [![gno.land](https://github.com/gnolang/gno/actions/workflows/gnoland.yml/badge.svg)](https://github.com/gnolang/gno/actions/workflows/gnoland.yml) * [![gnovm](https://github.com/gnolang/gno/actions/workflows/gnovm.yml/badge.svg)](https://github.com/gnolang/gno/actions/workflows/gnovm.yml) * [![tm2](https://github.com/gnolang/gno/actions/workflows/tm2.yml/badge.svg)](https://github.com/gnolang/gno/actions/workflows/tm2.yml) * [![examples](https://github.com/gnolang/gno/actions/workflows/examples.yml/badge.svg)](https://github.com/gnolang/gno/actions/workflows/examples.yml) * [![docker](https://github.com/gnolang/gno/actions/workflows/docker.yml/badge.svg)](https://github.com/gnolang/gno/actions/workflows/docker.yml) - + Codecov: - + * General: [![codecov](https://codecov.io/gh/gnolang/gno/branch/master/graph/badge.svg?token=HPP82HR1P4)](https://codecov.io/gh/gnolang/gno) * tm2: [![codecov](https://codecov.io/gh/gnolang/gno/branch/master/graph/badge.svg?token=HPP82HR1P4&flag=tm2)](https://codecov.io/gh/gnolang/gno/tree/master/tm2) * gnovm: [![codecov](https://codecov.io/gh/gnolang/gno/branch/master/graph/badge.svg?token=HPP82HR1P4&flag=gnovm)](https://codecov.io/gh/gnolang/gno/tree/master/gnovm) * gno.land: [![codecov](https://codecov.io/gh/gnolang/gno/branch/master/graph/badge.svg?token=HPP82HR1P4&flag=gno.land)](https://codecov.io/gh/gnolang/gno/tree/master/gno.land) * examples: TODO - + Go Report Card: - + * [![Go Report Card](https://goreportcard.com/badge/github.com/gnolang/gno)](https://goreportcard.com/report/github.com/gnolang/gno) * tm2, gnovm, gno.land: TODO (blocked by tm2 split, because we need go mod workspaces) - + Pkg.go.dev - - * [![Go Reference](https://pkg.go.dev/badge/github.com/gnolang/gno.svg)](https://pkg.go.dev/github.com/gnolang/gno) - * TODO: host custom docs on gh-pages, to bypass license limitation + + * [![Go Reference](https://pkg.go.dev/badge/hey/google)](https://gnolang.github.io/gno/github.com/gnolang/gno.html) \ + (pkg.go.dev will not show our repository as it has a license it doesn't recognise)
diff --git a/contribs/Makefile b/contribs/Makefile new file mode 100644 index 00000000000..a7414771ffa --- /dev/null +++ b/contribs/Makefile @@ -0,0 +1,40 @@ +.PHONY: help +help: + @echo "Available make commands:" + @cat Makefile | grep '^[a-z][^:]*:' | cut -d: -f1 | sort | sed 's/^/ /' + +.PHONY: install +install: install.gnomd install.gnodev + +install.gnomd:; cd gnomd && go install . +install.gnodev:; $(MAKE) -C ./gnodev install + +.PHONY: clean +clean: + rm -rf build + +######################################## +# Dev tools +rundep=go run -modfile ../misc/devdeps/go.mod + +.PHONY: fmt +GOFMT_FLAGS ?= -w +fmt: + $(rundep) mvdan.cc/gofumpt $(GOFMT_FLAGS) . + +.PHONY: tidy +tidy: + @for gomod in `find . -name go.mod`; do ( \ + dir=`dirname $$gomod`; \ + set -xe; \ + cd $$dir; \ + go mod tidy -v; \ + ); done + +######################################## +# Test suite +GOTEST_FLAGS ?= -v -p 1 -timeout=30m + +.PHONY: test +test: + @echo "nothing to do." diff --git a/contribs/README.md b/contribs/README.md new file mode 100644 index 00000000000..c481af0f4aa --- /dev/null +++ b/contribs/README.md @@ -0,0 +1,17 @@ +# Gno Contribs + +This directory houses additional commands and tools designed to enhance your Gno experience. +These tools can range from simple wrappers for `gno`, `gnoland`, and `gnokey` to complete applications. +Some may be Go binaries with their own `go.mod` files, allowing the use of specialized libraries, +while others may be shell scripts or programs written in different languages. + +## Contributing Guidelines + +If you'd like to contribute a tool to Gno Contribs, please follow these guidelines: + +1. **Naming**: Choose a clear and concise name for your tool, starting with one of the prefixes: `gno`, `gnoland`, or `gnokey`, + followed by a short descriptive word or abbreviation. + This naming convention helps users identify the purpose of the tool easily. + +2. **User-Friendly**: Ensure that your tool is user-friendly and follows a similar style to other Gno tools, + providing a consistent experience for users. diff --git a/contribs/gnodev/Makefile b/contribs/gnodev/Makefile new file mode 100644 index 00000000000..23fb22a372d --- /dev/null +++ b/contribs/gnodev/Makefile @@ -0,0 +1,9 @@ +GNOROOT_DIR ?= $(abspath $(lastword $(MAKEFILE_LIST))/../../../) + +GOBUILD_FLAGS := -ldflags "-X github.com/gnolang/gno/gnovm/pkg/gnoenv._GNOROOT=$(GNOROOT_DIR)" + +install: + go install $(GOBUILD_FLAGS) . + +build: + go build $(GOBUILD_FLAGS) -o build/gnodev ./cmd/gno diff --git a/contribs/gnodev/README.md b/contribs/gnodev/README.md new file mode 100644 index 00000000000..440713d45cb --- /dev/null +++ b/contribs/gnodev/README.md @@ -0,0 +1,28 @@ +## `gnodev`: Your Gno Companion Tool + +`gnodev` is designed to be a robust and user-friendly tool in your realm package development journey, streamlining your workflow and enhancing productivity. + +### Synopsis +**gnodev** [**-minimal**] [**-no-watch**] [**PKG_PATH ...**] + +### Features +- **In-Memory Node**: Gnodev starts an in-memory node, and automatically loads + the **examples** folder and any user-specified paths. +- **Web Interface Server**: Starts a `gnoweb` server on `localhost:8888`. +- **Hot Reload**: Monitors the example packages folder and specified directories for file changes, + reloading the package and automatically restarting the node as needed. +- **State Maintenance**: Ensures the current state is preserved by replaying all transactions. + +### Commands +While `gnodev` is running, the user can trigger specific actions by pressing +the following combinations: +- **H**: Display help information. +- **R**: Reload the node, without resetting the state. +- **Ctrl+R**: Reset the current node state. +- **Ctrl+C**: Exit `gnodev`. + +### Example Folder Loading +The **example** package folder is loaded automatically. If working within this folder, you don't have to specify any additional paths to `gnodev`. Use `--minimal` to prevent this. + +### Installation +Run `make install` to install `gnodev`. diff --git a/contribs/gnodev/go.mod b/contribs/gnodev/go.mod new file mode 100644 index 00000000000..71838979ec4 --- /dev/null +++ b/contribs/gnodev/go.mod @@ -0,0 +1,72 @@ +module github.com/gnolang/gno/contribs/gnodev + +go 1.20 + +replace github.com/gnolang/gno => ../.. + +require ( + github.com/fsnotify/fsnotify v1.7.0 + github.com/gnolang/gno v0.0.0-00010101000000-000000000000 + go.uber.org/zap v1.24.0 + golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 + golang.org/x/term v0.16.0 +) + +require ( + dario.cat/mergo v1.0.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/btcsuite/btcd/btcutil v1.1.3 // indirect + github.com/cespare/xxhash v1.1.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cockroachdb/apd/v3 v3.2.1 // indirect + github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/dgraph-io/badger/v3 v3.2103.5 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect + github.com/gnolang/goleveldb v0.0.9 // indirect + github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/glog v1.1.0 // indirect + github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/flatbuffers v1.12.1 // indirect + github.com/gorilla/mux v1.8.1 // indirect + github.com/gorilla/securecookie v1.1.1 // indirect + github.com/gorilla/sessions v1.2.1 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/gotuna/gotuna v0.6.0 // indirect + github.com/jaekwon/testify v1.6.1 // indirect + github.com/jmhodges/levigo v1.0.0 // indirect + github.com/klauspost/compress v1.12.3 // indirect + github.com/kr/pretty v0.2.1 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/linxGnu/grocksdb v1.8.11 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/peterbourgon/ff/v3 v3.4.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rs/cors v1.10.1 // indirect + github.com/stretchr/testify v1.8.4 // indirect + github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect + github.com/zondax/hid v0.9.2 // indirect + github.com/zondax/ledger-go v0.14.3 // indirect + go.etcd.io/bbolt v1.3.8 // indirect + go.opencensus.io v0.22.5 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.10.0 // indirect + go.uber.org/zap/exp v0.1.0 // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.17.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c // indirect + google.golang.org/grpc v1.58.3 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/contribs/gnodev/go.sum b/contribs/gnodev/go.sum new file mode 100644 index 00000000000..ca5774fa7ad --- /dev/null +++ b/contribs/gnodev/go.sum @@ -0,0 +1,325 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= +github.com/btcsuite/btcd v0.23.0 h1:V2/ZgjfDFIygAX3ZapeigkVBoVUtOJKSwrhZdlpSvaA= +github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= +github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= +github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= +github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +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/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= +github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= +github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= +github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gnolang/goleveldb v0.0.9 h1:Q7rGko9oXMKtQA+Apeeed5a3sjba/mcDhzJGoTVLCKE= +github.com/gnolang/goleveldb v0.0.9/go.mod h1:Dz6p9bmpy/FBESTgduiThZt5mToVDipcHGzj/zUOo8E= +github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk= +github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= +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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= +github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/gorilla/csrf v1.7.0/go.mod h1:+a/4tCmqhG6/w4oafeAZ9pEa3/NZOWYVbD9fV0FwIQA= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/gotuna/gotuna v0.6.0 h1:N1lQKXEi/lwRp8u3sccTYLhzOffA4QasExz/1M5Riws= +github.com/gotuna/gotuna v0.6.0/go.mod h1:F/ecRt29ChB6Ycy1AFIBpBiMNK0j7Heq+gFbLWquhjc= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jaekwon/testify v1.6.1 h1:4AtAJcR9GzXN5W4DdY7ie74iCPiJV1JJUJL90t2ZUyw= +github.com/jaekwon/testify v1.6.1/go.mod h1:Oun0RXIHI7osufabQ60i4Lqkj0GXLbqI1I7kgzBNm1U= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/linxGnu/grocksdb v1.8.11 h1:BGol9e5gB1BrsTvOxloC88pe70TCqgrfLNwkyWW0kD8= +github.com/linxGnu/grocksdb v1.8.11/go.mod h1:xZCIb5Muw+nhbDK4Y5UJuOrin5MceOuiXkVUR7vp4WY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 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.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/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= +github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +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/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= +github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= +github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= +go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap/exp v0.1.0 h1:Ol9zQNvAEAgFHSBiR5LlwS9Xq8u5QF+7HBwNHUB8rcI= +go.uber.org/zap/exp v0.1.0/go.mod h1:z/0T3As39ttolxZGOsvk1OEvQfwwfTZpmV9YTp+VAkc= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +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-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c h1:jHkCUWkseRf+W+edG5hMzr/Uh1xkDREY4caybAq4dpY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +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/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/contribs/gnodev/main.go b/contribs/gnodev/main.go new file mode 100644 index 00000000000..6ae2633c6f3 --- /dev/null +++ b/contribs/gnodev/main.go @@ -0,0 +1,377 @@ +package main + +import ( + "context" + "flag" + "fmt" + "io" + "net" + "net/http" + "os" + "path/filepath" + "time" + + "github.com/fsnotify/fsnotify" + "github.com/gnolang/gno/contribs/gnodev/pkg/dev" + gnodev "github.com/gnolang/gno/contribs/gnodev/pkg/dev" + "github.com/gnolang/gno/contribs/gnodev/pkg/rawterm" + "github.com/gnolang/gno/gno.land/pkg/gnoweb" + "github.com/gnolang/gno/gno.land/pkg/log" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/gnovm/pkg/gnomod" + "github.com/gnolang/gno/tm2/pkg/commands" + osm "github.com/gnolang/gno/tm2/pkg/os" + "go.uber.org/zap/zapcore" +) + +const ( + NodeLogName = "Node" + WebLogName = "GnoWeb" + KeyPressLogName = "KeyPress" + HotReloadLogName = "HotReload" +) + +type devCfg struct { + webListenerAddr string + minimal bool + verbose bool + noWatch bool +} + +var defaultDevOptions = &devCfg{ + webListenerAddr: "127.0.0.1:8888", +} + +func main() { + cfg := &devCfg{} + + stdio := commands.NewDefaultIO() + cmd := commands.NewCommand( + commands.Metadata{ + Name: "gnodev", + ShortUsage: "gnodev [flags] [path ...]", + ShortHelp: "Runs an in-memory node and gno.land web server for development purposes.", + LongHelp: `The gnodev command starts an in-memory node and a gno.land web interface +primarily for realm package development. It automatically loads the example folder and any +additional specified paths.`, + }, + cfg, + func(_ context.Context, args []string) error { + return execDev(cfg, args, stdio) + }) + + cmd.Execute(context.Background(), os.Args[1:]) +} + +func (c *devCfg) RegisterFlags(fs *flag.FlagSet) { + fs.StringVar( + &c.webListenerAddr, + "web-listener", + defaultDevOptions.webListenerAddr, + "web server listening address", + ) + + fs.BoolVar( + &c.minimal, + "minimal", + defaultDevOptions.verbose, + "do not load example folder packages", + ) + + fs.BoolVar( + &c.verbose, + "verbose", + defaultDevOptions.verbose, + "verbose output when deving", + ) + + fs.BoolVar( + &c.noWatch, + "no-watch", + defaultDevOptions.noWatch, + "do not watch for files change", + ) +} + +func execDev(cfg *devCfg, args []string, io commands.IO) error { + ctx, cancel := context.WithCancelCause(context.Background()) + defer cancel(nil) + + // guess root dir + gnoroot := gnoenv.RootDir() + + // Check and Parse packages + pkgpaths, err := parseArgsPackages(args) + if err != nil { + return fmt.Errorf("unable to parse package paths: %w", err) + } + + if !cfg.minimal { + examplesDir := filepath.Join(gnoroot, "examples") + pkgpaths = append(pkgpaths, examplesDir) + } + + // Setup Raw Terminal + rt, restore, err := setupRawTerm(io) + if err != nil { + return fmt.Errorf("unable to init raw term: %w", err) + } + defer restore() + + // Setup trap signal + osm.TrapSignal(func() { + restore() + cancel(nil) + }) + + // Setup Dev Node + // XXX: find a good way to export or display node logs + devNode, err := setupDevNode(ctx, rt, pkgpaths) + if err != nil { + return err + } + defer devNode.Close() + + rt.Taskf(NodeLogName, "Listener: %s\n", devNode.GetRemoteAddress()) + rt.Taskf(NodeLogName, "Default Address: %s\n", gnodev.DefaultCreator.String()) + rt.Taskf(NodeLogName, "Chain ID: %s\n", devNode.Config().ChainID()) + + // Setup packages watcher + pathChangeCh := make(chan []string, 1) + go func() { + defer close(pathChangeCh) + + cancel(runPkgsWatcher(ctx, cfg, devNode.ListPkgs(), pathChangeCh)) + }() + + // Setup GnoWeb listener + l, err := net.Listen("tcp", cfg.webListenerAddr) + if err != nil { + return fmt.Errorf("unable to listen to %q: %w", cfg.webListenerAddr, err) + } + defer l.Close() + + // Run GnoWeb server + go func() { + cancel(serveGnoWebServer(l, devNode, rt)) + }() + + rt.Taskf(WebLogName, "Listener: http://%s\n", l.Addr()) + + // GnoDev should be ready, run event loop + rt.Taskf("[Ready]", "for commands and help, press `h`") + + // Run the main event loop + return runEventLoop(ctx, cfg, rt, devNode, pathChangeCh) +} + +// XXX: Automatize this the same way command does +func printHelper(rt *rawterm.RawTerm) { + rt.Taskf("Helper", ` +Gno Dev Helper: + h, H Help - display this message + r, R Reload - Reload all packages to take change into account. + Ctrl+R Reset - Reset application state. + Ctrl+C Exit - Exit the application +`) +} + +func runEventLoop(ctx context.Context, + cfg *devCfg, + rt *rawterm.RawTerm, + dnode *dev.Node, + pathsCh <-chan []string, +) error { + nodeOut := rt.NamespacedWriter(NodeLogName) + keyOut := rt.NamespacedWriter(KeyPressLogName) + + keyPressCh := listenForKeyPress(keyOut, rt) + for { + var err error + + select { + case <-ctx.Done(): + return context.Cause(ctx) + case paths, ok := <-pathsCh: + if !ok { + return nil + } + + if cfg.verbose { + for _, path := range paths { + rt.Taskf(HotReloadLogName, "path %q has been modified", path) + } + } + + fmt.Fprintln(nodeOut, "Loading package updates...") + if err = dnode.UpdatePackages(paths...); err != nil { + checkForError(rt, err) + continue + } + + fmt.Fprintln(nodeOut, "Reloading...") + err = dnode.Reload(ctx) + checkForError(rt, err) + case key, ok := <-keyPressCh: + if !ok { + return nil + } + + if cfg.verbose { + fmt.Fprintf(keyOut, "<%s>\n", key.String()) + } + + switch key.Upper() { + case rawterm.KeyH: + printHelper(rt) + case rawterm.KeyR: + fmt.Fprintln(nodeOut, "Reloading all packages...") + checkForError(nodeOut, dnode.ReloadAll(ctx)) + case rawterm.KeyCtrlR: + fmt.Fprintln(nodeOut, "Reseting state...") + checkForError(nodeOut, dnode.Reset(ctx)) + case rawterm.KeyCtrlC: + return nil + default: + } + + // Listen for the next keypress + keyPressCh = listenForKeyPress(keyOut, rt) + } + } +} + +func runPkgsWatcher(ctx context.Context, cfg *devCfg, pkgs []gnomod.Pkg, changedPathsCh chan<- []string) error { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return fmt.Errorf("unable to watch files: %w", err) + } + + if cfg.noWatch { + // noop watcher, wait until context has been cancel + <-ctx.Done() + return ctx.Err() + } + + for _, pkg := range pkgs { + if err := watcher.Add(pkg.Dir); err != nil { + return fmt.Errorf("unable to watch %q: %w", pkg.Dir, err) + } + } + + const timeout = time.Millisecond * 500 + + var debounceTimer <-chan time.Time + pathList := []string{} + + for { + select { + case <-ctx.Done(): + return ctx.Err() + case watchErr := <-watcher.Errors: + return fmt.Errorf("watch error: %w", watchErr) + case <-debounceTimer: + changedPathsCh <- pathList + // Reset pathList and debounceTimer + pathList = []string{} + debounceTimer = nil + case evt := <-watcher.Events: + if evt.Op != fsnotify.Write { + continue + } + + pathList = append(pathList, evt.Name) + debounceTimer = time.After(timeout) + } + } +} + +func setupRawTerm(io commands.IO) (rt *rawterm.RawTerm, restore func() error, err error) { + rt = rawterm.NewRawTerm() + + restore, err = rt.Init() + if err != nil { + return nil, nil, err + } + + // correctly format output for terminal + io.SetOut(commands.WriteNopCloser(rt)) + + return rt, restore, nil +} + +// setupDevNode initializes and returns a new DevNode. +func setupDevNode(ctx context.Context, rt *rawterm.RawTerm, pkgspath []string) (*gnodev.Node, error) { + nodeOut := rt.NamespacedWriter("Node") + + zapLogger := log.NewZapConsoleLogger(nodeOut, zapcore.ErrorLevel) + + return gnodev.NewDevNode(ctx, log.ZapLoggerToSlog(zapLogger), pkgspath) +} + +// setupGnowebServer initializes and starts the Gnoweb server. +func serveGnoWebServer(l net.Listener, dnode *gnodev.Node, rt *rawterm.RawTerm) error { + var server http.Server + + webConfig := gnoweb.NewDefaultConfig() + webConfig.RemoteAddr = dnode.GetRemoteAddress() + webConfig.HelpChainID = dnode.Config().ChainID() + webConfig.HelpRemote = dnode.GetRemoteAddress() + + zapLogger := log.NewZapConsoleLogger(rt.NamespacedWriter("GnoWeb"), zapcore.DebugLevel) + + app := gnoweb.MakeApp(log.ZapLoggerToSlog(zapLogger), webConfig) + + server.ReadHeaderTimeout = 60 * time.Second + server.Handler = app.Router + + if err := server.Serve(l); err != nil { + return fmt.Errorf("unable to serve GnoWeb: %w", err) + } + + return nil +} + +func parseArgsPackages(args []string) (paths []string, err error) { + paths = make([]string, len(args)) + for i, arg := range args { + abspath, err := filepath.Abs(arg) + if err != nil { + return nil, fmt.Errorf("invalid path %q: %w", arg, err) + } + + ppath, err := gnomod.FindRootDir(abspath) + if err != nil { + return nil, fmt.Errorf("unable to find root dir of %q: %w", abspath, err) + } + + paths[i] = ppath + } + + return paths, nil +} + +func listenForKeyPress(w io.Writer, rt *rawterm.RawTerm) <-chan rawterm.KeyPress { + cc := make(chan rawterm.KeyPress, 1) + go func() { + defer close(cc) + key, err := rt.ReadKeyPress() + if err != nil { + fmt.Fprintf(w, "unable to read keypress: %s\n", err.Error()) + return + } + + cc <- key + }() + + return cc +} + +func checkForError(w io.Writer, err error) { + if err != nil { + fmt.Fprintf(w, "[ERROR] - %s\n", err.Error()) + return + } + + fmt.Fprintln(w, "[DONE]") +} diff --git a/contribs/gnodev/pkg/dev/node.go b/contribs/gnodev/pkg/dev/node.go new file mode 100644 index 00000000000..c1eddc7c91d --- /dev/null +++ b/contribs/gnodev/pkg/dev/node.go @@ -0,0 +1,423 @@ +package dev + +import ( + "context" + "fmt" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/gno.land/pkg/integration" + vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/pkg/gnomod" + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/bft/node" + "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" + bft "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/std" + "golang.org/x/exp/slog" + // backup "github.com/gnolang/tx-archive/backup/client" + // restore "github.com/gnolang/tx-archive/restore/client" +) + +const gnoDevChainID = "tendermint_test" // XXX: this is hardcoded and cannot be change bellow + +// Node is not thread safe +type Node struct { + *node.Node + + client client.Client + logger *slog.Logger + pkgs PkgsMap // path -> pkg + // keep track of number of loaded package to be able to skip them on restore + loadedPackages int +} + +var ( + DefaultFee = std.NewFee(50000, std.MustParseCoin("1000000ugnot")) + DefaultCreator = crypto.MustAddressFromString(integration.DefaultAccount_Address) + DefaultBalance = []gnoland.Balance{ + { + Address: DefaultCreator, + Amount: std.MustParseCoins("10000000000000ugnot"), + }, + } +) + +func NewDevNode(ctx context.Context, logger *slog.Logger, pkgslist []string) (*Node, error) { + mpkgs, err := newPkgsMap(pkgslist) + if err != nil { + return nil, fmt.Errorf("unable map pkgs list: %w", err) + } + + pkgsTxs, err := mpkgs.Load(DefaultCreator, DefaultFee, nil) + if err != nil { + return nil, fmt.Errorf("unable to load genesis packages: %w", err) + } + + // generate genesis state + genesis := gnoland.GnoGenesisState{ + Balances: DefaultBalance, + Txs: pkgsTxs, + } + + node, err := newNode(logger, genesis) + if err != nil { + return nil, fmt.Errorf("unable to create the node: %w", err) + } + client := client.NewLocal(node) + + if err := node.Start(); err != nil { + return nil, fmt.Errorf("unable to start node: %w", err) + } + + // Wait for readiness + select { + case <-gnoland.GetNodeReadiness(node): // ok + case <-ctx.Done(): + return nil, ctx.Err() + } + + return &Node{ + Node: node, + + client: client, + pkgs: mpkgs, + logger: logger, + loadedPackages: len(pkgsTxs), + }, nil +} + +func (d *Node) getLatestBlockNumber() uint64 { + return uint64(d.Node.BlockStore().Height()) +} + +func (d *Node) Close() error { + return d.Node.Stop() +} + +func (d *Node) ListPkgs() []gnomod.Pkg { + return d.pkgs.toList() +} + +func (d *Node) GetNodeReadiness() <-chan struct{} { + return gnoland.GetNodeReadiness(d.Node) +} + +func (d *Node) GetRemoteAddress() string { + return d.Node.Config().RPC.ListenAddress +} + +// UpdatePackages updates the currently known packages. It will be taken into +// consideration in the next reload of the node. +func (d *Node) UpdatePackages(paths ...string) error { + for _, path := range paths { + // List all packages from target path + pkgslist, err := gnomod.ListPkgs(path) + if err != nil { + return fmt.Errorf("failed to list gno packages for %q: %w", path, err) + } + + // Update or add package in the current known list. + for _, pkg := range pkgslist { + d.pkgs[pkg.Dir] = pkg + } + } + + return nil +} + +// Reset stops the node, if running, and reloads it with a new genesis state, +// effectively ignoring the current state. +func (d *Node) Reset(ctx context.Context) error { + // Stop the node if it's currently running. + if d.Node.IsRunning() { + if err := d.Node.Stop(); err != nil { + return fmt.Errorf("unable to stop the node: %w", err) + } + } + + // Generate a new genesis state based on the current packages + txs, err := d.pkgs.Load(DefaultCreator, DefaultFee, nil) + if err != nil { + return fmt.Errorf("unable to load pkgs: %w", err) + } + + genesis := gnoland.GnoGenesisState{ + Balances: DefaultBalance, + Txs: txs, + } + + // Reset the node with the new genesis state. + return d.reset(ctx, genesis) +} + +// ReloadAll updates all currently known packages and then reloads the node. +func (d *Node) ReloadAll(ctx context.Context) error { + pkgs := d.ListPkgs() + paths := make([]string, len(pkgs)) + for i, pkg := range pkgs { + paths[i] = pkg.Dir + } + + if err := d.UpdatePackages(paths...); err != nil { + return fmt.Errorf("unable to reload packages: %w", err) + } + + return d.Reload(ctx) +} + +// Reload saves the current state, stops the node if running, starts a new node, +// and re-apply previously saved state along with packages updated by `UpdatePackages`. +// If any transaction, including 'addpkg', fails, it will be ignored. +// Use 'Reset' to completely reset the node's state in case of persistent errors. +func (d *Node) Reload(ctx context.Context) error { + // Get current blockstore state + state, err := d.getBlockStoreState(ctx) + if err != nil { + return fmt.Errorf("unable to save state: %s", err.Error()) + } + + // Stop the node if it's currently running. + if d.Node.IsRunning() { + if err := d.Node.Stop(); err != nil { + return fmt.Errorf("unable to stop the node: %w", err) + } + } + + // Load genesis packages + pkgsTxs, err := d.pkgs.Load(DefaultCreator, DefaultFee, nil) + if err != nil { + return fmt.Errorf("unable to load pkgs: %w", err) + } + + // Create genesis with loaded pkgs + previous state + genesis := gnoland.GnoGenesisState{ + Balances: DefaultBalance, + Txs: append(pkgsTxs, state...), + } + + // Reset the node with the new genesis state. + if err := d.reset(ctx, genesis); err != nil { + return fmt.Errorf("unable to reset the node: %w", err) + } + + d.logger.Info("reload done", "pkgs", len(pkgsTxs), "state applied", len(state)) + d.loadedPackages = len(pkgsTxs) + + return nil +} + +func (d *Node) reset(ctx context.Context, genesis gnoland.GnoGenesisState) error { + var err error + + // recoverError handles panics and converts them to errors. + recoverError := func() { + if r := recover(); r != nil { + panicErr, ok := r.(error) + if !ok { + panic(r) // Re-panic if not an error. + } + + err = panicErr + } + } + + createNode := func() { + defer recoverError() + + node, nodeErr := newNode(d.logger, genesis) + if nodeErr != nil { + err = fmt.Errorf("unable to create node: %w", nodeErr) + return + } + + if startErr := node.Start(); startErr != nil { + err = fmt.Errorf("unable to start the node: %w", startErr) + return + } + + d.Node = node + d.client = client.NewLocal(d.Node) + } + + // Execute node creation and handle any errors. + createNode() + if err != nil { + return err + } + + // Wait for the node to be ready + select { + case <-d.GetNodeReadiness(): // Ok + case <-ctx.Done(): + return ctx.Err() + } + + return err +} + +// GetBlockTransactions returns the transactions contained +// within the specified block, if any +func (d *Node) GetBlockTransactions(blockNum uint64) ([]std.Tx, error) { + int64BlockNum := int64(blockNum) + b, err := d.client.Block(&int64BlockNum) + if err != nil { + return []std.Tx{}, fmt.Errorf("unable to load block at height %d: %w", blockNum, err) // nothing to see here + } + + txs := make([]std.Tx, len(b.Block.Data.Txs)) + for i, encodedTx := range b.Block.Data.Txs { + var tx std.Tx + if unmarshalErr := amino.Unmarshal(encodedTx, &tx); unmarshalErr != nil { + return nil, fmt.Errorf("unable to unmarshal amino tx, %w", unmarshalErr) + } + + txs[i] = tx + } + + return txs, nil +} + +// GetBlockTransactions returns the transactions contained +// within the specified block, if any +// GetLatestBlockNumber returns the latest block height from the chain +func (d *Node) GetLatestBlockNumber() (uint64, error) { + return d.getLatestBlockNumber(), nil +} + +// SendTransaction executes a broadcast commit send +// of the specified transaction to the chain +func (d *Node) SendTransaction(tx *std.Tx) error { + aminoTx, err := amino.Marshal(tx) + if err != nil { + return fmt.Errorf("unable to marshal transaction to amino binary, %w", err) + } + + // we use BroadcastTxCommit to ensure to have one block with the given tx + res, err := d.client.BroadcastTxCommit(aminoTx) + if err != nil { + return fmt.Errorf("unable to broadcast transaction commit: %w", err) + } + + if res.CheckTx.Error != nil { + d.logger.Error("check tx error trace", "log", res.CheckTx.Log) + return fmt.Errorf("check transaction error: %w", res.CheckTx.Error) + } + + if res.DeliverTx.Error != nil { + d.logger.Error("deliver tx error trace", "log", res.CheckTx.Log) + return fmt.Errorf("deliver transaction error: %w", res.DeliverTx.Error) + } + + return nil +} + +func (n *Node) getBlockStoreState(ctx context.Context) ([]std.Tx, error) { + // get current genesis state + genesis := n.GenesisDoc().AppState.(gnoland.GnoGenesisState) + + state := genesis.Txs[n.loadedPackages:] // ignore previously loaded packages + lastBlock := n.getLatestBlockNumber() + var blocnum uint64 = 1 + for ; blocnum <= lastBlock; blocnum++ { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + txs, txErr := n.GetBlockTransactions(blocnum) + if txErr != nil { + return nil, fmt.Errorf("unable to fetch block transactions, %w", txErr) + } + + state = append(state, txs...) + } + + // override current state + return state, nil +} + +type PkgsMap map[string]gnomod.Pkg + +func newPkgsMap(paths []string) (PkgsMap, error) { + pkgs := make(map[string]gnomod.Pkg) + for _, path := range paths { + // list all packages from target path + pkgslist, err := gnomod.ListPkgs(path) + if err != nil { + return nil, fmt.Errorf("listing gno packages: %w", err) + } + + for _, pkg := range pkgslist { + if pkg.Dir == "" { + continue + } + + if _, ok := pkgs[pkg.Dir]; ok { + continue // skip + } + pkgs[pkg.Dir] = pkg + } + } + + // Filter out draft packages. + return pkgs, nil +} + +func (pm PkgsMap) toList() gnomod.PkgList { + list := make([]gnomod.Pkg, 0, len(pm)) + for _, pkg := range pm { + list = append(list, pkg) + } + return list +} + +func (pm PkgsMap) Load(creator bft.Address, fee std.Fee, deposit std.Coins) ([]std.Tx, error) { + pkgs := pm.toList() + + sorted, err := pkgs.Sort() + if err != nil { + return nil, fmt.Errorf("unable to sort pkgs: %w", err) + } + + nonDraft := sorted.GetNonDraftPkgs() + txs := []std.Tx{} + for _, pkg := range nonDraft { + // Open files in directory as MemPackage. + memPkg := gno.ReadMemPackage(pkg.Dir, pkg.Name) + if err := memPkg.Validate(); err != nil { + return nil, fmt.Errorf("invalid package: %w", err) + } + + // Create transaction + tx := std.Tx{ + Fee: fee, + Msgs: []std.Msg{ + vmm.MsgAddPackage{ + Creator: creator, + Package: memPkg, + Deposit: deposit, + }, + }, + } + + tx.Signatures = make([]std.Signature, len(tx.GetSigners())) + txs = append(txs, tx) + } + + return txs, nil +} + +func newNode(logger *slog.Logger, genesis gnoland.GnoGenesisState) (*node.Node, error) { + rootdir := gnoenv.RootDir() + + nodeConfig := gnoland.NewDefaultInMemoryNodeConfig(rootdir) + nodeConfig.SkipFailingGenesisTxs = true + nodeConfig.TMConfig.Consensus.SkipTimeoutCommit = false // avoid time drifting, see issue #1507 + + nodeConfig.Genesis.AppState = genesis + return gnoland.NewInMemoryNode(logger, nodeConfig) +} diff --git a/contribs/gnodev/pkg/rawterm/keypress.go b/contribs/gnodev/pkg/rawterm/keypress.go new file mode 100644 index 00000000000..20503476a9b --- /dev/null +++ b/contribs/gnodev/pkg/rawterm/keypress.go @@ -0,0 +1,55 @@ +package rawterm + +import ( + "fmt" + "unicode" +) + +type KeyPress byte + +// key representation +const ( + KeyNone KeyPress = 0 // None + KeyCtrlC KeyPress = '\x03' // Ctrl+C + KeyCtrlD KeyPress = '\x04' // Ctrl+D + KeyCtrlE KeyPress = '\x05' // Ctrl+E + KeyCtrlL KeyPress = '\x0c' // Ctrl+L + KeyCtrlO KeyPress = '\x0f' // Ctrl+O + KeyCtrlR KeyPress = '\x12' // Ctrl+R + KeyCtrlT KeyPress = '\x14' // Ctrl+T + + KeyH KeyPress = 'H' + KeyR KeyPress = 'R' +) + +func (k KeyPress) Upper() KeyPress { + return KeyPress(unicode.ToUpper(rune(k))) +} + +func (k KeyPress) String() string { + switch k { + case KeyNone: + return "Null" + case KeyCtrlC: + return "Ctrl+C" + case KeyCtrlD: + return "Ctrl+D" + case KeyCtrlE: + return "Ctrl+E" + case KeyCtrlL: + return "Ctrl+L" + case KeyCtrlO: + return "Ctrl+O" + case KeyCtrlR: + return "Ctrl+R" + case KeyCtrlT: + return "Ctrl+T" + default: + // For printable ASCII characters + if k > 0x20 && k < 0x7e { + return fmt.Sprintf("%c", k) + } + + return fmt.Sprintf("Unknown (0x%02x)", byte(k)) + } +} diff --git a/contribs/gnodev/pkg/rawterm/rawterm.go b/contribs/gnodev/pkg/rawterm/rawterm.go new file mode 100644 index 00000000000..f6d6e7534e2 --- /dev/null +++ b/contribs/gnodev/pkg/rawterm/rawterm.go @@ -0,0 +1,178 @@ +package rawterm + +import ( + "bytes" + "fmt" + "io" + "os" + "strings" + "sync" + + "golang.org/x/term" +) + +var CRLF = []byte{'\r', '\n'} + +// rawTerminal wraps an io.Writer, converting \n to \r\n +type RawTerm struct { + syncWriter sync.Mutex + + fsin *os.File + reader io.Reader + taskWriter TaskWriter +} + +func NewRawTerm() *RawTerm { + return &RawTerm{ + fsin: os.Stdin, + reader: os.Stdin, + taskWriter: &rawTaskWriter{os.Stdout}, + } +} + +func (rt *RawTerm) Init() (restore func() error, err error) { + fd := int(rt.fsin.Fd()) + oldstate, err := term.MakeRaw(fd) + if err != nil { + return nil, fmt.Errorf("unable to init raw term: %w", err) + } + + rt.reader = rt.fsin + rt.taskWriter = &columnTaskWriter{os.Stdout} + return func() error { + return term.Restore(fd, oldstate) + }, nil +} + +func (rt *RawTerm) Taskf(task string, format string, args ...interface{}) (n int, err error) { + format = strings.TrimSpace(format) + if len(args) > 0 { + str := fmt.Sprintf(format, args...) + return rt.taskWriter.WriteTask(task, []byte(str+"\n")) + } + + return rt.taskWriter.WriteTask(task, []byte(format+"\n")) +} + +func (rt *RawTerm) Write(buf []byte) (n int, err error) { + rt.syncWriter.Lock() + defer rt.syncWriter.Unlock() + + return rt.taskWriter.Write(buf) +} + +func (rt *RawTerm) WriteTask(name string, buf []byte) (n int, err error) { + rt.syncWriter.Lock() + defer rt.syncWriter.Unlock() + + return rt.taskWriter.WriteTask(name, buf) +} + +func (rt *RawTerm) NamespacedWriter(namepsace string) io.Writer { + return &namespaceWriter{namepsace, rt} +} + +func (rt *RawTerm) read(buf []byte) (n int, err error) { + return rt.fsin.Read(buf) +} + +func (rt *RawTerm) ReadKeyPress() (KeyPress, error) { + buf := make([]byte, 1) + if _, err := rt.read(buf); err != nil { + return KeyNone, err + } + + return KeyPress(buf[0]), nil +} + +type namespaceWriter struct { + namespace string + writer TaskWriter +} + +func (r *namespaceWriter) Write(buf []byte) (n int, err error) { + return r.writer.WriteTask(r.namespace, buf) +} + +type TaskWriter interface { + io.Writer + WriteTask(task string, buf []byte) (n int, err error) +} + +type columnTaskWriter struct { + writer io.Writer +} + +func (r *columnTaskWriter) Write(buf []byte) (n int, err error) { + return r.WriteTask("", buf) +} + +func (r *columnTaskWriter) WriteTask(left string, buf []byte) (n int, err error) { + var nline int + for nline = 0; len(buf) > 0; nline++ { + i := bytes.IndexByte(buf, '\n') + todo := len(buf) + if i >= 0 { + todo = i + } + + var nn int + switch { + case nline == 0, left == "": // first line or left side is empty + nn, err = r.writeColumnLine(left, buf[:todo]) + case i < 0 || i+1 == len(buf): // last line + nn, err = r.writeColumnLine(" └─", buf[:todo]) + default: // middle lines + nn, err = r.writeColumnLine(" │", buf[:todo]) + } + + n += nn + if err != nil { + return n, err + } + buf = buf[todo:] + + if i >= 0 { // always jump a line on the last line + if _, err = r.writer.Write(CRLF); err != nil { + return n, err + } + n++ + buf = buf[1:] + } + } + + return +} + +func (r *columnTaskWriter) writeColumnLine(left string, line []byte) (n int, err error) { + // Write left column + if n, err = fmt.Fprintf(r.writer, "%-15s | ", left); err != nil { + return n, err + } + + // Write left line + var nn int + nn, err = r.writer.Write(line) + n += nn + + return +} + +type rawTaskWriter struct { + writer io.Writer +} + +func (r *rawTaskWriter) Write(buf []byte) (n int, err error) { + return r.writer.Write(buf) +} + +func (r *rawTaskWriter) WriteTask(task string, buf []byte) (n int, err error) { + if task != "" { + n, err = r.writer.Write([]byte(task + ": ")) + } + + var nn int + nn, err = r.writer.Write(buf) + n += nn + return +} diff --git a/contribs/gnokeykc/README.md b/contribs/gnokeykc/README.md new file mode 100644 index 00000000000..5d508b4b221 --- /dev/null +++ b/contribs/gnokeykc/README.md @@ -0,0 +1,16 @@ +# `gnokeykc` + +`gnokeykc` is a Go-based CLI tool that enhances [`gnokey`](../../gno.land/cmd/gnokey) by integrating with your system's keychain. It adds `gnokey kc ...` subcommands to set and unset passwords in the keychain, allowing Gnokey to fetch passwords directly from the keychain instead of prompting for terminal input. + +## Usage + + gnokey kc -h + +## Terminal Alias + +For ease of use, set up a terminal alias to replace `gnokey` with `gnokeykc`: + + echo "alias gnokey='gnokeykc'" >> ~/.bashrc && source ~/.bashrc + +Now, `gnokey` commands will use `gnokeykc`, fetching passwords from the keychain. + diff --git a/contribs/gnokeykc/go.mod b/contribs/gnokeykc/go.mod new file mode 100644 index 00000000000..732bececda4 --- /dev/null +++ b/contribs/gnokeykc/go.mod @@ -0,0 +1,63 @@ +module github.com/gnolang/gno/contribs/gnokeykc + +go 1.20 + +replace github.com/gnolang/gno => ../.. + +require ( + github.com/gnolang/gno v0.0.0-00010101000000-000000000000 + github.com/zalando/go-keyring v0.2.3 +) + +require ( + dario.cat/mergo v1.0.0 // indirect + github.com/alessio/shellescape v1.4.1 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/btcsuite/btcd/btcutil v1.1.3 // indirect + github.com/cespare/xxhash v1.1.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect + github.com/danieljoos/wincred v1.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/dgraph-io/badger/v3 v3.2103.5 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect + github.com/gnolang/goleveldb v0.0.9 // indirect + github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/glog v1.1.0 // indirect + github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/flatbuffers v1.12.1 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/jaekwon/testify v1.6.1 // indirect + github.com/jmhodges/levigo v1.0.0 // indirect + github.com/klauspost/compress v1.12.3 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/linxGnu/grocksdb v1.8.11 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/peterbourgon/ff/v3 v3.4.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rs/cors v1.10.1 // indirect + github.com/stretchr/testify v1.8.4 // indirect + github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect + github.com/zondax/hid v0.9.2 // indirect + github.com/zondax/ledger-go v0.14.3 // indirect + go.etcd.io/bbolt v1.3.8 // indirect + go.opencensus.io v0.22.5 // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/term v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c // indirect + google.golang.org/grpc v1.58.3 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/contribs/gnokeykc/go.sum b/contribs/gnokeykc/go.sum new file mode 100644 index 00000000000..4a2778e7919 --- /dev/null +++ b/contribs/gnokeykc/go.sum @@ -0,0 +1,305 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= +github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= +github.com/btcsuite/btcd v0.23.0 h1:V2/ZgjfDFIygAX3ZapeigkVBoVUtOJKSwrhZdlpSvaA= +github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= +github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= +github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE= +github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= +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/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= +github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= +github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= +github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +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/gnolang/goleveldb v0.0.9 h1:Q7rGko9oXMKtQA+Apeeed5a3sjba/mcDhzJGoTVLCKE= +github.com/gnolang/goleveldb v0.0.9/go.mod h1:Dz6p9bmpy/FBESTgduiThZt5mToVDipcHGzj/zUOo8E= +github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk= +github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= +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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= +github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jaekwon/testify v1.6.1 h1:4AtAJcR9GzXN5W4DdY7ie74iCPiJV1JJUJL90t2ZUyw= +github.com/jaekwon/testify v1.6.1/go.mod h1:Oun0RXIHI7osufabQ60i4Lqkj0GXLbqI1I7kgzBNm1U= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/linxGnu/grocksdb v1.8.11 h1:BGol9e5gB1BrsTvOxloC88pe70TCqgrfLNwkyWW0kD8= +github.com/linxGnu/grocksdb v1.8.11/go.mod h1:xZCIb5Muw+nhbDK4Y5UJuOrin5MceOuiXkVUR7vp4WY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 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.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/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= +github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +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/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms= +github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= +github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= +github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= +github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= +go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +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-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c h1:jHkCUWkseRf+W+edG5hMzr/Uh1xkDREY4caybAq4dpY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +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/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/contribs/gnokeykc/kc.go b/contribs/gnokeykc/kc.go new file mode 100644 index 00000000000..f91dc8ba4dd --- /dev/null +++ b/contribs/gnokeykc/kc.go @@ -0,0 +1,94 @@ +package main + +import ( + "context" + "flag" + "fmt" + + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/zalando/go-keyring" +) + +const ( + kcService = "gnokey" + kcName = "encryption" +) + +func newKcCmd(io commands.IO) *commands.Command { + cmd := commands.NewCommand( + commands.Metadata{ + Name: "kc", + ShortUsage: "kc ", + ShortHelp: "Manage OS keychain", + }, + commands.NewEmptyConfig(), + commands.HelpExec, + ) + cmd.AddSubCommands( + newKcSetCmd(io), + newKcUnsetCmd(io), + ) + return cmd +} + +func newKcSetCmd(io commands.IO) *commands.Command { + return commands.NewCommand( + commands.Metadata{ + Name: "set", + ShortUsage: "set", + ShortHelp: "set encryption password in OS keychain", + }, + commands.NewEmptyConfig(), + func(_ context.Context, args []string) error { + return execKcSet(args, io) + }, + ) +} + +func execKcSet(args []string, io commands.IO) error { + if len(args) != 0 { + return flag.ErrHelp + } + + insecurePasswordStdin := false // XXX: cfg.rootCfg.InsecurePasswordStdin + password, err := io.GetPassword("Enter password.", insecurePasswordStdin) + if err != nil { + return fmt.Errorf("cannot read password: %w", err) + } + + err = keyring.Set(kcService, kcName, password) + if err != nil { + return fmt.Errorf("cannot set password is OS keychain") + } + + io.Printfln("Successfully added password for key.") + return nil +} + +func newKcUnsetCmd(io commands.IO) *commands.Command { + return commands.NewCommand( + commands.Metadata{ + Name: "unset", + ShortUsage: "unset", + ShortHelp: "unset password in OS keychain", + }, + commands.NewEmptyConfig(), + func(_ context.Context, args []string) error { + return execKcUnset(args, io) + }, + ) +} + +func execKcUnset(args []string, io commands.IO) error { + if len(args) != 0 { + return flag.ErrHelp + } + + err := keyring.Delete(kcService, kcName) + if err != nil { + return fmt.Errorf("cannot unset password from OS keychain") + } + + io.Printfln("Successfully unset password") + return nil +} diff --git a/contribs/gnokeykc/main.go b/contribs/gnokeykc/main.go new file mode 100644 index 00000000000..2065725e5b6 --- /dev/null +++ b/contribs/gnokeykc/main.go @@ -0,0 +1,30 @@ +package main + +import ( + "context" + "os" + + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" + "github.com/zalando/go-keyring" +) + +func main() { + stdio := commands.NewDefaultIO() + wrappedio := &wrappedIO{IO: stdio} + baseCfg := client.DefaultBaseOptions + baseCfg.Home = gnoenv.HomeDir() + cmd := client.NewRootCmdWithBaseConfig(wrappedio, baseCfg) + cmd.AddSubCommands(newKcCmd(stdio)) + + cmd.Execute(context.Background(), os.Args[1:]) +} + +type wrappedIO struct { + commands.IO +} + +func (io *wrappedIO) GetPassword(prompt string, insecure bool) (string, error) { + return keyring.Get(kcService, kcName) +} diff --git a/contribs/gnomd/go.mod b/contribs/gnomd/go.mod new file mode 100644 index 00000000000..b631040ce94 --- /dev/null +++ b/contribs/gnomd/go.mod @@ -0,0 +1,25 @@ +module github.com/gnolang/gno/contribs/gnomd + +go 1.20 + +require github.com/MichaelMure/go-term-markdown v0.1.4 + +require ( + github.com/MichaelMure/go-term-text v0.3.1 // indirect + github.com/alecthomas/chroma v0.7.1 // indirect + github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect + github.com/disintegration/imaging v1.6.2 // indirect + github.com/dlclark/regexp2 v1.1.6 // indirect + github.com/eliukblau/pixterm/pkg/ansimage v0.0.0-20191210081756-9fb6cf8c2f75 // indirect + github.com/fatih/color v1.9.0 // indirect + github.com/gomarkdown/markdown v0.0.0-20191123064959-2c17d62f5098 // indirect + github.com/kyokomi/emoji/v2 v2.2.8 // indirect + github.com/lucasb-eyer/go-colorful v1.0.3 // indirect + github.com/mattn/go-colorable v0.1.4 // indirect + github.com/mattn/go-isatty v0.0.11 // indirect + github.com/mattn/go-runewidth v0.0.12 // indirect + github.com/rivo/uniseg v0.1.0 // indirect + golang.org/x/image v0.0.0-20191206065243-da761ea9ff43 // indirect + golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 // indirect + golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect +) diff --git a/contribs/gnomd/go.sum b/contribs/gnomd/go.sum new file mode 100644 index 00000000000..b4ad4f5c9bf --- /dev/null +++ b/contribs/gnomd/go.sum @@ -0,0 +1,70 @@ +github.com/MichaelMure/go-term-markdown v0.1.4 h1:Ir3kBXDUtOX7dEv0EaQV8CNPpH+T7AfTh0eniMOtNcs= +github.com/MichaelMure/go-term-markdown v0.1.4/go.mod h1:EhcA3+pKYnlUsxYKBJ5Sn1cTQmmBMjeNlpV8nRb+JxA= +github.com/MichaelMure/go-term-text v0.3.1 h1:Kw9kZanyZWiCHOYu9v/8pWEgDQ6UVN9/ix2Vd2zzWf0= +github.com/MichaelMure/go-term-text v0.3.1/go.mod h1:QgVjAEDUnRMlzpS6ky5CGblux7ebeiLnuy9dAaFZu8o= +github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= +github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= +github.com/alecthomas/chroma v0.7.1 h1:G1i02OhUbRi2nJxcNkwJaY/J1gHXj9tt72qN6ZouLFQ= +github.com/alecthomas/chroma v0.7.1/go.mod h1:gHw09mkX1Qp80JlYbmN9L3+4R5o6DJJ3GRShh+AICNc= +github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo= +github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= +github.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI= +github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY= +github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= +github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= +github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= +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/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= +github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= +github.com/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg= +github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/eliukblau/pixterm/pkg/ansimage v0.0.0-20191210081756-9fb6cf8c2f75 h1:vbix8DDQ/rfatfFr/8cf/sJfIL69i4BcZfjrVOxsMqk= +github.com/eliukblau/pixterm/pkg/ansimage v0.0.0-20191210081756-9fb6cf8c2f75/go.mod h1:0gZuvTO1ikSA5LtTI6E13LEOdWQNjIo5MTQOvrV0eFg= +github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/gomarkdown/markdown v0.0.0-20191123064959-2c17d62f5098 h1:Qxs3bNRWe8GTcKMxYOSXm0jx6j0de8XUtb/fsP3GZ0I= +github.com/gomarkdown/markdown v0.0.0-20191123064959-2c17d62f5098/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU= +github.com/kyokomi/emoji/v2 v2.2.8 h1:jcofPxjHWEkJtkIbcLHvZhxKgCPl6C7MyjTrD4KDqUE= +github.com/kyokomi/emoji/v2 v2.2.8/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE= +github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +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.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20191206065243-da761ea9ff43 h1:gQ6GUSD102fPgli+Yb4cR/cGaHF7tNBt+GYoRCpGC7s= +golang.org/x/image v0.0.0-20191206065243-da761ea9ff43/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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= diff --git a/contribs/gnomd/main.go b/contribs/gnomd/main.go new file mode 100644 index 00000000000..2d3b7266af5 --- /dev/null +++ b/contribs/gnomd/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + + markdown "github.com/MichaelMure/go-term-markdown" +) + +func main() { + // If no arguments are provided, read from stdin + if len(os.Args) <= 1 { + fileContent, err := ioutil.ReadAll(os.Stdin) + checkErr(err) + renderMarkdown("stdin.gno", fileContent) + } + + // Iterate through command-line arguments (file paths) + for _, filePath := range os.Args[1:] { + fileContent, err := ioutil.ReadFile(filePath) + checkErr(err) + renderMarkdown(filePath, fileContent) + } +} + +func renderMarkdown(filePath string, fileContent []byte) { + fmt.Printf("-- %s --\n", filePath) + + result := markdown.Render(string(fileContent), 80, 6) + fmt.Println(string(result)) +} + +func checkErr(err error) { + if err != nil { + panic(err) + } +} diff --git a/docs/assets/explanation/packages/pkg-1.gno b/docs/assets/explanation/packages/pkg-1.gno new file mode 100644 index 00000000000..e68d506612a --- /dev/null +++ b/docs/assets/explanation/packages/pkg-1.gno @@ -0,0 +1,6 @@ +func TotalSupply() uint64 +func BalanceOf(account std.Address) uint64 +func Transfer(to std.Address, amount uint64) +func Approve(spender std.Address, amount uint64) +func TransferFrom(from, to std.Address, amount uint64) +func Allowance(owner, spender std.Address) uint64 diff --git a/docs/assets/explanation/packages/pkg-2.gno b/docs/assets/explanation/packages/pkg-2.gno new file mode 100644 index 00000000000..0054cc95e3d --- /dev/null +++ b/docs/assets/explanation/packages/pkg-2.gno @@ -0,0 +1,11 @@ +// functions that work similarly to those of grc20 +func BalanceOf(owner std.Address) (uint64, error) +func Approve(approved std.Address, tid TokenID) error +func TransferFrom(from, to std.Address, tid TokenID) error + +// functions unique to grc721 +func OwnerOf(tid TokenID) (std.Address, error) +func SafeTransferFrom(from, to std.Address, tid TokenID) error +func SetApprovalForAll(operator std.Address, approved bool) error +func GetApproved(tid TokenID) (std.Address, error) +func IsApprovedForAll(owner, operator std.Address) bool diff --git a/docs/assets/explanation/packages/pkg-3.gno b/docs/assets/explanation/packages/pkg-3.gno new file mode 100644 index 00000000000..f1ba5609d6b --- /dev/null +++ b/docs/assets/explanation/packages/pkg-3.gno @@ -0,0 +1,12 @@ +func TestAddress(name string) std.Address { + if len(name) > std.RawAddressSize { + panic("address name cannot be greater than std.AddressSize bytes") + } + addr := std.RawAddress{} + // TODO: use strings.RepeatString or similar. + // NOTE: I miss python's "".Join(). + blanks := "____________________" + copy(addr[:], []byte(blanks)) + copy(addr[:], []byte(name)) + return std.Address(std.EncodeBech32("g", addr)) +} diff --git a/docs/assets/explanation/packages/pkg-4.gno b/docs/assets/explanation/packages/pkg-4.gno new file mode 100644 index 00000000000..edd34b5cc5d --- /dev/null +++ b/docs/assets/explanation/packages/pkg-4.gno @@ -0,0 +1,8 @@ +admin := users.AddressOrName("g1tntwtvzrkt2gex69f0pttan0fp05zmeg5yykv8") +test2 := users.AddressOrName(testutils.TestAddress("test2")) +recv := users.AddressOrName(testutils.TestAddress("recv")) +normal := users.AddressOrName(testutils.TestAddress("normal")) +owner := users.AddressOrName(testutils.TestAddress("owner")) +spender := users.AddressOrName(testutils.TestAddress("spender")) +recv2 := users.AddressOrName(testutils.TestAddress("recv2")) +mibu := users.AddressOrName(testutils.TestAddress("mint_burn")) diff --git a/docs/assets/getting-started/browsing-gno-source-code/gnoweb-avl.png b/docs/assets/getting-started/browsing-gno-source-code/gnoweb-avl.png new file mode 100644 index 00000000000..3754ccfe88e Binary files /dev/null and b/docs/assets/getting-started/browsing-gno-source-code/gnoweb-avl.png differ diff --git a/docs/assets/getting-started/browsing-gno-source-code/gnoweb-boards-source.png b/docs/assets/getting-started/browsing-gno-source-code/gnoweb-boards-source.png new file mode 100644 index 00000000000..65e0f880bc9 Binary files /dev/null and b/docs/assets/getting-started/browsing-gno-source-code/gnoweb-boards-source.png differ diff --git a/docs/assets/getting-started/browsing-gno-source-code/gnoweb-boards.png b/docs/assets/getting-started/browsing-gno-source-code/gnoweb-boards.png new file mode 100644 index 00000000000..dca226c3ec9 Binary files /dev/null and b/docs/assets/getting-started/browsing-gno-source-code/gnoweb-boards.png differ diff --git a/docs/assets/getting-started/browsing-gno-source-code/gnoweb.png b/docs/assets/getting-started/browsing-gno-source-code/gnoweb.png new file mode 100644 index 00000000000..3ba9035889d Binary files /dev/null and b/docs/assets/getting-started/browsing-gno-source-code/gnoweb.png differ diff --git a/docs/assets/getting-started/creating-a-key-pair/gnokey-add-mnemonic.gif b/docs/assets/getting-started/creating-a-key-pair/gnokey-add-mnemonic.gif new file mode 100644 index 00000000000..641acc9f825 Binary files /dev/null and b/docs/assets/getting-started/creating-a-key-pair/gnokey-add-mnemonic.gif differ diff --git a/docs/assets/getting-started/creating-a-key-pair/gnokey-add-random.gif b/docs/assets/getting-started/creating-a-key-pair/gnokey-add-random.gif new file mode 100644 index 00000000000..dba61a6b287 Binary files /dev/null and b/docs/assets/getting-started/creating-a-key-pair/gnokey-add-random.gif differ diff --git a/docs/assets/getting-started/creating-a-key-pair/gnokey-export.gif b/docs/assets/getting-started/creating-a-key-pair/gnokey-export.gif new file mode 100644 index 00000000000..e56938866f9 Binary files /dev/null and b/docs/assets/getting-started/creating-a-key-pair/gnokey-export.gif differ diff --git a/docs/assets/getting-started/creating-a-key-pair/gnokey-generate.gif b/docs/assets/getting-started/creating-a-key-pair/gnokey-generate.gif new file mode 100644 index 00000000000..ce371487c6c Binary files /dev/null and b/docs/assets/getting-started/creating-a-key-pair/gnokey-generate.gif differ diff --git a/docs/assets/getting-started/creating-a-key-pair/gnokey-import.gif b/docs/assets/getting-started/creating-a-key-pair/gnokey-import.gif new file mode 100644 index 00000000000..8ec80519372 Binary files /dev/null and b/docs/assets/getting-started/creating-a-key-pair/gnokey-import.gif differ diff --git a/docs/assets/getting-started/creating-a-key-pair/gnokey-list.gif b/docs/assets/getting-started/creating-a-key-pair/gnokey-list.gif new file mode 100644 index 00000000000..ad050da585c Binary files /dev/null and b/docs/assets/getting-started/creating-a-key-pair/gnokey-list.gif differ diff --git a/docs/assets/getting-started/local-setup/gno-help.gif b/docs/assets/getting-started/local-setup/gno-help.gif new file mode 100644 index 00000000000..ebd7d4cfcb0 Binary files /dev/null and b/docs/assets/getting-started/local-setup/gno-help.gif differ diff --git a/docs/assets/getting-started/local-setup/gnokey-help.gif b/docs/assets/getting-started/local-setup/gnokey-help.gif new file mode 100644 index 00000000000..d96d2424679 Binary files /dev/null and b/docs/assets/getting-started/local-setup/gnokey-help.gif differ diff --git a/docs/assets/getting-started/local-setup/make-build-gnoland.gif b/docs/assets/getting-started/local-setup/make-build-gnoland.gif new file mode 100644 index 00000000000..fe12670be2c Binary files /dev/null and b/docs/assets/getting-started/local-setup/make-build-gnoland.gif differ diff --git a/docs/assets/getting-started/local-setup/make-build-gnovm.gif b/docs/assets/getting-started/local-setup/make-build-gnovm.gif new file mode 100644 index 00000000000..54f133796d6 Binary files /dev/null and b/docs/assets/getting-started/local-setup/make-build-gnovm.gif differ diff --git a/docs/assets/getting-started/setting-up-a-local-chain/gnoland-start.gif b/docs/assets/getting-started/setting-up-a-local-chain/gnoland-start.gif new file mode 100644 index 00000000000..4da21bc863b Binary files /dev/null and b/docs/assets/getting-started/setting-up-a-local-chain/gnoland-start.gif differ diff --git a/docs/assets/getting-started/setting-up-funds/faucet-page.png b/docs/assets/getting-started/setting-up-funds/faucet-page.png new file mode 100644 index 00000000000..f1a5420659f Binary files /dev/null and b/docs/assets/getting-started/setting-up-funds/faucet-page.png differ diff --git a/docs/assets/getting-started/setting-up-funds/gnofaucet-serve.gif b/docs/assets/getting-started/setting-up-funds/gnofaucet-serve.gif new file mode 100644 index 00000000000..79f46e7563c Binary files /dev/null and b/docs/assets/getting-started/setting-up-funds/gnofaucet-serve.gif differ diff --git a/docs/assets/getting-started/setting-up-funds/gnokey-query.gif b/docs/assets/getting-started/setting-up-funds/gnokey-query.gif new file mode 100644 index 00000000000..9f58c638624 Binary files /dev/null and b/docs/assets/getting-started/setting-up-funds/gnokey-query.gif differ diff --git a/docs/assets/getting-started/setting-up-funds/gnoland-start.gif b/docs/assets/getting-started/setting-up-funds/gnoland-start.gif new file mode 100644 index 00000000000..3c323c40410 Binary files /dev/null and b/docs/assets/getting-started/setting-up-funds/gnoland-start.gif differ diff --git a/docs/assets/getting-started/setting-up-funds/gnoweb.gif b/docs/assets/getting-started/setting-up-funds/gnoweb.gif new file mode 100644 index 00000000000..ce4202dcca4 Binary files /dev/null and b/docs/assets/getting-started/setting-up-funds/gnoweb.gif differ diff --git a/docs/assets/gno-tooling/gnodev/gnodev.gif b/docs/assets/gno-tooling/gnodev/gnodev.gif new file mode 100644 index 00000000000..f3f86bdbb2f Binary files /dev/null and b/docs/assets/gno-tooling/gnodev/gnodev.gif differ diff --git a/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno b/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno new file mode 100644 index 00000000000..5500f019886 --- /dev/null +++ b/docs/assets/how-to-guides/creating-grc20/mytoken-1.gno @@ -0,0 +1,21 @@ +package mytoken + +import ( + "std" + + "gno.land/p/demo/grc/grc20" +) + +var ( + mytoken *grc20.AdminToken + admin std.Address = "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj" // set admin account +) + +// init is a constructor function that runs only once (at time of deployment) +func init() { + // provision the token's name, symbol and number of decimals + mytoken = grc20.NewAdminToken("Mytoken", "MTKN", 4) + + // set the total supply + mytoken.Mint(admin, 1000000*10000) // @administrator (supply = 1 million) +} diff --git a/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno b/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno new file mode 100644 index 00000000000..25ff85c55ab --- /dev/null +++ b/docs/assets/how-to-guides/creating-grc20/mytoken-2.gno @@ -0,0 +1,83 @@ +func TotalSupply() uint64 { + return mytoken.TotalSupply() +} + +func BalanceOf(owner users.AddressOrName) uint64 { + balance, err := mytoken.BalanceOf(owner.Resolve()) + if err != nil { + panic(err) + } + return balance +} + +func Allowance(owner, spender users.AddressOrName) uint64 { + allowance, err := mytoken.Allowance(owner.Resolve(), spender.Resolve()) + if err != nil { + panic(err) + } + return allowance +} + +func Transfer(to users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + err := mytoken.Transfer(caller, to.Resolve(), amount) + if err != nil { + panic(err) + } +} + +func Approve(spender users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + err := mytoken.Approve(caller, spender.Resolve(), amount) + if err != nil { + panic(err) + } +} + +func TransferFrom(from, to users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + err := mytoken.TransferFrom(caller, from.Resolve(), to.Resolve(), amount) + if err != nil { + panic(err) + } +} + +func Mint(address users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + err := mytoken.Mint(address.Resolve(), amount) + if err != nil { + panic(err) + } +} + +func Burn(address users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + err := mytoken.Burn(address.Resolve(), amount) + if err != nil { + panic(err) + } +} + +func Render(path string) string { + parts := strings.Split(path, "/") + c := len(parts) + + switch { + case path == "": + return mytoken.RenderHome() + case c == 2 && parts[0] == "balance": + owner := users.AddressOrName(parts[1]) + balance, _ := mytoken.BalanceOf(owner.Resolve()) + return ufmt.Sprintf("%d\n", balance) + default: + return "404\n" + } +} + +func assertIsAdmin(address std.Address) { + if address != admin { + panic("restricted access") + } +} diff --git a/docs/assets/how-to-guides/creating-grc721/mynonfungibletoken-1.gno b/docs/assets/how-to-guides/creating-grc721/mynonfungibletoken-1.gno new file mode 100644 index 00000000000..14e25bd7264 --- /dev/null +++ b/docs/assets/how-to-guides/creating-grc721/mynonfungibletoken-1.gno @@ -0,0 +1,17 @@ +package mynonfungibletoken + +import ( + "std" + + "gno.land/p/demo/grc/grc721" +) + +var ( + admin std.Address = "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj" // set admin account + // provision the token's name and symbol + mynonfungibletoken = grc721.NewBasicNFT("mynonfungibletoken", "MNFT") +) + +func init() { + mintNNFT(admin, 10) // @administrator (supply = 10) +} diff --git a/docs/assets/how-to-guides/creating-grc721/mynonfungibletoken-2.gno b/docs/assets/how-to-guides/creating-grc721/mynonfungibletoken-2.gno new file mode 100644 index 00000000000..8092596ae0a --- /dev/null +++ b/docs/assets/how-to-guides/creating-grc721/mynonfungibletoken-2.gno @@ -0,0 +1,102 @@ +func mintNNFT(owner std.Address, n uint64) { + count := my.TokenCount() + for i := count; i < count+n; i++ { + tid := grc721.TokenID(ufmt.Sprintf("%d", i)) + mynonfungibletoken.Mint(owner, tid) + } +} + +// Getters + +func BalanceOf(user users.AddressOrName) uint64 { + balance, err := mynonfungibletoken.BalanceOf(user.Resolve()) + if err != nil { + panic(err) + } + + return balance +} + +func OwnerOf(tid grc721.TokenID) std.Address { + owner, err := mynonfungibletoken.OwnerOf(tid) + if err != nil { + panic(err) + } + + return owner +} + +func IsApprovedForAll(owner, user users.AddressOrName) bool { + return mynonfungibletoken.IsApprovedForAll(owner.Resolve(), user.Resolve()) +} + +func GetApproved(tid grc721.TokenID) std.Address { + addr, err := mynonfungibletoken.GetApproved(tid) + if err != nil { + panic(err) + } + + return addr +} + +// Setters + +func Approve(user users.AddressOrName, tid grc721.TokenID) { + err := mynonfungibletoken.Approve(user.Resolve(), tid) + if err != nil { + panic(err) + } +} + +func SetApprovalForAll(user users.AddressOrName, approved bool) { + err := mynonfungibletoken.SetApprovalForAll(user.Resolve(), approved) + if err != nil { + panic(err) + } +} + +func TransferFrom(from, to users.AddressOrName, tid grc721.TokenID) { + err := mynonfungibletoken.TransferFrom(from.Resolve(), to.Resolve(), tid) + if err != nil { + panic(err) + } +} + +// Admin + +func Mint(to users.AddressOrName, tid grc721.TokenID) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + err := mynonfungibletoken.Mint(to.Resolve(), tid) + if err != nil { + panic(err) + } +} + +func Burn(tid grc721.TokenID) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + err := mynonfungibletoken.Burn(tid) + if err != nil { + panic(err) + } +} + +// Render + +func Render(path string) string { + switch { + case path == "": + return mynonfungibletoken.RenderHome() + default: + return "404\n" + } +} + +// Util + +func assertIsAdmin(address std.Address) { + if address != admin { + panic("restricted access") + } +} diff --git a/docs/assets/how-to-guides/porting-solidity-to-gno/porting-1.gno b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-1.gno new file mode 100644 index 00000000000..1e1dee77a86 --- /dev/null +++ b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-1.gno @@ -0,0 +1,39 @@ +func shouldEqual(t *testing.T, got interface{}, expected interface{}) { + t.Helper() + + if got != expected { + t.Errorf("expected %v(%T), got %v(%T)", expected, expected, got, got) + } +} + +func shouldErr(t *testing.T, err error) { + t.Helper() + if err == nil { + t.Errorf("expected an error, but got nil.") + } +} + +func shouldNoErr(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Errorf("expected no error, but got err: %s.", err.Error()) + } +} + +func shouldPanic(t *testing.T, f func()) { + defer func() { + if r := recover(); r == nil { + t.Errorf("should have panic") + } + }() + f() +} + +func shouldNoPanic(t *testing.T, f func()) { + defer func() { + if r := recover(); r != nil { + t.Errorf("should not have panic") + } + }() + f() +} diff --git a/docs/assets/how-to-guides/porting-solidity-to-gno/porting-10.sol b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-10.sol new file mode 100644 index 00000000000..8d7aff1794b --- /dev/null +++ b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-10.sol @@ -0,0 +1,29 @@ +/// End the auction and send the highest bid +/// to the beneficiary. +function auctionEnd() external { + // It is a good guideline to structure functions that interact + // with other contracts (i.e. they call functions or send Ether) + // into three phases: + // 1. checking conditions + // 2. performing actions (potentially changing conditions) + // 3. interacting with other contracts + // If these phases are mixed up, the other contract could call + // back into the current contract and modify the state or cause + // effects (ether payout) to be performed multiple times. + // If functions called internally include interaction with external + // contracts, they also have to be considered interaction with + // external contracts. + + // 1. Conditions + if (block.timestamp < auctionEndTime) + revert AuctionNotYetEnded(); + if (ended) + revert AuctionEndAlreadyCalled(); + + // 2. Effects + ended = true; + emit AuctionEnded(highestBidder, highestBid); + + // 3. Interaction + beneficiary.transfer(highestBid); +} \ No newline at end of file diff --git a/docs/assets/how-to-guides/porting-solidity-to-gno/porting-11.gno b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-11.gno new file mode 100644 index 00000000000..e48ebf919a0 --- /dev/null +++ b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-11.gno @@ -0,0 +1,17 @@ +func AuctionEnd() { + if std.GetHeight() < auctionEndBlock { + panic("Auction hasn't ended") + } + + if ended { + panic("Auction has ended") + + } + ended = true + + // Send the highest bid to the recipient + banker := std.GetBanker(std.BankerTypeRealmSend) + pkgAddr := std.GetOrigPkgAddr() + + banker.SendCoins(pkgAddr, receiver, std.Coins{{"ugnot", int64(highestBid)}}) +} diff --git a/docs/assets/how-to-guides/porting-solidity-to-gno/porting-12.gno b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-12.gno new file mode 100644 index 00000000000..55817537298 --- /dev/null +++ b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-12.gno @@ -0,0 +1,18 @@ +// AuctionEnd() Function Test +func TestAuctionEnd(t *testing.T) { + // Auction is ongoing + shouldPanic(t, AuctionEnd) + + // Auction ends + highestBid = 3 + std.TestSkipHeights(500) + shouldNoPanic(t, AuctionEnd) + shouldEqual(t, ended, true) + + banker := std.GetBanker(std.BankerTypeRealmSend) + shouldEqual(t, banker.GetCoins(receiver).String(), "3ugnot") + + // Auction has already ended + shouldPanic(t, AuctionEnd) + shouldEqual(t, ended, true) +} diff --git a/docs/assets/how-to-guides/porting-solidity-to-gno/porting-13.gno b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-13.gno new file mode 100644 index 00000000000..0e5f2d57de9 --- /dev/null +++ b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-13.gno @@ -0,0 +1,74 @@ +// The whole test +func TestFull(t *testing.T) { + bidder01 := testutils.TestAddress("bidder01") // g1vf5kger9wgcrzh6lta047h6lta047h6lufftkw + bidder02 := testutils.TestAddress("bidder02") // g1vf5kger9wgcryh6lta047h6lta047h6lnhe2x2 + + // Variables test + { + shouldEqual(t, highestBidder, "") + shouldEqual(t, receiver, "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + shouldEqual(t, auctionEndBlock, 423) + shouldEqual(t, highestBid, 0) + shouldEqual(t, pendingReturns.Size(), 0) + shouldEqual(t, ended, false) + } + + // Send two or more types of coins + { + std.TestSetOrigCaller(bidder01) + std.TestSetOrigSend(std.Coins{{"ugnot", 0}, {"test", 1}}, nil) + shouldPanic(t, Bid) + } + + // Send less than the highest bid + { + std.TestSetOrigCaller(bidder01) + std.TestSetOrigSend(std.Coins{{"ugnot", 0}}, nil) + shouldPanic(t, Bid) + } + + // Send more than the highest bid + { + std.TestSetOrigCaller(bidder01) + std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) + shouldNoPanic(t, Bid) + + shouldEqual(t, pendingReturns.Size(), 0) + shouldEqual(t, highestBid, 1) + shouldEqual(t, highestBidder, "g1vf5kger9wgcrzh6lta047h6lta047h6lufftkw") + } + + // Other participants in the auction + { + + // Send less amount than the current highest bid (current: 1) + std.TestSetOrigCaller(bidder02) + std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) + shouldPanic(t, Bid) + + // Send more amount than the current highest bid (exceeded) + std.TestSetOrigCaller(bidder02) + std.TestSetOrigSend(std.Coins{{"ugnot", 2}}, nil) + shouldNoPanic(t, Bid) + + shouldEqual(t, highestBid, 2) + shouldEqual(t, highestBidder, "g1vf5kger9wgcryh6lta047h6lta047h6lnhe2x2") + + shouldEqual(t, pendingReturns.Size(), 1) // Return to the existing bidder + shouldEqual(t, pendingReturns.Has("g1vf5kger9wgcrzh6lta047h6lta047h6lufftkw"), true) + } + + // Auction ends + { + std.TestSkipHeights(150) + shouldPanic(t, AuctionEnd) + shouldEqual(t, ended, false) + + std.TestSkipHeights(301) + shouldNoPanic(t, AuctionEnd) + shouldEqual(t, ended, true) + + banker := std.GetBanker(std.BankerTypeRealmSend) + shouldEqual(t, banker.GetCoins(receiver).String(), "2ugnot") + } +} diff --git a/docs/assets/how-to-guides/porting-solidity-to-gno/porting-2.sol b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-2.sol new file mode 100644 index 00000000000..0040c3ca75f --- /dev/null +++ b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-2.sol @@ -0,0 +1,47 @@ +// Parameters of the auction. Times are either +// absolute unix timestamps (seconds since 1970-01-01) +// or time periods in seconds. +address payable public beneficiary; +uint public auctionEndTime; + +// Current state of the auction. +address public highestBidder; +uint public highestBid; + +// Allowed withdrawals of previous bids +mapping(address => uint) pendingReturns; + +// Set to true at the end, disallows any change. +// By default initialized to `false`. +bool ended; + +// Events that will be emitted on changes. +event HighestBidIncreased(address bidder, uint amount); +event AuctionEnded(address winner, uint amount); + +// Errors that describe failures. + +// The triple-slash comments are so-called natspec +// comments. They will be shown when the user +// is asked to confirm a transaction or +// when an error is displayed. + +/// The auction has already ended. +error AuctionAlreadyEnded(); +/// There is already a higher or equal bid. +error BidNotHighEnough(uint highestBid); +/// The auction has not ended yet. +error AuctionNotYetEnded(); +/// The function auctionEnd has already been called. +error AuctionEndAlreadyCalled(); + +/// Create a simple auction with `biddingTime` +/// seconds bidding time on behalf of the +/// beneficiary address `beneficiaryAddress`. +constructor( + uint biddingTime, + address payable beneficiaryAddress +) { + beneficiary = beneficiaryAddress; + auctionEndTime = block.timestamp + biddingTime; +} \ No newline at end of file diff --git a/docs/assets/how-to-guides/porting-solidity-to-gno/porting-3.gno b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-3.gno new file mode 100644 index 00000000000..af8137b4044 --- /dev/null +++ b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-3.gno @@ -0,0 +1,8 @@ +var ( + receiver = std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + auctionEndBlock = std.GetHeight() + uint(300) // in blocks + highestBidder std.Address + highestBid = uint(0) + pendingReturns avl.Tree + ended = false +) diff --git a/docs/assets/how-to-guides/porting-solidity-to-gno/porting-4.sol b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-4.sol new file mode 100644 index 00000000000..0e3ab6d7e0d --- /dev/null +++ b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-4.sol @@ -0,0 +1,32 @@ +function bid() external payable { + // No arguments are necessary, all + // information is already part of + // the transaction. The keyword payable + // is required for the function to + // be able to receive Ether. + + // Revert the call if the bidding + // period is over. + if (block.timestamp > auctionEndTime) + revert AuctionAlreadyEnded(); + + // If the bid is not higher, send the + // money back (the revert statement + // will revert all changes in this + // function execution including + // it having received the money). + if (msg.value <= highestBid) + revert BidNotHighEnough(highestBid); + + if (highestBid != 0) { + // Sending back the money by simply using + // highestBidder.send(highestBid) is a security risk + // because it could execute an untrusted contract. + // It is always safer to let the recipients + // withdraw their money themselves. + pendingReturns[highestBidder] += highestBid; + } + highestBidder = msg.sender; + highestBid = msg.value; + emit HighestBidIncreased(msg.sender, msg.value); +} \ No newline at end of file diff --git a/docs/assets/how-to-guides/porting-solidity-to-gno/porting-5.gno b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-5.gno new file mode 100644 index 00000000000..43f0b43b397 --- /dev/null +++ b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-5.gno @@ -0,0 +1,30 @@ +func Bid() { + if std.GetHeight() > auctionEndBlock { + panic("Exceeded auction end block") + } + + sentCoins := std.GetOrigSend() + if len(sentCoins) != 1 { + panic("Send only one type of coin") + } + + sentAmount := uint(sentCoins[0].Amount) + if sentAmount <= highestBid { + panic("Too few coins sent") + } + + // A new bid is higher than the current highest bid + if sentAmount > highestBid { + // If the highest bid is greater than 0, + if highestBid > 0 { + // Need to return the bid amount to the existing highest bidder + // Create an AVL tree and save + pendingReturns.Set(highestBidder.String(), highestBid) + } + + // Update the top bidder address + highestBidder = std.GetOrigCaller() + // Update the top bid amount + highestBid = sentAmount + } +} diff --git a/docs/assets/how-to-guides/porting-solidity-to-gno/porting-6.gno b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-6.gno new file mode 100644 index 00000000000..b544d0017c4 --- /dev/null +++ b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-6.gno @@ -0,0 +1,41 @@ +// Bid Function Test - Send Coin +func TestBidCoins(t *testing.T) { + // Sending two types of coins + std.TestSetOrigCaller(bidder01) + std.TestSetOrigSend(std.Coins{{"ugnot", 0}, {"test", 1}}, nil) + shouldPanic(t, Bid) + + // Sending lower amount than the current highest bid + std.TestSetOrigCaller(bidder01) + std.TestSetOrigSend(std.Coins{{"ugnot", 0}}, nil) + shouldPanic(t, Bid) + + // Sending more amount than the current highest bid (exceeded) + std.TestSetOrigCaller(bidder01) + std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) + shouldNoPanic(t, Bid) +} + +// Bid Function Test - Bid by two or more people +func TestBidCoins(t *testing.T) { + // bidder01 bidding with 1 coin + std.TestSetOrigCaller(bidder01) + std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) + shouldNoPanic(t, Bid) + shouldEqual(t, highestBid, 1) + shouldEqual(t, highestBidder, bidder01) + shouldEqual(t, pendingReturns.Size(), 0) + + // bidder02 bidding with 1 coin + std.TestSetOrigCaller(bidder02) + std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) + shouldPanic(t, Bid) + + // bidder02 bidding with 2 coins + std.TestSetOrigCaller(bidder02) + std.TestSetOrigSend(std.Coins{{"ugnot", 2}}, nil) + shouldNoPanic(t, Bid) + shouldEqual(t, highestBid, 2) + shouldEqual(t, highestBidder, bidder02) + shouldEqual(t, pendingReturns.Size(), 1) +} diff --git a/docs/assets/how-to-guides/porting-solidity-to-gno/porting-7.sol b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-7.sol new file mode 100644 index 00000000000..b28ecec1f52 --- /dev/null +++ b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-7.sol @@ -0,0 +1,20 @@ +/// Withdraw a bid that was overbid. +function withdraw() external returns (bool) { + uint amount = pendingReturns[msg.sender]; + if (amount > 0) { + // It is important to set this to zero because the recipient + // can call this function again as part of the receiving call + // before `send` returns. + pendingReturns[msg.sender] = 0; + + // msg.sender is not of type `address payable` and must be + // explicitly converted using `payable(msg.sender)` in order + // use the member function `send()`. + if (!payable(msg.sender).send(amount)) { + // No need to call throw here, just reset the amount owing + pendingReturns[msg.sender] = amount; + return false; + } + } + return true; +} \ No newline at end of file diff --git a/docs/assets/how-to-guides/porting-solidity-to-gno/porting-8.gno b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-8.gno new file mode 100644 index 00000000000..7cb6bbd8d90 --- /dev/null +++ b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-8.gno @@ -0,0 +1,15 @@ +func Withdraw() { + // Query the return amount to non-highest bidders + amount, _ := pendingReturns.Get(std.GetOrigCaller().String()) + + if amount > 0 { + // If there's an amount, reset the amount first, + pendingReturns.Set(std.GetOrigCaller().String(), 0) + + // Return the exceeded amount + banker := std.GetBanker(std.BankerTypeRealmSend) + pkgAddr := std.GetOrigPkgAddr() + + banker.SendCoins(pkgAddr, std.GetOrigCaller(), std.Coins{{"ugnot", amount.(int64)}}) + } +} diff --git a/docs/assets/how-to-guides/porting-solidity-to-gno/porting-9.gno b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-9.gno new file mode 100644 index 00000000000..fbc06792ce4 --- /dev/null +++ b/docs/assets/how-to-guides/porting-solidity-to-gno/porting-9.gno @@ -0,0 +1,17 @@ +// Withdraw Function Test +func TestWithdraw(t *testing.T) { + // If there's no participants for return + shouldEqual(t, pendingReturns.Size(), 0) + + // If there's participants for return (data generation + returnAddr := bidder01.String() + returnAmount := int64(3) + pendingReturns.Set(returnAddr, returnAmount) + shouldEqual(t, pendingReturns.Size(), 1) + shouldEqual(t, pendingReturns.Has(returnAddr), true) + + banker := std.GetBanker(std.BankerTypeRealmSend) + pkgAddr := std.GetOrigPkgAddr() + banker.SendCoins(pkgAddr, std.Address(returnAddr), std.Coins{{"ugnot", returnAmount}}) + shouldEqual(t, banker.GetCoins(std.Address(returnAddr)).String(), "3ugnot") +} diff --git a/docs/assets/how-to-guides/simple-contract/counter.gno b/docs/assets/how-to-guides/simple-contract/counter.gno new file mode 100644 index 00000000000..0cc45813bda --- /dev/null +++ b/docs/assets/how-to-guides/simple-contract/counter.gno @@ -0,0 +1,19 @@ +package counter + +import ( + "gno.land/p/demo/ufmt" +) + +var count int + +func Increment() { + count++ +} + +func Decrement() { + count-- +} + +func Render(_ string) string { + return ufmt.Sprintf("Count: %d", count) +} diff --git a/docs/assets/how-to-guides/simple-contract/init.gno b/docs/assets/how-to-guides/simple-contract/init.gno new file mode 100644 index 00000000000..823c394dfa0 --- /dev/null +++ b/docs/assets/how-to-guides/simple-contract/init.gno @@ -0,0 +1,11 @@ +package counter + +var count int + +// ... + +func init() { + count = 2 * 10 // arbitrary value +} + +// ... diff --git a/docs/assets/how-to-guides/simple-library/tapas.gno b/docs/assets/how-to-guides/simple-library/tapas.gno new file mode 100644 index 00000000000..7d8eb184898 --- /dev/null +++ b/docs/assets/how-to-guides/simple-library/tapas.gno @@ -0,0 +1,40 @@ +package tapas + +import ( + "gno.land/p/demo/rand" +) + +// List of tapas suggestions +var listOfTapas = []string{ + "Patatas Bravas", + "Gambas al Ajillo", + "Croquetas", + "Tortilla Española", + "Pimientos de Padrón", + "Jamon Serrano", + "Boquerones en Vinagre", + "Calamares a la Romana", + "Pulpo a la Gallega", + "Tostada con Tomate", + "Mejillones en Escabeche", + "Chorizo a la Sidra", + "Cazón en Adobo", + "Banderillas", + "Espárragos a la Parrilla", + "Huevos Rellenos", + "Tuna Empanada", + "Sardinas a la Plancha", +} + +// GetTapaSuggestion randomly selects and returns a tapa suggestion +func GetTapaSuggestion() string { + // Create a new instance of the random number generator. + // Notice that this is from an imported Gno library + generator := rand.New() + + // Generate a random index + randomIndex := generator.Intn(len(listOfTapas)) + + // Return the random suggestion + return listOfTapas[randomIndex] +} diff --git a/docs/assets/how-to-guides/testing-gno/counter-1.gno b/docs/assets/how-to-guides/testing-gno/counter-1.gno new file mode 100644 index 00000000000..6c2f0d69209 --- /dev/null +++ b/docs/assets/how-to-guides/testing-gno/counter-1.gno @@ -0,0 +1,21 @@ +// counter-app/r/counter/counter.gno + +package counter + +import ( + "gno.land/p/demo/ufmt" +) + +var count int + +func Increment() { + count++ +} + +func Decrement() { + count-- +} + +func Render(_ string) string { + return ufmt.Sprintf("Count: %d", count) +} diff --git a/docs/assets/how-to-guides/testing-gno/counter-2.gno b/docs/assets/how-to-guides/testing-gno/counter-2.gno new file mode 100644 index 00000000000..1298432bc03 --- /dev/null +++ b/docs/assets/how-to-guides/testing-gno/counter-2.gno @@ -0,0 +1,51 @@ +// counter-app/r/counter/counter_test.gno + +package counter + +import "testing" + +func TestCounter_Increment(t *testing.T) { + // Reset the value + count = 0 + + // Verify the initial value is 0 + if count != 0 { + t.Fatalf("initial value != 0") + } + + // Increment the value + Increment() + + // Verify the initial value is 1 + if count != 1 { + t.Fatalf("initial value != 1") + } +} + +func TestCounter_Decrement(t *testing.T) { + // Reset the value + count = 0 + + // Verify the initial value is 0 + if count != 0 { + t.Fatalf("initial value != 0") + } + + // Decrement the value + Decrement() + + // Verify the initial value is 1 + if count != -1 { + t.Fatalf("initial value != -1") + } +} + +func TestCounter_Render(t *testing.T) { + // Reset the value + count = 0 + + // Verify the Render output + if Render("") != "Count: 0" { + t.Fatalf("invalid Render value") + } +} diff --git a/docs/assets/how-to-guides/write-simple-dapp/poll-1.gno b/docs/assets/how-to-guides/write-simple-dapp/poll-1.gno new file mode 100644 index 00000000000..45ac073a387 --- /dev/null +++ b/docs/assets/how-to-guides/write-simple-dapp/poll-1.gno @@ -0,0 +1,69 @@ +package poll + +import ( + "std" + + "gno.land/p/demo/avl" +) + +// Main struct +type Poll struct { + title string + description string + deadline int64 // block height + voters *avl.Tree // addr -> yes / no (bool) +} + +// Getters +func (p Poll) Title() string { + return p.title +} + +func (p Poll) Description() string { + return p.description +} + +func (p Poll) Deadline() int64 { + return p.deadline +} + +func (p Poll) Voters() *avl.Tree { + return p.voters +} + +// Poll instance constructor +func NewPoll(title, description string, deadline int64) *Poll { + return &Poll{ + title: title, + description: description, + deadline: deadline, + voters: avl.NewTree(), + } +} + +// Vote Votes for a user +func (p *Poll) Vote(voter std.Address, vote bool) { + p.Voters().Set(string(voter), vote) +} + +// HasVoted vote: yes - true, no - false +func (p *Poll) HasVoted(address std.Address) (bool, bool) { + vote, exists := p.Voters().Get(string(address)) + if exists { + return true, vote.(bool) + } + return false, false +} + +// VoteCount Returns the number of yay & nay votes +func (p Poll) VoteCount() (int, int) { + var yay int + + p.Voters().Iterate("", "", func(key string, value interface{}) bool { + vote := value.(bool) + if vote == true { + yay = yay + 1 + } + }) + return yay, p.Voters().Size() - yay +} diff --git a/docs/assets/how-to-guides/write-simple-dapp/poll-2.gno b/docs/assets/how-to-guides/write-simple-dapp/poll-2.gno new file mode 100644 index 00000000000..7720c042f98 --- /dev/null +++ b/docs/assets/how-to-guides/write-simple-dapp/poll-2.gno @@ -0,0 +1,78 @@ +package poll + +import ( + "std" + + "gno.land/p/demo/avl" + "gno.land/p/demo/poll" + "gno.land/p/demo/ufmt" +) + +// state variables +var ( + polls *avl.Tree // id -> Poll + pollIDCounter int +) + +func init() { + polls = avl.NewTree() + pollIDCounter = 0 +} + +// NewPoll - Creates a new Poll instance +func NewPoll(title, description string, deadline int64) string { + // get block height + if deadline <= std.GetHeight() { + return "Error: Deadline has to be in the future." + } + + // convert int ID to string used in AVL tree + id := ufmt.Sprintf("%d", pollIDCounter) + p := poll.NewPoll(title, description, deadline) + + // add new poll in avl tree + polls.Set(id, p) + + // increment ID counter + pollIDCounter = pollIDCounter + 1 + + return ufmt.Sprintf("Successfully created poll #%s!", id) +} + +// Vote - vote for a specific Poll +// yes - true, no - false +func Vote(pollID int, vote bool) string { + // get txSender + txSender := std.GetOrigCaller() + + id := ufmt.Sprintf("%d", pollID) + // get specific Poll from AVL tree + pollRaw, exists := polls.Get(id) + + if !exists { + return "Error: Poll with specified doesn't exist." + } + + // cast Poll into proper format + poll, _ := pollRaw.(*poll.Poll) + + voted, _ := poll.HasVoted(txSender) + if voted { + return "Error: You've already voted!" + } + + if poll.Deadline() <= std.GetHeight() { + return "Error: Voting for this poll is closed." + } + + // record vote + poll.Vote(txSender, vote) + + // update Poll in tree + polls.Set(id, poll) + + if vote == true { + return ufmt.Sprintf("Successfully voted YAY for poll #%s!", id) + } + return ufmt.Sprintf("Successfully voted NAY for poll #%s!", id) +} diff --git a/docs/assets/how-to-guides/write-simple-dapp/poll-3.gno b/docs/assets/how-to-guides/write-simple-dapp/poll-3.gno new file mode 100644 index 00000000000..281c209c1ff --- /dev/null +++ b/docs/assets/how-to-guides/write-simple-dapp/poll-3.gno @@ -0,0 +1,74 @@ +func Render(path string) string { + var b bytes.Buffer + + b.WriteString("# Polls!\n\n") + + if polls.Size() == 0 { + b.WriteString("### No active polls currently!") + return b.String() + } + polls.Iterate("", "", func(key string, value interface{}) bool { + + // cast raw data from tree into Poll struct + p := value.(*poll.Poll) + ddl := p.Deadline() + + yay, nay := p.VoteCount() + yayPercent := 0 + nayPercent := 0 + + if yay+nay != 0 { + yayPercent = yay * 100 / (yay + nay) + nayPercent = nay * 100 / (yay + nay) + } + + b.WriteString( + ufmt.Sprintf( + "## Poll #%s: %s\n", + key, // poll ID + p.Title(), + ), + ) + + dropdown := "
\nPoll details
" + + b.WriteString(dropdown + "Description: " + p.Description()) + + b.WriteString( + ufmt.Sprintf("
Voting until block: %d
Current vote count: %d", + p.Deadline(), + p.Voters().Size()), + ) + + b.WriteString( + ufmt.Sprintf("
YAY votes: %d (%d%%)", yay, yayPercent), + ) + b.WriteString( + ufmt.Sprintf("
NAY votes: %d (%d%%)
", nay, nayPercent), + ) + + dropdown = "
\nVote details" + b.WriteString(dropdown) + + p.Voters().Iterate("", "", func(key string, value interface{}) bool { + + voter := key + vote := value.(bool) + + if vote == true { + b.WriteString( + ufmt.Sprintf("
%s voted YAY!", voter), + ) + } else { + b.WriteString( + ufmt.Sprintf("
%s voted NAY!", voter), + ) + } + return false + }) + + b.WriteString("
\n\n") + return false + }) + return b.String() +} diff --git a/docs/assets/reference/standard-library/std-1.gno b/docs/assets/reference/standard-library/std-1.gno new file mode 100644 index 00000000000..5f5a5d86b76 --- /dev/null +++ b/docs/assets/reference/standard-library/std-1.gno @@ -0,0 +1,14 @@ +// returns the list of coins owned by the address +GetCoins(addr Address) (dst Coins) + +// sends coins from one address to another +SendCoins(from, to Address, amt Coins) + +// returns the total supply of the coin +TotalCoin(denom string) int64 + +// issues coins to the address +IssueCoin(addr Address, denom string, amount int64) + +// burns coins from the address +RemoveCoin(addr Address, denom string, amount int64) diff --git a/docs/assets/reference/standard-library/std-2.gno b/docs/assets/reference/standard-library/std-2.gno new file mode 100644 index 00000000000..1ef9a0a1af1 --- /dev/null +++ b/docs/assets/reference/standard-library/std-2.gno @@ -0,0 +1,4 @@ +type Coin struct { + Denom string `json:"denom"` // the symbol of the coin + Amount int64 `json:"amount"` // the quantity of the coin +} diff --git a/docs/concepts/effective-gno.md b/docs/concepts/effective-gno.md new file mode 100644 index 00000000000..ca593edf212 --- /dev/null +++ b/docs/concepts/effective-gno.md @@ -0,0 +1,684 @@ +--- +id: effective-gno +--- + +# Effective Gno + +Welcome to the guide for writing effective Gno code. This document is designed +to help you understand the nuances of Gno and how to use it effectively. + +Before we dive in, it's important to note that Gno shares several similarities +with Go. Therefore, if you haven't already, we highly recommend reading +["Effective Go"](https://go.dev/doc/effective_go) as a primer. + +## Disclaimer + +Gno is a young language. The practices we've identified are based on its current +state. As Gno evolves, new practices will emerge and some current ones may +become obsolete. We welcome your contributions and feedback. Stay updated and +help shape Gno's future! + +## Counter-intuitive good practices + +This section highlights some Gno good practices that might seem +counter-intuitive, especially if you're coming from a Go background. + +### Embrace global variables in realms + +In Gno, using global variables is not only acceptable, but also encouraged, +specifically when working with realms. This is due to the unique persistence +feature of realms. + +In Go, you would typically write your logic and maintain some state in memory. +However, to persist the state and ensure it survives a restart, you would need +to use a store (like a plain file, custom file structure, a database, a +key-value store, an API, etc.). + +In contrast, Gno simplifies this process. When you declare global variables in +Gno realms, the GnoVM automatically persists and restores them as needed between +each run. This means that the state of these variables is maintained across +different executions of the realm, providing a simple and efficient way to +manage state persistence. + +However, it's important to note that this practice is not a blanket +recommendation for all Gno code. It's specifically beneficial in the context of +realms due to their persistent characteristics. In other Gno code, such as +packages, the use of global variables is actually discouraged and may even be +completely disabled in the future. Instead, packages should use global +constants, which provide a safe and reliable way to define values that don't +change. + +Also, be mindful not to export your global variables. Doing so would make them +accessible for everyone to read and write, potentially leading to unintended +side effects. Instead, consider using getters and setters to control access to +these variables, as shown in the following pattern: + +```go +// private global variable. +var counter int + +// public getter endpoint. +func GetCounter() int { + return counter +} + +// public setter endpoint. +func IncCounter() { + counter++ +} +``` + +In this example, `GetCounter` and `IncCounter` are used to read and increment +the `counter` variable, respectively. This allows you to control how the +`counter` variable is accessed and modified, ensuring that it's used correctly +and securely. + +### Embrace `panic` + +In Gno, we have a slightly different approach to handling errors compared to Go. +While the famous [quote by Rob +Pike](https://github.com/golang/go/wiki/CodeReviewComments#dont-panic) advises +Go developers "Don't panic.", in Gno, we actually embrace `panic`. + +Panic in Gno is not just for critical errors or programming mistakes as it is in +Go. Instead, it's used as a control flow mechanism to stop the execution of a +[realm](realms.md) when something goes wrong. This could be due to an invalid input, a +failed precondition, or any other situation where it's not possible or desirable +to continue executing the contract. + +So, while in Go, you should avoid `panic` and handle `error`s gracefully, in Gno, +don't be afraid to use `panic` to enforce contract rules and protect the integrity +of your contract's state. Remember, a well-placed panic can save your contract +from a lot of trouble. + +When you return an `error` in Gno, it's like giving back any other piece of data. +It tells you something went wrong, but it doesn't stop your code or undo any +changes you made. + +But, when you use `panic` in Gno, it stops your code right away, says it failed, +and doesn't save any changes you made. This is safer when you want to stop +everything and not save wrong changes. + +In Gno, the use of `panic()` and `error` should be context-dependent to ensure +clarity and proper error handling: +- Use `panic()` to immediately halt execution and roll back the transaction when + encountering critical issues or invalid inputs that cannot be recovered from. +- Return an `error` when the situation allows for the possibility of recovery or + when the caller should decide how to handle the error. + +Consequently, reusable packages should avoid `panic()` except in assert-like +functions, such as `Must*` or `Assert*`, which are explicit about their +behavior. Packages should be designed to be flexible and not impose restrictions +that could lead to user frustration or the need to fork the code. + +```go +import "std" + +func Foobar() { + caller := std.PrevRealm().Addr() + if caller != "g1xxxxx" { + panic("permission denied") + } + // ... +} +``` + +- TODO: suggest MustXXX and AssertXXX flows in p/. + +### Understand the importance of `init()` + +In Gno, the `init()` function isn't just a function, it's a cornerstone. It's +automatically triggered when a new realm is added onchain, making it a one-time +setup tool for the lifetime of a realm. In essence, `init()` acts as a +constructor for your realm. + +Unlike Go, where `init()` is used for tasks like setting up database +connections, configuring logging, or initializing global variables every time +you start a program, in Gno, `init()` is executed once in a realm's lifetime. + +In Gno, `init()` primarily serves two purposes: +1. It establishes the initial state, specifically, setting up global variables. + - Note: global variables can often be set up just by assigning their initial value when you're declaring them. See below for an example! \ + Deciding when to initialise the variable directly, and when to set it up in `init` can be non-straightforward. As a rule of thumb, though, `init` visually marks the code as executing only when the realm is started, while assigning the variables can be less straightforward. +2. It communicates with another realm, for example, to register itself in a registry. + +```go +import "gno.land/r/some/registry" + +func init() { + registry.Register("myID", myCallback) +} + +func myCallback(a, b string) { /* ... */ } +``` + +A common use case could be to set the "admin" as the caller uploading the +package. + +```go +import ( + "std" + "time" +) + +var ( + created time.Time + admin std.Address + list = []string{"foo", "bar", time.Now().Format("15:04:05")} +) + +func init() { + created = time.Now() + // std.GetOrigCaller in the context of realm initialisation is, + // of course, the publisher of the realm :) + // This can be better than hardcoding an admin address as a constant. + admin = std.GetOrigCaller() + // list is already initialized, so it will already contain "foo", "bar" and + // the current time as existing items. + list = append(list, admin.String()) +} +``` + +In essence, `init()` in Gno is your go-to function for setting up and +registering realms. It's a powerful tool that helps keep your realms organized +and properly configured from the get-go. Acting as a constructor, it sets the +stage for the rest of your realm's lifetime. + +### A little dependency is better than a little copying + +In Go, there's a well-known saying by Rob Pike: ["A little copying is better +than a little dependency"](https://www.youtube.com/watch?v=PAAkCSZUG1c&t=568s). +This philosophy encourages developers to minimize their dependencies and instead +copy small amounts of code where necessary. While this approach often makes +sense in Go, it's not always the best strategy in Gno. + +In Gno, especially for `p/` packages, another philosophy prevails, one that is +more akin to the Node/NPM ecosystem. This philosophy encourages creating small +modules and leveraging multiple dependencies. The main reason for this shift is +code readability and trust. + +A Gno contract is not just its lines of code, but also the imports it uses. More +importantly, Gno contracts are not just for developers. For the first time, it +makes sense for users to see what functionality they are executing too. Code simplicity, transparency, +explicitness, and trustability are paramount. + +Another good reason for creating simple, focused libraries is the composability +of Go and Gno. Essentially, you can think of each `p/` package as a Lego brick +in an ever-growing collection, giving more power to users. `p/` in Gno is +basically a way to extend the standard libraries in a community-driven manner. + +Unlike other compiled languages where dependencies are not always well-known and +clear metrics are lacking, Gno allows for a reputation system not only for the +called contracts, but also for the dependencies. + +For example, you might choose to use well-crafted `p/` packages that have been +reviewed, audited, and have billions of transactions under their belt, boasting +super high stability. This approach can make your code footprint smaller and more +reliable. + +In other platforms, an audit usually involves auditing everything, including the +dependencies. However, in Gno, we can expect that over time, contracts will +become smaller, more powerful, and partially audited by default, thanks to this +enforced open-source system. + +One key difference between the Go and Gno ecosystem is the trust assumption when +adding a new dependency. Dependency code always needs to be vetted, [regardless +of what programming language or ecosystem you're using][sc-attack]. However, in +Gno, you can have the certainty that the author of a package cannot overwrite an +existing, published contract; as that is simply disallowed by the blockchain. In +other words, using existing and widely-used packages reinforces your security +rather than harming it. +[sc-attack]: https://en.wikipedia.org/wiki/Supply_chain_attack + +So, while you can still adhere to the original philosophy of minimizing +dependencies, ultimately, try to use and write super stable, simple, tested, +and focused `p/` small libraries. This approach can lead to more reliable, +efficient, and trustworthy Gno contracts. + +```go +import ( + "gno.land/p/finance/tokens" + "gno.land/p/finance/exchange" + "gno.land/p/finance/wallet" + "gno.land/p/utils/permissions" +) + +var ( + myWallet wallet.Wallet + myToken tokens.Token + myExchange exchange.Exchange +) + +func init() { + myWallet = wallet.NewWallet() + myToken = tokens.NewToken("MyToken", "MTK") + myExchange = exchange.NewExchange(myToken) +} + +func BuyTokens(amount int) { + caller := permissions.GetCaller() + permissions.CheckPermission(caller, "buy") + myWallet.Debit(caller, amount) + myExchange.Buy(caller, amount) +} + +func SellTokens(amount int) { + caller := permissions.GetCaller() + permissions.CheckPermission(caller, "sell") + myWallet.Credit(caller, amount) + myExchange.Sell(caller, amount) +} +``` + +## When Gno takes Go practices to the next level + +### Documentation is for users + +One of the well-known proverbs in Go is: ["Documentation is for +users"](https://www.youtube.com/watch?v=PAAkCSZUG1c&t=1147s), as stated by Rob +Pike. In Go, documentation is for users, but users are often developers. In Gno, +documentation is for users, and users can be other developers but also the end users. + +In Go, we usually have well-written documentation for other developers to +maintain and use our code as a library. Then, we often have another layer of +documentation on our API, sometimes with OpenAPI Specs, Protobuf, or even user +documentation. + +In Gno, the focus shifts towards writing documentation for the end user. You can +even consider that the main reader is an end user, who is not so interested in +technical details, but mostly interested in how and why they should use a +particular endpoint. Comments will be used to aid code source reading, but also to +generate documentation and even for smart wallets that need to understand what +to do. + +Inline comments have the same goal: to guide users (developers or end users) +through the code. While comments are still important for maintainability, their +main purpose in Gno is for discoverability. This shift towards user-centric +documentation reflects the broader shift in Gno towards making code more +accessible and understandable for all users, not just developers. + +TODO: `func ExampleXXX`. + +### Reflection is never clear + +In Go, there's a well-known saying by Rob Pike: ["Reflection is never +clear."](https://www.youtube.com/watch?v=PAAkCSZUG1c&t=15m22s) This statement +emphasizes the complexity and potential pitfalls of using reflection in Go. + +In Gno, reflection does not exist (yet). There are technical reasons for this, +but also a desire to create a Go alternative that is explicitly safer to use +than Go, with a smaller cognitive difficulty to read, discover, and understand. + +The absence of reflection in Gno is not just about simplicity, but also about +safety. Reflection can be powerful, but it can also lead to code that is hard to +understand, hard to debug, and prone to runtime errors. By not supporting +reflection, Gno encourages you to write code that is explicit, clear, and easy +to understand. + +We're currently in the process of considering whether to add reflection support +or not, or perhaps add it in a privileged mode for very few libraries. But for now, +when you're writing Gno code, remember: explicit is better than implicit, and +clear code is better than clever code. + +## Gno good practices + +### Package naming and organization + +Your package name should match the folder name. This helps to prevent having +named imports, which can make your code more difficult to understand and +maintain. By matching the package name with the folder name, you can ensure that +your imports are clear and intuitive. + +Ideally, package names should be short and human-readable. This makes it easier +for other developers to understand what your package does at a glance. Avoid +using abbreviations or acronyms unless they are widely understood. + +Packages and realms can be organized into subdirectories. However, consider that the +best place for your main project will likely be `r/NAMESPACE/DAPP`, similar +to how repositories are organized on GitHub. + +If you have multiple sublevels of realms, remember that they are actually +independent realms and won't share data. A good usage could be to have an +ecosystem of realms, where one realm is about storing the state, another one +about configuration, etc. But in general, a single realm makes sense. + +You can also create small realms to create your ecosystem. For example, you +could centralize all the authentication for your whole company/organization in +`r/NAMESPACE/auth`, and then import it in all your contracts. + +The `p/` prefix is different. In general, you should use top-level `p/` like +`p/NAMESPACE/DAPP` only for things you expect people to use. If your goal is +just to have internal libraries that you created to centralize your helpers and +don't expect that other people will use your helpers, then you should probably +use subdirectories like `p/NAMESPACE/DAPP/foo/bar/baz`. + +### Define types and interfaces in pure packages (p/) + +In Gno, it's common to create `p/NAMESPACE/DAPP` for defining types and +interfaces, and `r/NAMESPACE/DAPP` for the runtime, especially when the goal +for the realm is to become a standard that could be imported by `p/`. + +The reason for this is that `p/` can only import `p/`, while `r/` can import +anything. This separation allows you to define standards in `p/` that can be +used across multiple realms and packages. + +In general, you can just write your `r/` to be an app. But if for some reason +you introduce a concept that can be reused, it makes sense to have a +dedicated `p/` so that people can re-use your logic without depending on +your realm's data. + +For instance, if you want to create a token type in a realm, you can use it, and +other realms can import the realm and compose it. But if you want to create a +`p/` helper that will create a pattern, then you need to have your interface and +types defined in `p/` so anything can import it. + +By separating your types and interfaces into `p/` and your runtime into `r/`, +you can create more modular, reusable, and standardized code in Gno. This +approach allows you to leverage the composability of Gno to build more powerful +and flexible applications. + +### Design your realm as a public API + +In Go, all your packages, including your dependencies, are typically treated as +part of your safe zone, similar to a secure perimeter. The boundary is drawn +between your program and the rest of the world, which means you secure the API +itself, potentially with authentication middlewares. + +However, in Gno, your realm is the public API. It's exposed to the outside +world and can be accessed by other realms. Therefore, it's crucial to design +your realm with the same level of care and security considerations as you would +a public API. + +One approach is to simulate a secure perimeter within your realm by having +private functions for the logic and then writing your API layer by adding some +front-facing API with authentication. This way, you can control access to your +realm's functionality and ensure that only authorized callers can execute +certain operations. + +```go +import "std" + +func PublicMethod(nb int) { + caller := std.PrevRealm().Addr() + privateMethod(caller, nb) +} + +func privateMethod(caller std.Address, nb int) { /* ... */ } +``` + +In this example, `PublicMethod` is a public function that can be called by other +realms. It retrieves the caller's address using `std.PrevRealm().Addr()`, and +then passes it to `privateMethod`, which is a private function that performs the +actual logic. This way, `privateMethod` can only be called from within the +realm, and it can use the caller's address for authentication or authorization +checks. + +### Contract-level access control + +In Gno, it's a good practice to design your contract as an application with its +own access control. This means that different endpoints of your contract should +be accessible to different types of users, such as the public, admins, or +moderators. + +The goal is usually to store the admin address or a list of addresses +(`std.Address`) in a variable, and then create helper functions to update the +owners. These helper functions should check if the caller of a function is +whitelisted or not. + +Let's deep dive into the different access control mechanisms we can use: + +One strategy is to look at the caller with `std.PrevRealm()`, which could be the +EOA (Externally Owned Account), or the preceding realm in the call stack. + +Another approach is to look specifically at the EOA. For this, you should call +`std.GetOrigCaller()`, which returns the public address of the account that +signed the transaction. + +TODO: explain when to use `std.GetOrigCaller`. + +Internally, this call will look at the frame stack, which is basically the stack +of callers including all the functions, anonymous functions, other realms, and +take the initial caller. This allows you to identify the original caller and +implement access control based on their address. + +Here's an example: + +```go +import "std" + +var admin std.Address = "g1xxxxx" + +func AdminOnlyFunction() { + caller := std.PrevRealm().Addr() + if caller != admin { + panic("permission denied") + } + // ... +} + +// func UpdateAdminAddress(newAddr std.Address) { /* ... */ } +``` + +In this example, `AdminOnlyFunction` is a function that can only be called by +the admin. It retrieves the caller's address using `std.PrevRealm().Addr()`, +this can be either another realm contract, or the calling user if there is no +other intermediary realm. and then checks if the caller is the admin. If not, it +panics and stops the execution. + +The goal of this approach is to allow a contract to own assets (like grc20 or +native tokens), so that you can create contracts that can be called by another +contract, reducing the risk of stealing money from the original caller. This is +the behavior of the default grc20 implementation. + +Here's an example: + +```go +import "std" + +func TransferTokens(to std.Address, amount int64) { + caller := std.PrevRealm().Addr() + if caller != admin { + panic("permission denied") + } + // ... +} +``` + +In this example, `TransferTokens` is a function that can only be called by the +admin. It retrieves the caller's address using `std.PrevRealm().Addr()`, and +then checks if the caller is the admin. If not, the function panics and execution is stopped. + +By using these access control mechanisms, you can ensure that your contract's +functionality is accessible only to the intended users, providing a secure and +reliable way to manage access to your contract. + +### Using avl.Tree for efficient data retrieval + +In Gno, the `avl.Tree` data structure is a powerful tool for optimizing data +retrieval. It works by lazily resolving information, which means it only loads +the data you need when you need it. This allows you to scale your application +and pay less gas for data retrieval. + +[AVL is short for Adelson-Velsky and Landis:][avl-wiki] under the hood, it is an +implementation of a self-balancing binary tree. +[avl-wiki]: https://en.wikipedia.org/wiki/AVL_tree + +The `avl.Tree` can be used like a map, where you can store key-value pairs and +retrieve an entry with a simple key. However, unlike a traditional map, the +`avl.Tree` doesn't load unnecessary data. This makes it particularly efficient +for large data sets where you only need to access a small subset of the data at +a time. + +Here's an example of how you can use `avl.Tree`: + +```go +import "avl" + +var tree avl.Tree + +func GetPost(id string) *Post { + return tree.Get(id).(*Post) +} + +func AddPost(id string, post *Post) { + tree.Set(id, post) +} +``` + +In this example, `GetPost` is a function that retrieves a post from the +`avl.Tree` using an ID. It only loads the post with the specified ID, without +loading any other posts. + +In the future, we plan to add built-in "map" support that will match the +efficienty of an `avl.Tree` while offering a more intuitive API. Until then, if +you're dealing with a compact dataset, it's probably best to use slices. +For larger datasets where you need to quickly retrieve elements by keys, +`avl.Tree` is the way to go. + +You can also create SQL-like indexes by having multiple `avl.Tree` instances for +different fields. For example, you can have an `avl.Tree` for ID to *post, then +an `avl.Tree` for Tags, etc. Then, you can create reader methods that will just +retrieve what you need, similar to SQL indexes. + +By using `avl.Tree` and other efficient data structures, you can optimize your +Gno code for performance and cost-effectiveness, making your applications more +scalable and efficient. + +TODO: multi-indices example + +### Construct "safe" objects + +A safe object in Gno is an object that is designed to be tamper-proof and +secure. It's created with the intent of preventing unauthorized access and +modifications. This follows the same principle of making a package an API, but +for a Gno object that can be directly referenced by other realms. + +The goal is to create an object which, once instantiated, can be linked and its +pointer can be "stored" by other realms without issue, because it protects its +usage completely. + +```go +type MySafeStruct { + counter nb + admin std.Address +} + +func NewSafeStruct() *MySafeStruct { + caller := std.PrevRealm().Addr() + return &MySafeStruct{ + counter: 0, + admin: caller, + } +} + +func (s *MySafeStruct) Counter() int { return s.counter } +func (s *MySafeStruct) Inc() { + caller := std.PrevRealm().Addr() + if caller != s.admin { + panic("permission denied") + } + s.counter++ +} +``` + +Then, you can register this object in another or several other realms so other +realms can access the object, but still following your own rules. + +```go +import "gno.land/r/otherrealm" + +func init() { + mySafeObj := NewSafeStruct() + otherrealm.Register(mySafeObject) +} + +// then, other realm can call the public functions but won't be the "owner" of +// the object. +``` + +### Choosing between native tokens and GRC20 tokens + +In Gno, you've got two main choices for tokens: Native or GRC20. Each has its +own pros and cons, and the best fit depends on your needs. + +#### Native tokens + +Native tokens are managed by the banker module, separate from GnoVM. They're +simple, strict, and secure. You can create, transfer, and check balances with an +RPC call, no GnoVM needed. + +For example, if you're creating a coin for cross-chain transfers, native tokens +are your best bet. They're IBC-ready and their strict rules offer top-notch +security. + +Read about how to use the Banker module [here](../concepts/standard-library/banker). + +#### GRC20 tokens + +GRC20 tokens, on the other hand, are like Ethereum's ERC20 or CosmWasm's CW20. +They're flexible, composable, and perfect for DeFi protocols and DAOs. They +offer more features like token-gating, vaults, and wrapping. + +For instance, if you're creating a voting system for a DAO, GRC20 tokens are +ideal. They're programmable, can be embedded in safe Gno objects, and offer more +control. + +Remember, GRC20 tokens are more gas-intensive and aren't IBC-ready yet. They +also come with shared ownership, meaning the contract retains some control. + +In the end, your choice depends on your needs: simplicity and security with +Native tokens, or flexibility and control with GRC20 tokens. And if you want the +best of both worlds, you can wrap a native token into a GRC20 compatible +token. + +```go +import "gno.land/p/demo/grc/grc20" + +var fooToken grc20.AdminToken = grc20.NewAdminToken("Foo Token", "FOO", 4) + +func MyBalance() uint64 { + caller := std.PrevRealm().Addr() + balance, _ := fooToken.BalanceOf(caller) + return balance +} +``` + +See also: https://gno.land/r/demo/foo20 + +#### Wrapping native tokens + +Want the best of both worlds? Consider wrapping your Native tokens. This gives +your tokens the flexibility of GRC20 while keeping the security of Native +tokens. It's a bit more complex, but it's a powerful option that offers great +versatility. + +See also: https://github.com/gnolang/gno/tree/master/examples/gno.land/r/demo/wugnot + + diff --git a/docs/from-go-to-gno.md b/docs/concepts/from-go-to-gno.md similarity index 97% rename from docs/from-go-to-gno.md rename to docs/concepts/from-go-to-gno.md index fc7f28ab73a..41cccc6e971 100644 --- a/docs/from-go-to-gno.md +++ b/docs/concepts/from-go-to-gno.md @@ -1,3 +1,7 @@ +--- +id: from-go-to-gno +--- + # From Go to Gno ## Runtime comparison @@ -69,5 +73,5 @@ TODO ## See also -- [go-gno-compatibility.md](./go-gno-compatibility.md) +- [go-gno-compatibility.md](../reference/go-gno-compatibility.md) - ["go -> gno" presentation by Zack Scholl](https://github.com/gnolang/workshops/tree/main/presentations/2023-06-26--go-to-gno--schollz) diff --git a/docs/concepts/gno-language.md b/docs/concepts/gno-language.md new file mode 100644 index 00000000000..4e0605a6fac --- /dev/null +++ b/docs/concepts/gno-language.md @@ -0,0 +1,68 @@ +--- +id: gno-language +--- + +# The Gno Language + +Gno (Gnolang) is an interpretation of the widely-used Go (Golang) programming language for blockchain created by Cosmos +co-founder Jae Kwon in 2021 to mark a new era in smart contracting. Gno is almost identical to Go, so Go developers can +quickly start using it, with minimal effort. For example, Gno comes with blockchain-specific standard libraries, but any +code that doesn’t use blockchain-specific logic can run in Go with minimal processing. Libraries that could lead to +non-deterministic behaviour when executed by thousands of validators are not available in Gno, such as network access, +or determining system time. Otherwise, Gno loads and uses many standard libraries that power Go, so the experience +writing code feels very similar to Go's. + +Under the hood, the Gno code is parsed into an abstract syntax tree (AST) and the AST itself is used in the interpreter, +rather than bytecode as in many virtual machines such as Java, Python, or Wasm. The design aims to make reading & +understanding the source code of the GnoVM accessible to any Go programmer. The novel design of the intuitive GnoVM +interpreter allows Gno to freeze and resume the program by persisting and loading the memory state automatically. Gno is +deterministic, auto-persisted, and auto-Merkle-ized, allowing programs to be succinct, as the programmer doesn’t have to +serialize and deserialize objects to persist them into a database (unlike programming applications with the Cosmos SDK). + +## How Gno Differs from Go + +The composable nature of Go/Gno allows for type-checked interactions between contracts, making Gno.land safer and more +powerful, as well as operationally cheaper and faster. Smart contracts on Gno.land are light, simple, more focused, and +easily interoperable - they represent a network of interconnected contracts rather than siloed monoliths that limit +interactions with other contracts. + +## Gno Inherits Go’s Built-in Security Features + +Go supports secure programming through exported/non-exported fields, enabling a “least-authority” design. It is easy to +create objects and APIs that expose only what should be accessible to callers while hiding what should not be simply by +the capitalization of letters, thus allowing a succinct representation of secure logic that can be called by multiple +users. + +Another major advantage of Go is that the language comes with an ecosystem of great tooling, like the compiler and +third-party tools that statically analyze code. Gno inherits these advantages from Go directly to create a smart +contract programming language that provides embedding, composability, type-check safety, and garbage collection, helping +developers to write secure code relying on the compiler, parser, and interpreter to give warning alerts for common +mistakes. + +## Gno vs Solidity + +The most widely-adopted smart contract language today is Ethereum’s EVM-compatible Solidity. With bytecode built from +the ground up and Turing complete, Solidity opened up a world of possibilities for decentralized applications (dApps) +and there are currently more than 10 million contracts deployed on Ethereum. However, Solidity provides limited tooling +and its EVM has a stack limit and computational inefficiencies. + +Solidity is designed for one purpose only (writing smart contracts) and is bound by the limitations of the EVM. In +addition, developers have to learn several languages if they want to understand the whole stack or work across different +ecosystems. Gno aspires to exceed Solidity on multiple fronts (and other smart contract languages like CosmWasm or +Substrate) as every part of the stack is written in Go (or Gno!). It’s easy for developers to understand the entire system just +by studying a relatively small code base. + +## Gno Is Essential for the Wider Adoption of Web3 + +Gno makes imports as easy as they are in web2 with runtime-based imports for seamless dependency flow comprehension, and +support for complex structs, beyond primitive types. Gno is ultimately cost-effective as dependencies are loaded once, +enabling remote function calls as local, and providing automatic and independent per-realm state persistence. + +Using Gno, developers can rapidly accelerate application development and adopt a modular structure by reusing and +reassembling existing modules without building from scratch. They can embed one structure inside another in an intuitive +way while preserving localism, and the language specification is simple, successfully balancing practicality and +minimalism. + +Building on top of the excellent design of Go, the aim for Gno programming is to become the new gold standard for smart +contract development, not just in our ecosystem but blockchain as a whole. Combining Go's large success, together with +type safety and composability, Gno aims to kickstart a broader adoption of Web3 and its growth. diff --git a/docs/concepts/gno-modules.md b/docs/concepts/gno-modules.md new file mode 100644 index 00000000000..970dd16644c --- /dev/null +++ b/docs/concepts/gno-modules.md @@ -0,0 +1,42 @@ +--- +id: gno-modules +--- + +# Gno Modules + +The packages and realms containing `gno.mod` file can be referred as Gno modules. `gno.mod` file is introduced to enhance local testing and handle dependency management while testing Gno packages/realms locally. At the time of writing, `gno.mod` is only used by the `gno` tool for local development, and it is disregarded on the Gno.land chain. + +## What is the gno.mod file for? + +`gno.mod` file is very useful for local testing and development. Its primary purposes include: + +- **Working outside of the monorepo**: by adding a `gno.mod` file to your directory, all gno tooling will recognise it and understand the implicit import path of your current directory (marked by the `module` directive in your `gno.mod` file). +- **Local dependency management**: the gno.mod file allows you to manage and download local dependencies effectively when developing Go Modules. +- **Configuration and metadata (WIP)**: while the gno.mod file is currently used for specifying dependencies, it's worth noting that in the future, it might also serve as a container for additional configuration and metadata related to Gno Modules. For more information, see: [issue #498](https://github.com/gnolang/gno/issues/498). + +## Gno Modules and Subdirectories + +It's important to note that Gno Modules do not include subdirectories. Each directory within your project is treated as an individual Gno Module, and each should contain its own gno.mod file, even if it's located within an existing Gno Module directory. + +## Available gno Commands + +The gno command-line tool provides several commands to work with the gno.mod file and manage dependencies in Gno Modules: + +- **gno mod init**: small helper to initialize a new `gno.mod` file. +- **gno mod download**: downloads the dependencies specified in the gno.mod file. This command fetches the required dependencies from chain and ensures they are available for local testing and development. +- **gno mod tidy**: removes any unused dependency and adds any required but not yet listed in the file -- most of the maintenance you'll usually need to do! + +## Sample `gno.mod` file + +``` +module gno.land/p/demo/sample + +require ( + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest +) + +``` + +- **`module gno.land/p/demo/sample`**: specifies the package/realm import path. +- **`require` Block**: lists the required dependencies. Here using the latest available versions of "gno.land/p/demo/avl" and "gno.land/p/demo/testutils". These dependencies should be specified with the version "v0.0.0-latest" since on-chain packages currently do not support versioning. diff --git a/docs/concepts/gno-test.md b/docs/concepts/gno-test.md new file mode 100644 index 00000000000..e05d2db7ed5 --- /dev/null +++ b/docs/concepts/gno-test.md @@ -0,0 +1,91 @@ +--- +id: gno-test +--- + +# Gno Test + +There are two methods for testing a realm or package during the development phase: + +1. Calling the realm/package after deploying it on a local network (or testnet). +2. Using the `test` option within the [`gno`](../gno-tooling/cli/gno.md) CLI. + +While the first method is recommended for its accuracy and similarity to the actual deployment environment, it is more efficient to initially utilize the second method for composing test cases and then proceed to the first method if no errors are detected. + +This section will teach you how to use the second method. + +Writing test cases in Gno is similar to that of Go, with general rules as the following: + +* Test file naming conventions must be adhered to (ex: `xxx_test.gno`). +* Test functions must start with `Test`. +* The `t *testing.T` argument must be included in each test function. + * The `testing` package must be imported. +* Tests must be run with the `gno test` command. + +Let's write a sample code and test it. + +```go +// contract.gno + +package demo + +func Hello(name string) string { + return "Hello " + name + "!" +} +``` + +This is a simple code that returns the string-typed argument in a specific format. + +Next, we'll write a test case that looks like the following: + +```go +// contract_test.gno + +package demo + +import "testing" + +func TestHello(t *testing.T) { + { + got := Hello("People") + expected := "Hello People!" + if got != expected { + t.Fatalf("expected %q, got %q.", expected, got) + } + } + { + got := Hello("") + expected := "Hello People!" + if got != expected { + t.Fatalf("expected %q, got %q.", expected, got) + } + } +} +``` + +Two conditions exist in the test case above. + +1. "Hello People!" should be returned when calling `Hello("People")`. +2. "Hello People!" should be returned when calling `Hello("")`. + +Upon examination of our realm code and the associated test results, the initial condition exhibited the desired behavior; however, an error was identified in the second condition. +Despite the expected outcome of "Hello" being returned, the test case incorrectly specified that the expected output should be "Hello People!" instead. + +Replacing the second test case with the following will successfully fix the issue and allow the test to pass. + +```go + { + got := Hello("") + expected := "Hello !" + if expected != got { + t.Fatalf("expected %q, got %q.", expected, got) + } + } +``` + +## Blockchain context in tests +Running `gno test` executes files within the directory that end with `_test.gno` and `_filetest.gno`. +Internally, a GnoVM instance is initialized to run the test, and, at that moment, +a blockchain-related context is injected into the GnoVM. Utilizing this context, the transaction sender, +coins, block height, etc. can be mocked. + +For detailed information on these functions, refer to their [reference page](../reference/standard-library/std/testing.md). diff --git a/docs/concepts/gnovm.md b/docs/concepts/gnovm.md new file mode 100644 index 00000000000..8b76bc9e508 --- /dev/null +++ b/docs/concepts/gnovm.md @@ -0,0 +1,27 @@ +--- +id: gnovm +--- + +# GnoVM + +GnoVM is a virtual machine that interprets Gno, a custom version of Go optimized for blockchains, featuring automatic state management, full determinism, and idiomatic Go. +It works with Tendermint2 and enables smarter, more modular, and transparent appchains with embedded smart-contracts. +It can be adapted for use in TendermintCore, forks, and non-Cosmos blockchains. + +Read the ["Intro to Gnoland"](https://test3.gno.land/r/gnoland/blog:p/intro) blogpost. + +This folder focuses on the VM, language, stdlibs, tests, and tools, independent of the blockchain. +This enables non-web3 developers to contribute without requiring an understanding of the broader context. + +## Language Features + +* Like interpreted Go, but more ambitious. +* Completely deterministic, for complete accountability. +* Transactional persistence across data realms. +* Designed for concurrent blockchain smart contracts systems. + +## Getting started + +Install [`gno`](../getting-started/local-setup.md) and refer to the [`examples`](https://github.com/gnolang/gno/tree/master/examples) folder to start developing contracts. + +Check the [Makefile](https://github.com/gnolang/gno/blob/master/gnovm/Makefile) to enhance GnoVM, Gno, and stdlibs. diff --git a/docs/concepts/packages.md b/docs/concepts/packages.md new file mode 100644 index 00000000000..3978de59120 --- /dev/null +++ b/docs/concepts/packages.md @@ -0,0 +1,113 @@ +--- +id: packages +--- + +# Packages + +Packages aim to encompass functionalities that are more closely aligned with the characteristics and capabilities of realms, as opposed to standard libraries. As opposed to realms, they are stateless. + +The full list of pre-deployed available packages can be found under the [demo package](https://github.com/gnolang/gno/tree/master/examples/gno.land/p/demo). Below are some of the most commonly used packages. + +## `avl` + +In Go, the classic key/value data type is represented by the `map` construct. However, while Gno also supports the use of `map`, it is not a viable option as it lacks determinism due to its non-sequential nature. + +To address this issue, Gno implements the [AVL Tree](https://en.wikipedia.org/wiki/AVL\_tree) (Adelson-Velsky-Landis Tree) as a solution. The AVL Tree is a self-balancing binary search tree. + +The `avl` package comprises a set of functions that can manipulate the leaves and nodes of the AVL Tree. + +## `grc20` + +Gno includes an implementation of the `erc20` fungible token standard referred to as `grc20`. The interfaces of `grc20` are as follows: + +[embedmd]:# (../assets/explanation/packages/pkg-1.gno go) +```go +func TotalSupply() uint64 +func BalanceOf(account std.Address) uint64 +func Transfer(to std.Address, amount uint64) +func Approve(spender std.Address, amount uint64) +func TransferFrom(from, to std.Address, amount uint64) +func Allowance(owner, spender std.Address) uint64 +``` + +The role of each function is as follows: + +* `TotalSupply`: Returns the total supply of the token. +* `BalanceOf`: Returns the balance of tokens of an account. +* `Transfer`: Transfers specific `amount` of tokens from the `caller` of the function to the `to` address. +* `Approve`: Grants the `spender`(also referred to as `operator`) with the ability to send specific `amount` of the `caller`'s (also referred to as `owner`) tokens on behalf of the `caller`. +* `TransferFrom`: Can be called by the `operator` to send specific `amount` of `owner`'s tokens from the `owner`'s address to the `to` address. +* `Allowance`: Returns the number of tokens approved to the `spender` by the `owner`. + +Two types of contracts exist in`grc20`: + +1. `AdminToken`\ + \- Implements the token factory with `Helper` functions.\ + \- The underlying struct should not be exposed to users. However, it can be typecasted as UserToken using the `GRC20()` method. +2. `UserToken`\ + \- Implements the `IGRC20` interface.\ + \- The underlying struct can be exposed to users. Created with the `GRC20()` method of `adminToken`. + +## `grc721` + +Gno includes an implementation of the `erc721` non-fungible token standard referred to as `grc721`. The interfaces of `grc721` are as follows: + +[embedmd]:# (../assets/explanation/packages/pkg-2.gno go) +```go +// functions that work similarly to those of grc20 +func BalanceOf(owner std.Address) (uint64, error) +func Approve(approved std.Address, tid TokenID) error +func TransferFrom(from, to std.Address, tid TokenID) error + +// functions unique to grc721 +func OwnerOf(tid TokenID) (std.Address, error) +func SafeTransferFrom(from, to std.Address, tid TokenID) error +func SetApprovalForAll(operator std.Address, approved bool) error +func GetApproved(tid TokenID) (std.Address, error) +func IsApprovedForAll(owner, operator std.Address) bool +``` + +`grc721` contains a new set of functions: + +* `OwnerOf`: Returns the `owner`'s address of a token specified by its `TokenID`. +* `SafeTransferFrom`: Equivalent to the `TransferFrom` function of `grc20`. + * The `Safe` prefix indicates that the function runs a check to ensure that the `to` address is a valid address that can receive tokens. + * As you can see from the [code](https://github.com/gnolang/gno/blob/master/examples/gno.land/p/demo/grc/grc721/basic\_nft.gno#L341), the concept of `Safe` has yet to be implemented. +* `SetApprovalForAll`: Approves all tokens owned by the `owner` to an `operator`. + * You may not set multiple `operator`s. +* `GetApproved`: Returns the `address` of the `operator` for a token, specified with its `ID`. +* `IsApprovedForAll`: Returns if all NFTs of the `owner` have been approved to the `operator`. + +## `testutils` + +The `testutils` package contains a set of functions that comes in handy when testing realms. The sample function below is the commonly used `TestAddress` function that generates a random address. + +[embedmd]:# (../assets/explanation/packages/pkg-3.gno go) +```go +func TestAddress(name string) std.Address { + if len(name) > std.RawAddressSize { + panic("address name cannot be greater than std.AddressSize bytes") + } + addr := std.RawAddress{} + // TODO: use strings.RepeatString or similar. + // NOTE: I miss python's "".Join(). + blanks := "____________________" + copy(addr[:], []byte(blanks)) + copy(addr[:], []byte(name)) + return std.Address(std.EncodeBech32("g", addr)) +} +``` + +The code takes the `name` as the input and creates a random address. Below is a list of examples where it's used in the test case of the `foo20` realm. + +[embedmd]:# (../assets/explanation/packages/pkg-4.gno go) +```go +admin := users.AddressOrName("g1tntwtvzrkt2gex69f0pttan0fp05zmeg5yykv8") +test2 := users.AddressOrName(testutils.TestAddress("test2")) +recv := users.AddressOrName(testutils.TestAddress("recv")) +normal := users.AddressOrName(testutils.TestAddress("normal")) +owner := users.AddressOrName(testutils.TestAddress("owner")) +spender := users.AddressOrName(testutils.TestAddress("spender")) +recv2 := users.AddressOrName(testutils.TestAddress("recv2")) +mibu := users.AddressOrName(testutils.TestAddress("mint_burn")) +``` diff --git a/docs/proof-of-contribution.md b/docs/concepts/proof-of-contribution.md similarity index 99% rename from docs/proof-of-contribution.md rename to docs/concepts/proof-of-contribution.md index e839a3cdf1f..364a5eb254d 100644 --- a/docs/proof-of-contribution.md +++ b/docs/concepts/proof-of-contribution.md @@ -1,3 +1,7 @@ +--- +id: proof-of-contribution +--- + # Proof of Contribution The gno.land chain utilizes a reputation-based consensus mechanism instead of proof-of-stake. diff --git a/docs/concepts/realms.md b/docs/concepts/realms.md new file mode 100644 index 00000000000..c4d68636c90 --- /dev/null +++ b/docs/concepts/realms.md @@ -0,0 +1,49 @@ +--- +id: realms +--- + +# Realms + +A realm refers to a specific instance of a smart contract that can be written +in [Gno](./gno-language.md). The potentials of realms are endless - you can create virtually any +application in your mind with built-in composability, +transparency, and censorship resistance. Here are some ideas of what you can build with realms: + +* Self-custodial financial exchanges (decentralized exchanges). +* Lending platforms with better rates. +* Transparent insurance systems. +* Fair and accessible voting systems. +* Logistics and supply chain networks. + +## Packages vs Realms + +#### [**Pure Packages**](https://github.com/gnolang/gno/tree/master/examples/gno.land/p) + +* A unit that contains functionalities and utilities that can be used in realms. +* Packages are stateless. +* The default import path is `gno.land/p/~~~`. +* Can be imported to other realms or packages. +* Cannot import realms. + +#### [**Realms**](https://github.com/gnolang/gno/tree/master/examples/gno.land/r) + +* Smart contracts in Gno. +* Realms are stateful. +* Realms can own assets (tokens). +* The default import path is `gno.land/r/~~~`. +* Realms can implement `Render(path string) string` to simplify dapp frontend development by allowing users to request + markdown renderings from validators and full nodes without a transaction. + +A notable feature of realms is the `Render()` function. + +```go +package demo + +func Render(path string) string { + return "# Hello Gno!" +} +``` + +Upon calling the realm above, `# Hello Gno!` is printed with a string-typed `path` declared in an argument. It should be +noted that while the `path` argument included in the sample code is not utilized, it serves the purpose of +distinguishing the path during the rendering process. diff --git a/docs/concepts/standard-library/banker.md b/docs/concepts/standard-library/banker.md new file mode 100644 index 00000000000..a4a1104859c --- /dev/null +++ b/docs/concepts/standard-library/banker.md @@ -0,0 +1,18 @@ +--- +id: banker +--- + +# Banker + +The Banker's main purpose is to handle balance changes of [native coins](./coin.md) within Gno chains. This includes issuance, transfers, and burning of coins. + +The Banker module can be cast into 4 subtypes of bankers that expose different functionalities and safety features within your packages and realms. + +### Banker Types + +1. `BankerTypeReadonly` - read-only access to coin balances +2. `BankerTypeOrigSend` - full access to coins sent with the transaction that called the banker +3. `BankerTypeRealmSend` - full access to coins that the realm itself owns, including the ones sent with the transaction +4. `BankerTypeRealmIssue` - able to issue new coins + +The Banker API can be found under the `std` package [reference](../../reference/standard-library/std/banker.md). diff --git a/docs/concepts/standard-library/coin.md b/docs/concepts/standard-library/coin.md new file mode 100644 index 00000000000..1d79804c6b3 --- /dev/null +++ b/docs/concepts/standard-library/coin.md @@ -0,0 +1,36 @@ +--- +id: coin +--- + +# Coin + +A Coin is a native Gno type that has a denomination and an amount. Coins can be issued by the [Banker](banker.md). + +A coin is defined by the following: + +```go +type Coin struct { + Denom string `json:"denom"` + Amount int64 `json:"amount"` +} +``` + +`Denom` is the denomination of the coin, i.e. `ugnot`, and `Amount` is a non-negative +amount of the coin. + +Multiple coins can be bundled together into a `Coins` slice: + +```go +type Coins []Coin +``` + +This slice behaves like a mathematical set - it cannot contain duplicate `Coin` instances. + +The `Coins` slice can be included in a transaction made by a user addresses or a realm. +Coins in this set are then available for access by specific types of Bankers, +which can manipulate them depending on access rights. + +[//]: # (TODO ADD LINK TO Effective GNO) +Read more about coins in the [Effective Gno] section. + +The Coin(s) API can be found in under the `std` package [reference](../../reference/standard-library/std/coin.md). \ No newline at end of file diff --git a/docs/concepts/standard-library/gnopher-hole.md b/docs/concepts/standard-library/gnopher-hole.md new file mode 100644 index 00000000000..b8795cc5af7 --- /dev/null +++ b/docs/concepts/standard-library/gnopher-hole.md @@ -0,0 +1,106 @@ +--- +id: gnopher-hole-stdlib +--- + +# Gnopher Hole + +## Native bindings + +Gno has support for "natively-defined" functions exclusively within the standard +libraries. These are functions which are _declared_ in Gno code, but only _defined_ +in Go. There are generally three reasons why a function should be natively +defined: + +1. It relies on inspecting the Gno Virtual Machine itself, i.e. `std.AssertOriginCall` + or `std.CurrentRealmPath`. +2. It relies on `unsafe`, or other features which are not planned to be + available in the GnoVM, i.e. `math.Float64frombits`. +3. Its native Go performance significantly outperforms the Gno counterpart by + several orders of magnitude, and it is used in crucial code or hot paths in + many programs, i.e. `sha256.Sum256`. + +Native bindings are made to be a special feature which can be +help overcome pure Gno limitations, but it is not a substitute for writing +standard libraries in Gno. + +There are three components to a natively bound function in Gno: + +1. The Gno function declaration, which must be a top-level function with no body + (and no brackets), i.e. `crypto/sha256/sha256.gno`. +2. The Go function definition, which must be a top-level function with the same + name and signature, i.e. `crypto/sha256/sha256.go`. +3. When the two above are present and valid, the native binding can be created + by executing the code generator: either by executing `go generate` from the + `stdlibs` directory, or run `make generate` from the `gnovm` directory. + This generates the `native.go` file available in the `stdlibs` directory, + which provides the binding itself to then be used by the GnoVM. + +The code generator in question is available in the `misc/genstd` directory. +There are some quirks and features that must be kept in mind when writing native +bindings, which are the following: + +- Unexported functions (i.e. `func sum256(b []byte)`) must have their + Go counterpart prefixed with `X_` in order to make the functions exported (i.e. + `func X_sum256(b []byte)`). +- The Go function declaration may specify as the first argument + `m *gno.Machine`, where `gno` is an import for + `github.com/gnolang/gno/gnovm/pkg/gnolang`. This gives the function access to + the Virtual Machine state, and is used by functions like `std.AssertOriginCall()`. +- The Go function may change the type of any parameter or result to + `gno.TypedValue`, where `gno` is an import for the above import path. This + means that the `native.go` generated code will not attempt to automatically + convert the Gno value into the Go value, and can be useful for unsupported + conversions like interface values. +- A small set of named types are "linked" between their Gno version and Go + counterpart. For instance, `std.Address` in Gno is + `(".../tm2/pkg/crypto").Bech32Address` in Go. A list of these can be found in + `misc/genstd/mapping.go`. +- Not all type literals are currently supported when converting from their Gno + version to their Go counterpart, i.e. `struct` and `map` literals. If you intend to use these, + modify the code generator to support them. +- The code generator does not inspect any imported packages from the Go native code + to determine the default package identifier (i.e. the `package` clause). + For example, if a package is in `foo/bar`, but declares `package xyz`, when importing + foo/bar the generator will assume the name to be `bar` instead of `xyz`. + You can add an identifier to the import to fix this and use the identifier + you want/need, such as `import gno "github.com/gnolang/gno/gnovm/pkg/gnolang"`. + +## Adding new standard libraries + +New standard libraries may be added by simply creating a new directory (whose +path relative to the `stdlibs` directory will be the import path used in Gno +programs). Following that, the suggested approach for adding a Go standard +library is to copy the original files from the Go source tree, and renaming their +extensions from `.go` to `.gno`. + +:::note +As a small aid, this bash one-liner can be useful to convert all the file +extensions: +```sh +for i in *.go; do mv $i "$(echo $i | sed 's/\.go$/.gno/')"; done +``` +::: + +Following that, the suggested approach is to iteratively try running `gno test .`, +while fixing any errors that may come out of trying to test the package. + +Some things to keep in mind: + +- Gno doesn't support assembly functions and build tags. Some Go packages may + contain assembly versions for different architecture and a `generic.go` file + containing the architecture-independent version. The general approach is that + of removing everything architecture/os-specific except for the `generic.go` file. +- Gno doesn't support reflection at the time of writing, which means that for + now many packages which rely heavily on reflection have to be delayed or + reduced while we figure out the details on how to implement reflection. + Aside from the `reflect` package itself, this also translates to very common + packages still not available in Gno, such as `fmt` or `encoding/json`. +- In the package documentation, specify the Go version from which the library + was taken. +- All changes from the Go standard libraries must be explicitly marked, possibly + with `// XXX` comments as needed. + +If you intend to create a PR to add a new standard library, remember to update +[Go<\>Gno compatibility](../../reference/go-gno-compatibility.md) accordingly. + + diff --git a/docs/concepts/standard-library/overview.md b/docs/concepts/standard-library/overview.md new file mode 100644 index 00000000000..6068de83f0f --- /dev/null +++ b/docs/concepts/standard-library/overview.md @@ -0,0 +1,75 @@ +--- +id: overview +--- + +# Overview + +Gno comes with a set of standard libraries which are included to ease development and provide extended functionality to the language. These include: +- standard libraries as we know them in classic Go, i.e. `encoding/binary`, `strings`, `testing`, etc. +- a special `std` package, which contains types, interfaces, and APIs created to handle blockchain-related functionality. + + +Standard libraries differ from on-chain packages in terms of their import path structure. +Unlike on-chain [packages](../packages.md), standard libraries do not incorporate a domain-like format at the beginning +of their import path. For example: +- `import "encoding/binary"` refers to a standard library +- `import "gno.land/p/demo/avl"` refers to an on-chain package. + +To see concrete implementation details & API references, see the [reference](../../reference/standard-library/overview.md) section. + +## Accessing documentation + +Apart from the official documentation you are currently reading, you can also access documentation for the standard +libraries in several other different ways. You can obtain a list of all the available standard libraries with the following commands: + +```console +$ cd gnovm/stdlibs # go to correct directory + +$ find -type d +./testing +./math +./crypto +./crypto/chacha20 +./crypto/chacha20/chacha +./crypto/chacha20/rand +./crypto/sha256 +./crypto/cipher +... +``` + +All the packages have automatically generated documentation through the use of the +`gno doc` command, which has similar functionality and features to `go doc`: + +```console +$ gno doc encoding/binary +package binary // import "encoding/binary" + +Package binary implements simple translation between numbers and byte sequences +and encoding and decoding of varints. + +[...] + +var BigEndian bigEndian +var LittleEndian littleEndian +type AppendByteOrder interface{ ... } +type ByteOrder interface{ ... } +$ gno doc -u -src encoding/binary littleEndian.AppendUint16 +package binary // import "encoding/binary" + +func (littleEndian) AppendUint16(b []byte, v uint16) []byte { + return append(b, + byte(v), + byte(v>>8), + ) +} +``` + +`gno doc` will work automatically when used within the Gno repository or any +repository which has a `go.mod` dependency on `github.com/gnolang/gno`. + +Another alternative is setting your environment variable `GNOROOT` to point to +where you cloned the Gno repository. + +```sh +export GNOROOT=$HOME/gno +``` \ No newline at end of file diff --git a/docs/concepts/tendermint2.md b/docs/concepts/tendermint2.md new file mode 100644 index 00000000000..a6004606a78 --- /dev/null +++ b/docs/concepts/tendermint2.md @@ -0,0 +1,122 @@ +--- +id: tendermint2 +--- + +# Tendermint2 + +**Disclaimer: Tendermint2 is currently part of the Gno monorepo for streamlined development.** + +**Once Gno.land is on the mainnet, Tendermint2 will operate independently, including for governance, +on https://github.com/tendermint/tendermint2.** + +## Problems + +* Open source is open for subversion. +* Incentives and mission are misaligned. +* Need directory & forum for Tendermint/SDK forks. + +## Partial Solution: adopt principles + +* Simplicity of design. +* The code is the spec. +* Minimal code - keep total footprint small. +* Minimal dependencies - all dependencies must get audited, and become part of + the repo. +* Modular dependencies - wherever reasonable, make components modular. +* Completeness - software projects that don't become finished are projects + that are forever vulnerable. One of the primary goals of the Gno language + and related works is to become finished within a reasonable timeframe. + +## What is already proposed for Tendermint2: + +* Complete Amino. -> multiplier of productivity for SDK development, to not + have to think about protobuf at all. Use "genproto" to even auto-generate + proto3 for encoding/decoding optimization through protoc. + - MISSION: be the basis for improving the encoding standard from proto3, because + proto3 length-prefixing is slow, and we need "proto4" or "amino2". + - LOOK at the auto-generated proto files! + https://github.com/gnolang/gno/blob/master/pkgs/bft/consensus/types/cstypes.proto + for example. + - There was work to remove this from the CosmosSDK because + Amino wasn't ready, but now that it is, it makes sense to incorporate it into + Tendermint2. + + +* Remove EvidenceReactor, Evidence, Violation: + + We need to make it easy to create alt mempool reactors. + + We "kill two birds with one stone" by implementing evidence as a first-class mempool lane. + + The authors of "ABCI++" have a different set of problems to solve, so we should do both! Tendermint++ + and Tendermint2. + + +* Fix address size to 20 bytes -> 160 is sufficient, and fixing it brings optimizations. + + +* General versionset system for handshake negotiation. -> So Tendermint2 can be + used as basis for other P2P applications. + + +* EventBus -> EventSwitch. -> For indexing, use an external system. + + To ensure Tendermint2 remains minimal and easily integrated with plugin modules, there is no internal implementation. + + The use of an EventSwitch makes the process simpler and synchronous, which maintains the determinism of Tendermint + tests. + + Keeping the Tendermint protocol synchronous is sufficient for optimal performance. + + However, if there is a need for asynchronous processing due to an exceptionally large number of validators, it should + be a separate fork with a unique name under the same taxonomy as Tendermint. + + +* Fix nondeterminism in consensus tests -> in relation to the above. + +* Add "MaxDataBytes" for total tx data size limitation. + + To avoid unexpected behavior caused by changes in validator size, it's best to allocate room for each module + separately instead of limiting the total block size as we did before. + +This way, we can ensure that there's enough space for all modules. + +* Remove external dependencies like prometheus + To ensure accuracy, all metrics and events should be integrated through interfaces. This may require extracting client + logic from Prometheus, but it will be incorporated into Tendermint2 and undergo the same auditing process as + everything else. + +* General consensus/WAL -> a WAL is useful enough to warrant being a re-usable + module. + +* Remove GRPC -> GRPC support should be plugged in (say in a GRPC fork of + Tendermint2), so alternative RPC protocols can likewise be. Tendermint2 aims + to be independent of the Protobuf stack so that it can retain freedom for + improving its codec. + +* Remove dependency on viper/cobra -> I have tried to strip out what we don't + use of viper/cobra for minimalism, but could not; and viper/cobra is one + prime target for malware to be introduced. Rather than audit viper/cobra, + Tendermint2 implements a cli convention for Go-structure-based flags and cli; + so if you still want to use viper/cobra you can do so by translating flags to + an options struct. + +* Question: Which projects use ABCI sockets besides CosmosSDK derived chains? + +## Roadmap + +First, we create a multi-organizational team for Tendermint2 & +TendermintCore/++ development. We will maintain a fork of the Tendermint++ repo +and suggest changes upstream based on our work on Tendermint2, while also +porting necessary fixes from Tendermint++ over to Tendermint2. + +We will also reach out to ecosystem partners and survey and create a +directory/taxonomy for Tendermint and CosmosSDK derivatives and manage a forum +for interfork collaboration. + +Ideally, Tendermint2 and TendermintCore/++ merge into one. + +## Challenge + +Either make a PR to Gaia/CosmosSDK/TendermintCore to be like Tendermint2, or +make a PR to Tendermint2 to import a feature or fix of TendermintCore. diff --git a/docs/getting-started/browsing-gno-source-code.md b/docs/getting-started/browsing-gno-source-code.md new file mode 100644 index 00000000000..3e49fd1f57e --- /dev/null +++ b/docs/getting-started/browsing-gno-source-code.md @@ -0,0 +1,96 @@ +--- +id: browsing-gno-source-code +--- + +# Browsing Gno Source Code + +## Overview + +In this tutorial, you will learn how to browse deployed Gno [Realms](../concepts/realms.md) +and [Packages](../concepts/packages.md). Additionally, you will understand how the `Render` method is utilized +to achieve Realm state visibility. + +## Prerequisites + +- **`gnoweb` set up. Reference the [Local Setup](local-setup.md#3-installing-other-gno-tools) guide for steps** + +## 1. Start the local chain + +In order for `gnoweb` to fetch realm and package source code, it needs to connect a running Gno node. For a better +overview on running a local node, please reference the [Starting a Local Chain](setting-up-a-local-chain.md) guide. + +In this guide, we will start a local node with the default configuration. Navigate to the `gno.land` sub-folder and run: + +```bash +gnoland start +``` + +## 2. Start `gnoweb` + +Now that the chain is running, we can start the `gnoweb` tool: + +```bash +gnoweb +``` + +:::info Different JSON-RPC URL + +In case you are running a node on a different JSON-RPC URL from the default one (`http://127.0.0.1:26657`), +you can specify the remote URL with the `gnoweb` flag named `--remote` + +::: + +We should be able to access the website locally on http://127.0.0.1:8888/. + +![gnoweb screen](../assets/getting-started/browsing-gno-source-code/gnoweb.png) + +## 3. Browse Package source code + +Packages in Gno.land usually have names resembling `gno.land/p/`. Since packages do not contain state, we can only +view their source code on-chain. To learn more about Packages, please check out +the [Packages](../concepts/packages.md) explanation document. + +Using `gnoweb`, we can browse the source code in our browser. +For example, the `avl` package is deployed at `gno.land/p/demo/avl`. + +To access the source code of the `avl` package, we can append the `/p/demo/avl` to our browser URL (from the homepage). + +The final URL for the `avl` package source could be viewable at http://127.0.0.1:8888/p/demo/avl, if we followed +default setup params, as we did in this guide. + +![gnoweb avl](../assets/getting-started/browsing-gno-source-code/gnoweb-avl.png) + +From here, we can open any source code file of the deployed on-chain package and inspect its API. + +## 4. Browse Realm source code + +In contrast to Packages, Realms in Gno.land usually have names resembling `gno.land/r/`. + +Realms _do_ contain state, and in addition to being able to view their source code on-chain, users can also view their +internal state representation in the form of the `Render()` output. To learn more about Realms, please +check out the [Realms](../concepts/realms.md) explanation document. + +Using `gnoweb`, we can browse the Realm `Render()` method output and source code in our browser. +For example, the `boards` Realm is deployed at `gno.land/r/demo/boards`. + +To view the internal Realm state of the `boards` package, we can append the `/r/demo/boards` to our browser URL (from +the homepage). + +The final URL for the `boards` Realm internal state could be viewable at http://127.0.0.1:8888/r/demo/boards, if we +followed +default setup params, as we did in this guide. + +![gnoweb boards](../assets/getting-started/browsing-gno-source-code/gnoweb-boards.png) + +:::info Render() is not required + +Internal Realm state does not have to be exposed through the `Render()` method of the realm, as it is +not a requirement for deploying a Realm. + +::: + +Additionally, to view the source code for the realm, we simply need to append the `/` to the full realm path: + +http://127.0.0.1:8888/r/demo/boards/ + +![gnoweb boards source](../assets/getting-started/browsing-gno-source-code/gnoweb-boards-source.png) diff --git a/docs/getting-started/local-setup.md b/docs/getting-started/local-setup.md new file mode 100644 index 00000000000..cbef4522477 --- /dev/null +++ b/docs/getting-started/local-setup.md @@ -0,0 +1,111 @@ +--- +id: local-setup +--- + +# Local Setup + +## Overview + +In this tutorial, you will learn how to set up the Gno development environment locally, so you +can get up and running writing Gno code. You will download and install all the necessary tooling, +and validate that they are correctly configured to run on your machine. + +## Prerequisites + +- **Git** +- **`make` (for running Makefiles)** +- **Go 19+** + +## 1. Cloning the repository + +To get started with a local Gno.land development environment, you must clone the GitHub repository +somewhere on disk: + +```bash +git clone https://github.com/gnolang/gno.git +``` + +## 2. Installing the `gno` development toolkit + +Next, we are going to build and install the `gno` development toolkit. +`gno` provides ample functionality to the user, among which is running, precompiling, testing and building `.gno` files. + +To install the toolkit, navigate to the `gnovm` folder from the repository root, and run the `build` make directive: + +```bash +cd gnovm +make build +``` + +This will build out the necessary `gno` binary into the `gnovm/cmd` sub-folder: + +![gno tool build](../assets/getting-started/local-setup/make-build-gnovm.gif) + +Next, to make development easier, we need to make the binary available system-wide. +From the same `gnovm` sub-folder, you can run: + +```bash +make install +``` + +To verify the `gno` binary is installed system-wide, you can run: + +```bash +gno --help +``` + +You should get the help output from the command: + +![gno help](../assets/getting-started/local-setup/gno-help.gif) + +Alternatively, if you don't want to have the binary callable system-wide, you can run the binary directly: + +```bash +go run ./cmd/gno --help +``` + +## 3. Installing other `gno` tools + +The next step is to install several other tools that are required for the Gno development environment, like + +- `gnoland` - the Gno [blockchain node](setting-up-a-local-chain.md) +- `gnokey` - the Gno [private key manager](working-with-key-pairs.md) +- `gnoweb` - the Gno [source code viewer](browsing-gno-source-code.md) +- `gnofaucet` - the Gno [native currency faucet](setting-up-funds/running-a-faucet.md) + +To build these tools, from the root directory navigate to the `gno.land` sub-folder, and run the `build` make +directive: + +```bash +cd gno.land +make build +``` + +This will build out the necessary binaries into the `gno.land/cmd` sub-folder: + +![gno tools build](../assets/getting-started/local-setup/make-build-gnoland.gif) + +Same as with the `gno` tool, we can make these binaries available system-wide. +From the same `gno.land` sub-folder, you can run: + +```bash +make install +``` + +To verify that, for example, the `gnokey` binary is installed system-wide, you can run: + +```bash +gnokey --help +``` + +You should get the help output from the command: + +![gnokey help](../assets/getting-started/local-setup/gnokey-help.gif) + +## Conclusion + +That's it 🎉 + +You have successfully built out and installed the necessary tools for Gno development! + +In further documents, you will gain a better understanding on how they are used to make Gno work. diff --git a/docs/getting-started/setting-up-a-local-chain.md b/docs/getting-started/setting-up-a-local-chain.md new file mode 100644 index 00000000000..51fe1fd45a6 --- /dev/null +++ b/docs/getting-started/setting-up-a-local-chain.md @@ -0,0 +1,142 @@ +--- +id: setting-up-a-local-chain +--- + +# Setting up a Local Chain + +## Overview + +In this tutorial, you will learn how to start a local Gno node (and chain!). +Additionally, you will see the different options you can use to make your Gno instance unique. + +## Prerequisites + +- [`gnoland` installed](local-setup.md#3-installing-other-gno-tools). + +## Starting a node with a default configuration + +You can start a Gno blockchain node with the default configuration by navigating to the `gno.land` sub-folder and +running the following command: + +```bash +gnoland start +``` + +The command will trigger a chain initialization process (if you haven't run the node before), and start the Gno node, +which is ready to accept transactions and interact with other Gno nodes. + +![gnoland start](../assets/getting-started/setting-up-a-local-chain/gnoland-start.gif) + +To view the command defaults, simply run the `help` command: + +```bash +gnoland start --help +``` + +Let's break down the most important default settings: + +- `chainid` - the ID of the Gno chain. This is used for Gno clients, and distinguishing the chain from other Gno + chains (ex. through IBC) +- `config` - the custom node configuration file + for more details on utilizing this file +- `genesis-balances-file` - the initial premine balances file, which contains initial native currency allocations for + the chain. By default, the genesis balances file is located in `gno.land/genesis/genesis_txs.txt`, this is also the + reason why we need to navigate to the `gno.land` sub-folder to run the command with default settings +- `root-dir` - the working directory for the node configuration and node data (state DB) + +:::info Resetting the chain + +As mentioned, the working directory for the node is located in `root-dir`. To reset the chain, you need +to delete this directory and start the node up again. If you are using the default node configuration, you can run +`make fclean` from the `gno.land` sub-folder to delete the `tempdir` working directory. + +::: + +## Changing the chain ID + +:::info Changing the Gno chain ID has several implications + +- It affects how the Gno node communicates with other Gno nodes / chains +- Gno clients that communicate through JSON-RPC need to match this value + +It's important to configure your node properly before launching it in a distributed network. +Keep in mind that changes may not be applicable once connected. + +::: + +To change the Gno chain ID, run the following command: + +```bash +gnoland start --chainid NewChainID +``` + +We can verify the chain ID has been changed, by fetching the status of the node and seeing the +associated chain ID. By default, the node exposes the JSON-RPC API on `http://127.0.0.1:26657`: + +```bash +curl -H "Content-type: application/json" -d '{ + "jsonrpc": "2.0", + "method": "status", + "params": [], + "id": 1 +}' 'http://127.0.0.1:26657' +``` + +We should get a response similar to this: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "node_info": { + "version_set": [ + // ... + ], + "net_address": "g10g9r37g9xa54a6clttzmhk2gmdkzsntzty0cvr@0.0.0.0:26656", + "network": "NewChainID" + // ... + } + } +} +``` + +:::danger Chain ID can be set only once + +Since the chain ID information is something bound to a chain, you can +only change it once upon chain initialization, and further attempts to change it will +have no effect. + +::: + +## Changing the node configuration + +You can specify a node configuration file using the `--config` flag. + +```bash +gnoland start --config config.toml +``` + +## Changing the premine list + +You do not need to use the `gno.land/genesis/genesis_balances.txt` file as the source of truth for initial network +funds. + +To specify a custom balance sheet for a fresh local chain, you can use the `-genesis-balances-file`: + +```bash +gnoland start -genesis-balances-file custom-balances.txt +``` + +Make sure the balances file follows the following format: + +```text +
=ugnot +``` + +Following this pattern, potential entries into the genesis balances file would look like: + +```text +g1qpymzwx4l4cy6cerdyajp9ksvjsf20rk5y9rtt=10000000000ugnot +g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq=10000000000ugnot +``` diff --git a/docs/getting-started/setting-up-funds/premining-balances.md b/docs/getting-started/setting-up-funds/premining-balances.md new file mode 100644 index 00000000000..00076cdfda1 --- /dev/null +++ b/docs/getting-started/setting-up-funds/premining-balances.md @@ -0,0 +1,106 @@ +--- +id: premining-balances +--- + +# Premining Balances + +## Overview + +In this tutorial, you will gain an understanding on how to premine native currency on a local Gno.land chain. +Additionally, you will understand how to query the account balance after you premine it. + +Premining balance is the process of making sure some accounts (addresses) have specific funds when the chain initially +launches. In the context of local chain deployments, premine balances are used to ensure the user accounts (developers) +have ample funds to interact with the chain and facilitate contract deployments. + +## Prerequisites + +- **`gnoland` and `gnokey` set up. Reference the [Installation](../local-setup.md#3-installing-other-gno-tools) guide + for steps** + +## 1. Clean chain data + +In order for us to premine funds on a fresh chain, we need to make sure we do not have any leftover blockchain data +from previous chain runs. + +The blockchain node, when it runs, works with an embedded DB locally on disk to store execution data (such as +configuration files, or the state DB). For Gno blockchain nodes, this working directory is labeled as `testdir` by +default. + +To clean out old blockchain data, navigate to the `gno.land` folder and run the appropriate make command: + +```bash +cd gno.land +make fclean +``` + +## 2. Change the `genesis_balances.txt` file + +When the Gno node boots up, among other things, it reads a file called `genesis_balances.txt` to generate the initial +balance set for the blockchain. + +An example of how this looks like in the initial `genesis.json` file after the chain starts: + +```bash + "app_state": { + "@type": "/gno.GenesisState", + "balances": [ + "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5=10000000000000ugnot", + "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj=10000000000000ugnot", + "g1f4v282mwyhu29afke4vq5r2xzcm6z3ftnugcnv=1000000000000ugnot", + "g127jydsh6cms3lrtdenydxsckh23a8d6emqcvfa=1000000000000ugnot" + ], +``` + +The `genesis_balances.txt` file is located at `./gno.land/genesis/genesis_balances.txt`. + +To add a new entry to the premine table, simply append a line to the end of the file: + +```bash +g1qpymzwx4l4cy6cerdyajp9ksvjsf20rk5y9rtt=10000000000ugnot # My address +``` + +Replace `g1qpymzwx4l4cy6cerdyajp9ksvjsf20rk5y9rtt` with the address you want balance on, and `10000000000ugnot` with the +desired `ugnot` balance. + +## 3. Start the local chain + +Now that our address and the desired premine balance are located in the `genesis_balances.txt` file, we can start the +local Gno node. + +To run the local Gno node, make sure you are in the `gno.land` sub-folder, and run the appropriate make command: + +```bash +cd gno.land +gnoland start +``` + +This command will initialize the Gno node, generate the `genesis.json` with our newly added premine information, and +start the chain. + +![gnoland start](../../assets/getting-started/setting-up-funds/gnoland-start.gif) + +## 3. Check the account balance + +To check the balance of any account (or the account we just premined), we can use the following ABCI query: + +```bash +gnokey query --remote localhost:26657 bank/balances/g1qpymzwx4l4cy6cerdyajp9ksvjsf20rk5y9rtt +``` + +Let's break down this command: + +- **`--remote`** - the JSON-RPC URL of the running Gno node. In the case of a local deployment, the default value + is `localhost:26657` +- **`bank/balances/g1qpymzwx4l4cy6cerdyajp9ksvjsf20rk5y9rtt`** - the ABCI query targets the `bank` module to find + the `balances` for address `g1qpymzwx4l4cy6cerdyajp9ksvjsf20rk5y9rtt`. Replace the address with your desired address + +![gnokey query](../../assets/getting-started/setting-up-funds/gnokey-query.gif) + +## Conclusion + +That's it 🎉 + +You have successfully premined a native currency balance on a locally-running Gno chain! +Additionally, you have also learned how to query the native currency balance for an address, using built-in ABCI queries +and the `gnokey` tool. diff --git a/docs/getting-started/setting-up-funds/running-a-faucet.md b/docs/getting-started/setting-up-funds/running-a-faucet.md new file mode 100644 index 00000000000..3bd0e55b791 --- /dev/null +++ b/docs/getting-started/setting-up-funds/running-a-faucet.md @@ -0,0 +1,100 @@ +--- +id: running-a-faucet +--- + +# Running a Faucet + +## Overview + +In this tutorial, we will cover how to run a local native currency faucet that works seamlessly with a Gno node. +Using the faucet, any address can get a hold of native currency funds in case they +haven't [premined a balance beforehand](premining-balances.md). + +## Prerequisites + +- **`gnoland`, `gnofaucet` and `gnoweb` set up. Reference + the [Installation](../local-setup.md#3-installing-other-gno-tools) guide for steps** + +## 1. Ensure a topped-up faucet address + +The Gno faucet works by designating a single address as a faucet address that will distribute funds. + +Ensure the faucet account will have enough funds by [premining its balance](premining-balances.md) to a high value. +In case you do not have an existing address added to `gnokey`, you can consult +the [Working with Key Pairs](../working-with-key-pairs.md) guide. + +## 2. Start the local chain + +After ensuring the faucet address will have enough funds in the premine, we +can [run the local blockchain node](../setting-up-a-local-chain.md). +Navigate to the `gno.land` sub-folder and run the appropriate make command: + +```bash +cd gno.land +gnoland start +``` + +## 3. Start the faucet + +After the chain is up and running locally, you can start the faucet by running the following command: + +```bash +gnofaucet serve --chain-id dev MyKey +``` + +The command will prompt you to enter the decryption password for the key you've provided. + +- **`--chain-id`** - the chain ID of the local running node. The default value is `dev` +- **`MyKey`** - the name of the faucet key (you can also use the address) we premined in + the [previous steps](#1-ensure-a-topped-up-faucet-address) + +This will initialize the faucet to listen on port `5050`, by default. + +![gnofaucet serve](../../assets/getting-started/setting-up-funds/gnofaucet-serve.gif) + +## 4. Start the `gnoweb` interface + +To access the faucet UI, we need to start the local `gnoweb` interface. + +Navigate to the `gno.land` subfolder, and run the appropriate binary: + +```bash +cd gno.land +gnoweb +``` + +This will initialize the `gnoweb` interface on `http://127.0.0.1:8888`. + +![gnoweb](../../assets/getting-started/setting-up-funds/gnoweb.gif) + +## 5. Use the deployed faucet + +Once `gnoweb` has been started, you can navigate to http://127.0.0.1:8888/faucet. + +Simply input the desired address you wish to receive funds on (`1 GNOT` by default), and press the `GO` button. + +![gnofaucet page](../../assets/getting-started/setting-up-funds/faucet-page.png) + +After you've added the address, you should see a success message in the browser: + +``` +faucet success +``` + +In the terminal where `gnofaucet` is running, you should see a success message as well, something along the lines of: + +```bash +will deliver: {"msg":[{"@type":"/bank.MsgSend","from_address":"g155n659f89cfak0zgy575yqma64sm4tv6exqk99","to_address":"g1qpymzwx4l4cy6cerdyajp9ksvjsf20rk5y9rtt","amount":"1000000ugnot"}],"fee":{"gas_wanted":"50000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A10ufcOV5WP71K+KvLagJi+3TSCkx8EWKep3NbjVclU8"},"signature":"7Y0hkdPBruzMiANAHXWx3luAMhQN6SF3AQtstvOSZJI5P4uep8RIntw2c8W5blFiCd9HoMiEZFNf5dgWYwkjmA=="}],"memo":""} + +OK! +GAS WANTED: 50000 +GAS USED: 41971 +127.0.0.1 faucet success +``` + +## Conclusion + +That's it 🎉 + +You have successfully set up a GNOT faucet on a locally-running Gno chain! +Additionally, you have also learned how to utilize the `gnoweb` tool for a visual faucet UI. diff --git a/docs/getting-started/working-with-key-pairs.md b/docs/getting-started/working-with-key-pairs.md new file mode 100644 index 00000000000..93a66e3fedc --- /dev/null +++ b/docs/getting-started/working-with-key-pairs.md @@ -0,0 +1,189 @@ +--- +id: working-with-key-pairs +--- + +# Working with Key Pairs + +## Overview + +In this tutorial, you will learn how to manage private user keys, which are required for interacting with the Gno.land +blockchain. You will understand what mnemonics are, how they are used, and how you can make interaction seamless with +Gno. + +## Prerequisites + +- **`gnokey` set up. Reference the [Local Setup](local-setup.md#3-installing-other-gno-tools) guide for steps** + +## Listing available keys + +`gnokey` works by creating a local directory in the filesystem for storing (encrypted!) user private keys. + +You can find this repository by checking the value of the `--home` flag when running the following command: + +```bash +gnokey --help +``` + +Example output: + +```bash +USAGE + [flags] [...] + +Manages private keys for the node + +SUBCOMMANDS + add Adds key to the keybase + delete Deletes a key from the keybase + generate Generates a bip39 mnemonic + export Exports private key armor + import Imports encrypted private key armor + list Lists all keys in the keybase + sign Signs the document + verify Verifies the document signature + query Makes an ABCI query + broadcast Broadcasts a signed document + maketx Composes a tx document to sign + +FLAGS + -config ... config file (optional) + -home $XDG_CONFIG/gno home directory + -insecure-password-stdin=false WARNING! take password from stdin + -quiet=false suppress output during execution + -remote 127.0.0.1:26657 remote node URL +``` + +In this example, the directory where `gnokey` will store working data +is `/Users/zmilos/Library/Application Support/gno`. + +Keep note of this directory, in case you need to reset the keystore, or migrate it for some reason. +You can provide a specific `gnokey` working directory using the `--home` flag. + +To list keys currently present in the keystore, we can run: + +```bash +gnokey list +``` + +In case there are no keys present in the keystore, the command will simply return an empty response. +Otherwise, it will return the list of keys and their accompanying metadata as a list, for example: + +```bash +0. Manfred (local) - addr: g15uk9d6feap7z078ttcnwc94k60ullrvhmynxjt pub: gpub1pgfj7ard9eg82cjtv4u4xetrwqer2dntxyfzxz3pqvn87u43scec4zfgn4la3nt237nehzydzayqxe43fx63lq6rty9c5almet4, path: +1. Milos (local) - addr: g15lppu0tuxets0c0t80tncs4enqzgxt7v4eftcj pub: gpub1pgfj7ard9eg82cjtv4u4xetrwqer2dntxyfzxz3pqw2kkzujprgrfg7vumg85mccsf790n5ep6htpygkuwedwuumf2g7ydm4vqf, path: +``` + +The key response consists of a few pieces of information: + +- The name of the private key +- The derived address (`addr`) +- The public key (`pub`) + +Using these pieces of information, we can interact with Gno.land tools and write blockchain applications. + +## Generating a BIP39 mnemonic + +Using `gnokey`, we can generate a [mnemonic phrase](https://en.bitcoin.it/wiki/Seed_phrase) based on +the [BIP39 standard](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki). + +To generate the mnemonic phrase in the console, you can run: + +```bash +gnokey generate +``` + +![gnokey generate](../assets/getting-started/creating-a-key-pair/gnokey-generate.gif) + +## Adding a random private key + +If we wanted to add a new private key to the keystore, we can run the following command: + +```bash +gnokey add MyKey +``` + +Of course, you can replace `MyKey` with whatever name you want for your key. + +The `gnokey` tool will prompt you to enter a password to encrypt the key on disk (don't forget this!). +After you enter the password, the `gnokey` tool will add the key to the keystore, and return the accompanying [mnemonic +phrase](https://en.bitcoin.it/wiki/Seed_phrase), which you should remember somewhere if you want to recover the key at a +future point in time. + +![gnokey add random](../assets/getting-started/creating-a-key-pair/gnokey-add-random.gif) + +You can check that the key was indeed added to the keystore, by listing available keys: + +```bash +gnokey list +``` + +![gnokey list](../assets/getting-started/creating-a-key-pair/gnokey-list.gif) + +## Adding a private key using a mnemonic + +To add a private key to the `gnokey` keystore [using an existing mnemonic](#generating-a-bip39-mnemonic), we can run the +following command with the +`--recover` flag: + +```bash +gnokey add --recover MyKey +``` + +Of course, you can replace `MyKey` with whatever name you want for your key. + +By following the prompts to encrypt the key on disk, and providing a BIP39 mnemonic, we can successfully add +the key to the keystore. + +![gnokey add mnemonic](../assets/getting-started/creating-a-key-pair/gnokey-add-mnemonic.gif) + +## Deleting a private key + +To delete a private key from the `gnokey` keystore, we need to know the name or address of the key to remove. +After we have this information, we can run the following command: + +```bash +gnokey delete MyKey +``` + +After entering the key decryption password, the key will be deleted from the keystore. + +:::caution Recovering a private key +In case you delete or lose access to your private key in the `gnokey` keystore, you +can recover it using the key's mnemonic, or by importing it if it was exported at a previous point in time. +::: + +## Exporting a private key + +Private keys stored in the `gnokey` keystore can be exported to a desired place +on the user's filesystem. + +Keys are exported in their original armor, encrypted or unencrypted. + +To export a key from the keystore, you can run: + +```bash +gnokey export -key MyKey -output-path ~/Work/gno-key.asc +``` + +Follow the prompts presented in the terminal. Namely, you will be asked to decrypt the key in the keystore, +and later to encrypt the armor file on disk. It is worth noting that you can also export unencrypted key armor, using +the `--unsafe` flag. + +![gnokey export](../assets/getting-started/creating-a-key-pair/gnokey-export.gif) + +## Importing a private key + +If you have an exported private key file, you can import it into `gnokey` fairly easily. + +For example, if the key is exported at `~/Work/gno-key.asc`, you can run the following command: + +```bash +gnokey import -armor-path ~/Work/gno-key.asc -name ImportedKey +``` + +You will be asked to decrypt the encrypted private key armor on disk (if it is encrypted, if not, use the `--unsafe` +flag), and then to provide an encryption password for storing the key in the keystore. + +After executing the previous command, the `gnokey` keystore will have imported `ImportedKey`. + +![gnokey import](../assets/getting-started/creating-a-key-pair/gnokey-import.gif) diff --git a/docs/gno-tooling/cli/gno.md b/docs/gno-tooling/cli/gno.md new file mode 100644 index 00000000000..e72f1414102 --- /dev/null +++ b/docs/gno-tooling/cli/gno.md @@ -0,0 +1,57 @@ +--- +id: gno-tooling-gno +--- + +# gno + +`gno` is a handy tool for developing and prototyping Gno packages and realms. You may use `gno` to use the GnoVM without an actual blockchain to build or test realms in a local environment. + +## Run `gno` Commands + +The following command will run `gno`. + +```bash +gno {SUB_COMMAND} +``` + +**Subcommands** + +| Name | Description | +| ------------ | ------------------------------------------ | +| `test` | Tests a gno package. | +| `precompile` | Precompiles a `.gno` file to a `.go` file. | +| `repl` | Starts a GnoVM REPL. | + +### `test` + +#### **Options** + +| Name | Type | Description | +| ------------ | ------------- | ------------------------------------------------------------------ | +| `verbose` | Boolean | Displays extended information. | +| `root-dir` | String | Clones location of github.com/gnolang/gno (gno tries to guess it). | +| `run` | String | Test name filtering pattern. | +| `timeout` | time.Duration | The maximum execution time in ns. | +| `precompile` | Boolean | Precompiles a `.gno` file to a `.go` file before testing. | + +### `precompile` + +#### **Options** + +| Name | Type | Description | +| ----------- | ------- | --------------------------------------------------------------- | +| `verbose` | Boolean | Displays extended information. | +| `skip-fmt` | Boolean | Skips the syntax checking of generated `.go` files. | +| `gobuild` | Boolean | Run `go build` on generated `.go` files, ignoring test files. | +| `go-binary` | String | The go binary to use for building (default: `go`). | +| `gofmt` | String | The gofmt binary to use for syntax checking (default: `gofmt`). | +| `output` | String | The output directory (default: `.`). | + +### `repl` + +#### **Options** + +| Name | Type | Description | +| ---------- | ------- | ------------------------------------------------------------------ | +| `verbose` | Boolean | Displays extended information. | +| `root-dir` | String | Clones location of github.com/gnolang/gno (gno tries to guess it). | diff --git a/docs/gno-tooling/cli/gnodev.md b/docs/gno-tooling/cli/gnodev.md new file mode 100644 index 00000000000..fae635ed5b7 --- /dev/null +++ b/docs/gno-tooling/cli/gnodev.md @@ -0,0 +1,49 @@ +--- +id: gno-tooling-gnodev +--- + +# gnodev + +Gnodev allows for quick and efficient development of Gno code. + +By watching your development directory, gnodev detects changes in your Gno +code, reflecting them in the state of the node immediately. Gnodev also runs a +local instance of `gnoweb`, allowing you to see the rendering of your Gno code instantly. + +## Features +- **In-Memory Node**: Gnodev starts an in-memory node, and automatically loads +the **examples** folder and any user-specified paths. +- **Web Interface Server**: Gnodev automatically starts a `gnoweb` server on +[`localhost:8888`](https://localhost:8888). +- **Hot Reload**: Gnodev monitors the **examples** folder and any specified for file changes, +reloading and automatically restarting the node as needed. +- **State Maintenance**: Gnodev replays all transactions in between reloads, +ensuring the previous node state is preserved. + +## Installation +Gnodev can be found in the `contribs` folder in the monorepo. +To install `gnodev`, run `make install`. + +## Usage +Gnodev can be run from anywhere on the machine it was installed on, and it will +automatically load the examples folder, providing all the packages and realms found in it for use. + +![gnodev_usage](../../assets/gno-tooling/gnodev/gnodev.gif) + +For hot reloading, `gnodev` watches the examples folder, as well as any specified folder: +``` +gnodev ./myrealm +``` + +While `gnodev` is running, the following shortcuts are available: +- To reload manually, press `R`. +- To reset the state of the node, press `CMD+R`. +- To see help, press `H`. +- To stop `gnodev`, press `CMD+C`. + +### Options + +| Flag | Effect | +|------------|-----------------------------------------------------| +| --minimal | Start `gnodev` without loading the examples folder. | +| --no-watch | Disable hot reload. | \ No newline at end of file diff --git a/docs/gno-tooling/cli/gnofaucet.md b/docs/gno-tooling/cli/gnofaucet.md new file mode 100644 index 00000000000..3e70add89c0 --- /dev/null +++ b/docs/gno-tooling/cli/gnofaucet.md @@ -0,0 +1,61 @@ +--- +id: gno-tooling-gnofaucet +--- + +# gnofaucet + +`gnofaucet` is a server for distributing GNOT, the gas currency of Gnoland, to specific addresses in a local chain. +Interact with the `gnofaucet` from an address with an empty balance in your locally built testnet to fuel it with GNOT +to pay for transactions. + +## Run `gnofaucet` Commands + +Enable the faucet using the following command. + +```bash +gnofaucet serve +``` + +#### **Options** + +| Name | Type | Description | +|---------------------------|---------|--------------------------------------------------------------------------------------| +| `chain-id` | String | The id of the chain (required). | +| `gas-wanted` | Int64 | The maximum amount of gas to use for the transaction (default: `50000`) | +| `gas-fee` | String | The gas fee to pay for the transaction. | +| `memo` | String | Any descriptive text (default: `""`) | +| `test-to` | String | Test address (optional) | +| `send` | String | Coins to send (default: `"1000000ugnot"`). | +| `captcha-secret` | String | The secret key for the recaptcha. If empty, the captcha is disabled (default: `""`). | +| `is-behind-proxy` | Boolean | Uses X-Forwarded-For IP for throttling (default: `false`). | +| `insecure-password-stdin` | Boolean | INSECURE! Takes password from stdin (default: `false`). | + +## Example + +### Step 1. Create an account named `test1` with the test seed phrase below. + +```bash +gnokey add test1 --recover +``` + +> **Test Seed Phrase:** source bonus chronic canvas draft south burst lottery vacant surface solve popular case indicate +> oppose farm nothing bullet exhibit title speed wink action roast + +### **Step 2. Run `gnofaucet`** + +```bash +gnofaucet serve test1 --chain-id dev --send 500000000ugnot +``` + +### **Step 3. Receive GNOTs from the faucet** + +To receive funds through the `gnoweb` form GUI, you can request them on: +`http://localhost:8888/faucet` (given `http://localhost:8888/` is the location where `gnoweb` is serving pages). + +Alternatively, you can request funds from the faucet by directly invoking a CURL command: + +```bash +curl --location --request POST 'http://localhost:5050' \ +--header 'Content-Type: application/x-www-form-urlencoded' \ +--data-urlencode 'toaddr={address to receive}' +``` diff --git a/docs/gno-tooling/cli/gnokey.md b/docs/gno-tooling/cli/gnokey.md new file mode 100644 index 00000000000..9103e6c5801 --- /dev/null +++ b/docs/gno-tooling/cli/gnokey.md @@ -0,0 +1,307 @@ +--- +id: gno-tooling-gnokey +--- + +# gnokey + +Used for account & key management and general interactions with the Gnoland blockchain. + +## Generate a New Seed Phrase + +Generate a new seed phrase and add it to your keybase with the following command. + +```bash +gnokey generate +``` + +## Add a New Key + +You can add a new private key to the keybase using the following command. + +```bash +gnokey add {KEY_NAME} +``` + +#### **Options** + +| Name | Type | Description | +| ----------- | ---------- | -------------------------------------------------------------------------------------- | +| `account` | UInt | Account number for HD derivation. | +| `dryrun` | Boolean | Performs action, but doesn't add key to local keystore. | +| `index` | UInt | Address index number for HD derivation. | +| `ledger` | Boolean | Stores a local reference to a private key on a Ledger device. | +| `multisig` | String \[] | Constructs and stores a multisig public key (implies `--pubkey`). | +| `nobackup` | Boolean | Doesn't print out seed phrase (if others are watching the terminal). | +| `nosort` | Boolean | Keys passed to `--multisig` are taken in the order they're supplied. | +| `pubkey` | String | Parses a public key in bech32 format and save it to disk. | +| `recover` | Boolean | Provides seed phrase to recover existing key instead of creating. | +| `threshold` | Int | K out of N required signatures. For use in conjunction with --multisig (default: `1`). | + +> **Test Seed Phrase:** source bonus chronic canvas draft south burst lottery vacant surface solve popular case indicate oppose farm nothing bullet exhibit title speed wink action roast + +### Using a ledger device + +You can add a ledger device using the following command + +> [!NOTE] +> Before running this command make sure your ledger device is connected, with the cosmos app installed and open in it. + +```bash +gnokey add {LEDGER_KEY_NAME} --ledger +``` + + + +## List all Known Keys + +List all keys stored in your keybase with the following command. + +```bash +gnokey list +``` + +## Delete a Key + +Delete a key from your keybase with the following command. + +```bash +gnokey delete {KEY_NAME} +``` + +#### **Options** + +| Name | Type | Description | +| ------- | ------- | ---------------------------- | +| `yes` | Boolean | Skips confirmation prompt. | +| `force` | Boolean | Removes key unconditionally. | + + +## Export a Private Key (Encrypted & Unencrypted) + +Export a private key's (encrypted or unencrypted) armor using the following command. + +```bash +gnokey export +``` + +#### **Options** + +| Name | Type | Description | +| ------------- | ------ | ------------------------------------------- | +| `key` | String | Name or Bech32 address of the private key | +| `output-path` | String | The desired output path for the armor file | +| `unsafe` | Bool | Export the private key armor as unencrypted | + + +## Import a Private Key (Encrypted & Unencrypted) + +Import a private key's (encrypted or unencrypted) armor with the following command. + +```bash +gnokey import +``` + +#### **Options** + +| Name | Type | Description | +| ------------ | ------ | ------------------------------------------- | +| `armor-path` | String | The path to the encrypted armor file. | +| `name` | String | The name of the private key. | +| `unsafe` | Bool | Import the private key armor as unencrypted | + + +## Make an ABCI Query + +Make an ABCI Query with the following command. + +```bash +gnokey query {QUERY_PATH} +``` + +#### **Query** + +| Query Path | Description | Example | +| ------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- | +| `auth/accounts/{ADDRESS}` | Returns information about an account. | `gnokey query auth/accounts/g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5` | +| `bank/balances/{ADDRESS}` | Returns balances of an account. | `gnokey query bank/balances/g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5` | +| `vm/qfuncs` | Returns public facing function signatures as JSON. | `gnokey query vm/qfuncs --data "gno.land/r/demo/boards"` | +| `vm/qfile` | Returns the file bytes, or list of files if directory. | `gnokey query vm/qfile --data "gno.land/r/demo/boards"` | +| `vm/qrender` | Calls .Render(path) in readonly mode. | `gnokey query vm/qrender --data "gno.land/r/demo/boards"` | +| `vm/qeval` | Evaluates any expression in readonly mode and returns the results. | `gnokey query vm/qeval --data "gno.land/r/demo/boards GetBoardIDFromName("my_board")"` | +| `vm/store` | (not yet supported) Fetches items from the store. | - | +| `vm/package` | (not yet supported) Fetches a package's files. | - | + +#### **Options** + +| Name | Type | Description | +| -------- | --------- | ---------------------------------------- | +| `data` | UInt8 \[] | Queries data bytes. | +| `height` | Int64 | (not yet supported) Queries height. | +| `prove` | Boolean | (not yet supported) Proves query result. | + + +## Sign and Broadcast a Transaction + +You can sign and broadcast a transaction with the following command. + +```bash +gnokey maketx {SUB_COMMAND} {ADDRESS or KeyName} +``` + +#### **Subcommands** + +| Name | Description | +| -------- | ---------------------------- | +| `addpkg` | Uploads a new package. | +| `call` | Calls a public function. | +| `send` | The amount of coins to send. | + +### `addpkg` + +This subcommand lets you upload a new package. + +```bash +gnokey maketx addpkg \ + -deposit="1ugnot" \ + -gas-fee="1ugnot" \ + -gas-wanted="5000000" \ + -pkgpath={Registered Realm path} \ + -pkgdir={Package folder path} \ + {ADDRESS} \ + > unsigned.tx +``` + +#### **SignBroadcast Options** + +| Name | Type | Description | +| ------------ | ------- | ------------------------------------------------------------------------ | +| `gas-wanted` | Int64 | The maximum amount of gas to use for the transaction. | +| `gas-fee` | String | The gas fee to pay for the transaction. | +| `memo` | String | Any descriptive text. | +| `broadcast` | Boolean | Broadcasts the transaction. | +| `chainid` | String | Defines the chainid to sign for (should only be used with `--broadcast`) | + +#### **makeAddPackageTx Options** + +| Name | Type | Description | +| --------- | ------ | ------------------------------------- | +| `pkgpath` | String | The package path (required). | +| `pkgdir` | String | The path to package files (required). | +| `deposit` | String | The amount of coins to send. | + + +### `call` + +This subcommand lets you call a public function. + +```bash +# Register +gnokey maketx call \ + -gas-fee="1ugnot" \ + -gas-wanted="5000000" \ + -pkgpath="gno.land/r/demo/users" \ + -send="200000000ugnot" \ + -func="Register" \ + -args="" \ + -args={NAME} \ + -args="" \ + {ADDRESS} \ + > unsigned.tx +``` + +#### **SignBroadcast Options** + +| Name | Type | Description | +| ------------ | ------- | ---------------------------------------------------------------- | +| `gas-wanted` | Int64 | The maximum amount of gas to use for the transaction. | +| `gas-fee` | String | The gas fee to pay for the transaction. | +| `memo` | String | Any descriptive text. | +| `broadcast` | Boolean | Broadcasts the transaction. | +| `chainid` | String | The chainid to sign for (should only be used with `--broadcast`) | + +#### **makeCallTx Options** + +| Name | Type | Description | +|-----------|--------|------------------------------------------------------------------------------------------------------------------------------------------------------| +| `send` | String | The amount of coins to send. | +| `pkgpath` | String | The package path (required). | +| `func` | String | The contract to call (required). | +| `args` | String | An argument of the function being called. Can be used multiple times in a single `call` command to accommodate possible multiple function arguments. | + +:::info +Currently, only primitive types are supported as `-args` parameters. This limitation will be addressed in the future. +Alternatively, see how `maketx run` works. +::: + +### `send` + +This subcommand lets you send a native currency to an address. + +```bash +gnokey maketx send \ + -gas-fee="1ugnot" \ + -gas-wanted="5000000" \ + -send={SEND_AMOUNT} \ + -to={TO_ADDRESS} \ + {ADDRESS} \ + > unsigned.tx +``` + +#### **SignBroadcast Options** + +| Name | Type | Description | +| ------------ | ------- | ----------------------------------------------------- | +| `gas-wanted` | Int64 | The maximum amount of gas to use for the transaction. | +| `gas-fee` | String | The gas fee to pay for the transaction. | +| `memo` | String | Any descriptive text. | +| `broadcast` | Boolean | Broadcasts the transaction. | +| `chainid` | String | The chainid to sign for (implies `--broadcast`) | + +#### **makeSendTx Options** + +| Name | Type | Description | +| ------ | ------ | ------------------------ | +| `send` | String | Amount of coins to send. | +| `to` | String | The destination address. | + + +## Sign a Document + +Sign a document with the following command. + +```bash +gnokey sign +``` + +#### **Options** + +| Name | Type | Description | +| ---------------- | ------- | ---------------------------------------------------------- | +| `txpath` | String | The path to file of tx to sign (default: `-`). | +| `chainid` | String | The chainid to sign for (default: `dev`). | +| `number` | UInt | The account number of the account to sign with (required) | +| `sequence` | UInt | The sequence number of the account to sign with (required) | +| `show-signbytes` | Boolean | Shows signature bytes. | + + +## Verify a Document Signature + +Verify a document signature with the following command. + +```bash +gnokey verify +``` + +#### **Options** + +| Name | Type | Description | +| --------- | ------ | ---------------------------------------- | +| `docpath` | String | The path of the document file to verify. | + +## Broadcast a Signed Document + +Broadcast a signed document with the following command. + +```bash +gnokey broadcast {signed transaction file document} +``` diff --git a/docs/gno-tooling/cli/gnoland.md b/docs/gno-tooling/cli/gnoland.md new file mode 100644 index 00000000000..3c05897646b --- /dev/null +++ b/docs/gno-tooling/cli/gnoland.md @@ -0,0 +1,31 @@ +--- +id: gno-tooling-gnoland +--- + +# gnoland + +## Run a Gnoland Node + +Start a node on the Gnoland blockchain with the following command. + +```bash +gnoland +``` + +### **Sub Commands** +| Command | Description | +| --------- | ----------------- | +| `start` | Run the full node | + + +### **Options** + +| Name | Type | Description | +| -------------------------- | ------- | --------------------------------------------------------------------------------------- | +| `chainid` | String | The id of the chain (default: `dev`). | +| `genesis-balances-file` | String | The initial GNOT distribution file (default: `./gnoland/genesis/genesis_balances.txt`). | +| `genesis-remote` | String | Replacement '%%REMOTE%%' in genesis (default: `"localhost:26657"`). | +| `genesis-txs-file` | String | Initial txs to be executed (default: `"./gnoland/genesis/genesis_txs.txt"`). | +| `root-dir` | String | directory for config and data (default: `testdir`). | +| `skip-failing-genesis-txs` | Boolean | Skips transactions that fail from the `genesis-txs-file` | +| `skip-start` | Boolean | Quits after initialization without starting the node. | diff --git a/docs/go-gno-compatibility.md b/docs/go-gno-compatibility.md deleted file mode 120000 index a15871f0340..00000000000 --- a/docs/go-gno-compatibility.md +++ /dev/null @@ -1 +0,0 @@ -../gnovm/docs/go-gno-compatibility.md \ No newline at end of file diff --git a/docs/how-to-guides/connect-wallet-dapp.md b/docs/how-to-guides/connect-wallet-dapp.md new file mode 100644 index 00000000000..294323b5560 --- /dev/null +++ b/docs/how-to-guides/connect-wallet-dapp.md @@ -0,0 +1,115 @@ +--- +id: connect-wallet-dapp +--- + +# How to connect a wallet to a dApp + +As a dapp developer, you must integrate a web3 wallet with your application to enable users to interact with your +application. Upon integration, you may retrieve account information of the connected user or request to sign & send +transactions from the user's account. + +:::warning Wallets on gno.land + +Here is a list of available wallets for Gnoland. +Note that none of these wallets are official or exclusive, so please +use them at your own diligence: + +- [Adena Wallet](https://adena.app/) + +::: + +## Adena Wallet + +[Adena](https://adena.app/) is a web extension wallet that supports the Gnoland blockchain. Below is the basic Adena +APIs that you can use for your application. For more detailed information, check out +Adena's [developer's docs](https://docs.adena.app/) to integrate Adena to your application. + +### Adena Connect For React App + +Check if Adena wallet exists. + +```javascript +// checks the existence of the adena object in window + +const existsWallet = () => { + if (window.adena) { + return true; + } + return false; +}; + +``` + +Register your website as a trusted domain. + +```javascript +// calls the AddEstablish of the adena object + +const addEstablish = (siteName) => { + return window?.adena?.AddEstablish(siteName); +}; + +``` + +Retrieve information about the connected account. + +```javascript +// calls the GetAccount function of the adena object + +const getAccount = () => { + return window.adena?.GetAccount(); +}; + +``` + +Request approval of a transaction that transfers tokens. + +```javascript +// Execute the DoContract function of the adena object to request transaction. + +const sendToken = (fromAddress, toAddress, sendAmount) => { + const message = { + type: "/bank.MsgSend", + value: { + from_address: fromAddress, + to_address: toAddress, + amount: sendAmount + } + }; + + return window.adena?.DoContract({ + messages: [message], + gasFee: 1, + gasWanted: 3000000 + }); +}; + +``` + +Request approval of a transaction that calls a function from a realm. + +```javascript +// Execute the DoContract function of the adena object to request transaction. + +const doContractPackageFunction = (caller, func, pkgPath, argument) => { + + // Setup Transaction Message + const message = { + type: "/vm.m_call", + value: { + caller, + func, + send: "", + pkg_path: pkgPath, + args: argument.split(',') + } + }; + + // Request Transaction + return window.adena?.DoContract({ + messages: [message], + gasFee: 1, + gasWanted: 3000000 + }); +}; +``` diff --git a/docs/how-to-guides/creating-grc20.md b/docs/how-to-guides/creating-grc20.md new file mode 100644 index 00000000000..d00fd8c349e --- /dev/null +++ b/docs/how-to-guides/creating-grc20.md @@ -0,0 +1,164 @@ +--- +id: creating-grc20 +--- + +# How to create a GRC20 Token + +## Overview + +This guide shows you how to write a simple _GRC20_ Smart Contract, or rather a [Realm](../concepts/realms.md), in [Gno (Gno)](../concepts/gno-language.md). For actually deploying the Realm, please see the [deployment](deploy.md) guide. + +Our _GRC20_ Realm will have the following functionality: + +- Minting a configurable amount of token. +- Keeping track of total token supply. +- Fetching the balance of an account. + +## Prerequisites + +We will proceed using the typical directory structure for a Realm found within the [simple-contract guide](simple-contract.md). It is also worthwhile to consult the [GRC20 interface](https://github.com/gnolang/gno/blob/master/examples/gno.land/p/demo/grc/grc20/igrc20.gno) which we will be importing and utilizing within this guide. + +## 1. Importing token package +For this realm, we'll want to import the `grc20` package as this will include the main functionality of our token factory realm. + +[embedmd]:# (../assets/how-to-guides/creating-grc20/mytoken-1.gno go) +```go +package mytoken + +import ( + "std" + + "gno.land/p/demo/grc/grc20" +) + +var ( + mytoken *grc20.AdminToken + admin std.Address = "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj" // set admin account +) + +// init is a constructor function that runs only once (at time of deployment) +func init() { + // provision the token's name, symbol and number of decimals + mytoken = grc20.NewAdminToken("Mytoken", "MTKN", 4) + + // set the total supply + mytoken.Mint(admin, 1000000*10000) // @administrator (supply = 1 million) +} +``` + +In this code preview, we have: +- Defined a new local variable `mytoken` and assigned that the type of pointer to `grc20.AdminToken`. +- Defined and set the value of local variable `admin` to point to a specific gno.land address of type `std.Address`. +- Set the value of `mytoken` (type `*AdminToken`) to equal the result of creating a new token and configuring its name, symbol + decimal representation. +- Minted 1 million `Mytoken` and set the administrator as the owner of these tokens. + +## 2. Adding token functionality + +The following section will be about introducing Public functions to expose functionality imported from the [grc20 package](https://github.com/gnolang/gno/tree/master/examples/gno.land/p/demo/grc/grc20). + +[embedmd]:# (../assets/how-to-guides/creating-grc20/mytoken-2.gno go) +```go +func TotalSupply() uint64 { + return mytoken.TotalSupply() +} + +func BalanceOf(owner users.AddressOrName) uint64 { + balance, err := mytoken.BalanceOf(owner.Resolve()) + if err != nil { + panic(err) + } + return balance +} + +func Allowance(owner, spender users.AddressOrName) uint64 { + allowance, err := mytoken.Allowance(owner.Resolve(), spender.Resolve()) + if err != nil { + panic(err) + } + return allowance +} + +func Transfer(to users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + err := mytoken.Transfer(caller, to.Resolve(), amount) + if err != nil { + panic(err) + } +} + +func Approve(spender users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + err := mytoken.Approve(caller, spender.Resolve(), amount) + if err != nil { + panic(err) + } +} + +func TransferFrom(from, to users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + err := mytoken.TransferFrom(caller, from.Resolve(), to.Resolve(), amount) + if err != nil { + panic(err) + } +} + +func Mint(address users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + err := mytoken.Mint(address.Resolve(), amount) + if err != nil { + panic(err) + } +} + +func Burn(address users.AddressOrName, amount uint64) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + err := mytoken.Burn(address.Resolve(), amount) + if err != nil { + panic(err) + } +} + +func Render(path string) string { + parts := strings.Split(path, "/") + c := len(parts) + + switch { + case path == "": + return mytoken.RenderHome() + case c == 2 && parts[0] == "balance": + owner := users.AddressOrName(parts[1]) + balance, _ := mytoken.BalanceOf(owner.Resolve()) + return ufmt.Sprintf("%d\n", balance) + default: + return "404\n" + } +} + +func assertIsAdmin(address std.Address) { + if address != admin { + panic("restricted access") + } +} +``` + +Detailing what is happening in the above code: +- Calling the `TotalSupply` method would return the total number of tokens minted. +- Calling the `BalanceOf` method would return the total balance of an account. +- Calling the `Allowance` method would set an account as an allowed spender to serve on behalf of the owner. +- Calling the `transfer` method would transfer a configurable amount of token from the calling account to another account, either owned or unowned. +- Calling the `Approve` method would approve a calling account to spend a configurable amount of token on behalf of the token owner. +- Calling the `TransferFrom` method would transfer a configurable amount of token from an account that granted approval to another account, either owned or unowned. +- Calling the `Mint` method would create a configurable number of tokens by the administrator. +- Calling the `Burn` method would destroy a configurable number of tokens by the administrator. +- Calling the `Render` method would return a user's `balance` as a formatted string. Learn more about the `Render` + method and how it's used [here](../concepts/realms.md). +- Finally, we provide a local function to assert that the calling account is in fact the owner, otherwise panic. This is a very important function that serves to prevent abuse by non-administrators. + +## Conclusion + +That's it 🎉 + +You have successfully built a simple GRC20 Realm that is ready to be deployed on the Gno chain and called by users. +In the upcoming guides, we will see how we can develop more complex Realm logic and have them interact with outside tools like a wallet application. diff --git a/docs/how-to-guides/creating-grc721.md b/docs/how-to-guides/creating-grc721.md new file mode 100644 index 00000000000..f8049e8a135 --- /dev/null +++ b/docs/how-to-guides/creating-grc721.md @@ -0,0 +1,199 @@ +--- +id: creating-grc721 +--- + +# How to create a GRC721 Token (NFT) + +## Overview + +This guide shows you how to write a simple _GRC721_ Smart Contract, or rather a [Realm](../concepts/realms.md), +in [Gno (Gnolang)](../concepts/gno-language.md). For actually deploying the Realm, please see +the [deployment](deploy.md) guide. + +Our _GRC721_ Realm will have the following functionality: + +- Minting a configurable amount of token. +- Keeping track of total token supply. +- Fetching the balance of an account. + +## Prerequisites + +We will proceed using the typical directory structure for a Realm found within +the [simple-contract guide](simple-contract.md). It is also worthwhile to consult +the [GRC721 interface](https://github.com/gnolang/gno/blob/master/examples/gno.land/p/demo/grc/grc721/igrc721.gno) which we will be borrowing from within +this guide. + +## 1. Importing token package + +For this realm, we'll want to import the `grc20` package as this will include the main functionality of our token +factory realm. + +[embedmd]:# (../assets/how-to-guides/creating-grc721/mynonfungibletoken-1.gno go) +```go +package mynonfungibletoken + +import ( + "std" + + "gno.land/p/demo/grc/grc721" +) + +var ( + admin std.Address = "g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj" // set admin account + // provision the token's name and symbol + mynonfungibletoken = grc721.NewBasicNFT("mynonfungibletoken", "MNFT") +) + +func init() { + mintNNFT(admin, 10) // @administrator (supply = 10) +} +``` + +In this code preview, we have: + +- Defined and set the value of `mynonfungibletoken` (type `*grc721.basicNFT`) to equal the result of creating a new + token and configuring its name and symbol. +- Defined and set the value of local variable `admin` to point to a specific gno.land address of type `std.Address`. +- Minted 5 `mynonfungibletoken (MNFT)` and set the administrator as the owner of these tokens + +## 2. Adding token functionality + +The following section will be about introducing Public functions to expose functionality imported from +the [grc721 package](https://github.com/gnolang/gno/tree/master/examples/gno.land/p/demo/grc/grc721). + +[embedmd]:# (../assets/how-to-guides/creating-grc721/mynonfungibletoken-2.gno go) +```go +func mintNNFT(owner std.Address, n uint64) { + count := my.TokenCount() + for i := count; i < count+n; i++ { + tid := grc721.TokenID(ufmt.Sprintf("%d", i)) + mynonfungibletoken.Mint(owner, tid) + } +} + +// Getters + +func BalanceOf(user users.AddressOrName) uint64 { + balance, err := mynonfungibletoken.BalanceOf(user.Resolve()) + if err != nil { + panic(err) + } + + return balance +} + +func OwnerOf(tid grc721.TokenID) std.Address { + owner, err := mynonfungibletoken.OwnerOf(tid) + if err != nil { + panic(err) + } + + return owner +} + +func IsApprovedForAll(owner, user users.AddressOrName) bool { + return mynonfungibletoken.IsApprovedForAll(owner.Resolve(), user.Resolve()) +} + +func GetApproved(tid grc721.TokenID) std.Address { + addr, err := mynonfungibletoken.GetApproved(tid) + if err != nil { + panic(err) + } + + return addr +} + +// Setters + +func Approve(user users.AddressOrName, tid grc721.TokenID) { + err := mynonfungibletoken.Approve(user.Resolve(), tid) + if err != nil { + panic(err) + } +} + +func SetApprovalForAll(user users.AddressOrName, approved bool) { + err := mynonfungibletoken.SetApprovalForAll(user.Resolve(), approved) + if err != nil { + panic(err) + } +} + +func TransferFrom(from, to users.AddressOrName, tid grc721.TokenID) { + err := mynonfungibletoken.TransferFrom(from.Resolve(), to.Resolve(), tid) + if err != nil { + panic(err) + } +} + +// Admin + +func Mint(to users.AddressOrName, tid grc721.TokenID) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + err := mynonfungibletoken.Mint(to.Resolve(), tid) + if err != nil { + panic(err) + } +} + +func Burn(tid grc721.TokenID) { + caller := std.PrevRealm().Addr() + assertIsAdmin(caller) + err := mynonfungibletoken.Burn(tid) + if err != nil { + panic(err) + } +} + +// Render + +func Render(path string) string { + switch { + case path == "": + return mynonfungibletoken.RenderHome() + default: + return "404\n" + } +} + +// Util + +func assertIsAdmin(address std.Address) { + if address != admin { + panic("restricted access") + } +} +``` + +Detailing what is happening in the above code: + +- Calling the **local** `mintNNFT` method would mint a configurable number of tokens to the provided owner's account. +- Calling the `BalanceOf` method would return the total balance of an account. +- Calling the `OwnerOf` method would return the owner of the token based on the ID that is passed into the method. +- Calling the `IsApprovedByAll` method will return true if an operator is approved for all operations by the owner; + otherwise, returns false. +- Calling the `GetApproved` method will return the address approved to operate on the token. +- Calling the `Approve` method would approve the input address for a particular token. +- Calling the `SetApprovalForAll` method would approve an operating account to operate on all tokens. +- Calling the `TransferFrom` method would transfer a configurable amount of token from an account that granted approval + to another account, either owned or unowned. +- Calling the `Mint` method would create a configurable number of tokens by the administrator. +- Calling the `Burn` method would destroy a configurable number of tokens by the administrator. +- Calling the `Render` method on success would invoke + a [`RenderHome`](https://github.com/gnolang/gno/blob/master/examples/gno.land/p/demo/grc/grc721/basic_nft.gno#L353) + method on the `grc721` instance we instantiated at the top of the file; this method returns a formatted string that + includes the token: symbol, supply and account balances (`balances avl.Tree`) which is a mapping denoted + as: `OwnerAddress -> TokenCount`; otherwise returns false and renders a `404`; you can find more information about + this `Render` method and how it's used [here](../concepts/realms.md). +- Finally, we provide a local function to assert that the calling account is in fact the owner, otherwise panic. This is + a very important function that serves to prevent abuse by non-administrators. + +## Conclusion + +That's it 🎉 + +You have successfully built a simple GRC721 Realm that is ready to be deployed on the Gno chain and called by users. +In the upcoming guides, we will see how we can develop more complex Realm logic and have them interact with outside +tools like a wallet application. diff --git a/docs/how-to-guides/deploy.md b/docs/how-to-guides/deploy.md new file mode 100644 index 00000000000..ab31716f014 --- /dev/null +++ b/docs/how-to-guides/deploy.md @@ -0,0 +1,74 @@ +--- +id: deploy +--- + +# How to deploy a Realm / Package + +## Overview + +This guide shows you how to deploy any realm or package to the Gno chain. Deployment is be done by utilizing `gnokey`'s `maketx addpkg` API. + +:::info +Regardless of whether you're deploying a realm or a package, you will be using `gnokey`'s `maketx addpkg` - the usage of `maketx addpkg` in both cases is identical. +::: + +## Prerequisites + +- **Have `gnokey` installed** +- **Have access to a `gnoland` node (local or remote)** +- **Have generated a keypair with `gnokey` & funded it with `gnot`** +- **Have a Realm or Package ready to deploy** + +## Deploying + +To illustrate deployment, we will use a realm. Consider the following folder structure: + +``` +counter-app/ +├─ r/ +│ ├─ counter/ +│ │ ├─ counter.gno +``` + +We would like to deploy the realm found in `counter.gno`. To do this, open a terminal at `counter-app/` and use the following `gnokey` command: + +```bash +gnokey maketx addpkg \ +--pkgpath "gno.land/r/demo/counter" \ +--pkgdir "./r/counter" \ +--gas-fee 10000000ugnot \ +--gas-wanted 800000 \ +--broadcast \ +--chainid dev \ +--remote localhost:26657 \ +MyKey +``` + +Let's analyze all of the flags in detail: +- `--pkgpath` - path where the package/realm will be placed on-chain +- `--pkgdir` - local path where the package/realm is located +- `--gas-wanted` - the upper limit for units of gas for the execution of the transaction - similar to Solidity's gas limit +- `--gas-fee` - similar to Solidity's gas-price +- `--broadcast` - broadcast the transaction on-chain +- `--chain-id` - id of the chain to connect to - local or remote +- `--remote` - `gnoland` node endpoint - local or remote +- `MyKey` - the keypair to use for the transaction + +:::info +As of October 2023, `--gas-fee` is fixed to 1gnot (10000000ugnot), with plans to change it down the line. +::: + +Next, confirm the transaction with your keypair passphrase. If deployment was successful, you will be presented with a message similar to the following: + +``` +OK! +GAS WANTED: 800000 +GAS USED: 775097 +``` +Depending on the size of the package/realm, you might need to increase amount given in the `--gas-wanted` flag to cover the deployment cost. + +## Conclusion + +That's it 🎉 + +You have now successfully deployed a realm/package to a Gno.land chain. diff --git a/docs/how-to-guides/interact-with-gnoland.md b/docs/how-to-guides/interact-with-gnoland.md new file mode 100644 index 00000000000..dacfead2957 --- /dev/null +++ b/docs/how-to-guides/interact-with-gnoland.md @@ -0,0 +1,94 @@ +--- +id: interact-with-gnoland +--- + +# Interact with Gno.land + +This tutorial will teach you how to interact with the gno.land blockchain by creating an account and calling various realms to send transactions on the network. + +## Prerequisites + +- [Installation](../getting-started/local-setup.md) + +## Create an Account + +In order to interact with Gnoland, you need an account that you will use to sign and send transactions. You may create a new account with `gnokey generate` or recover an existing one with `gnokey add`. Confirm that your account was successfully added with `gnokey list` to display all accounts registered in the key base of your device. + +```bash +gnokey generate # create a new seed phrase (mnemonic) + +gnokey add -recover {your_account_name} # registers a key with the name set as the value you put in {your_account_name} with a seed phrase + +gnokey list # check the list of keys +``` + +## Register As a User + +```bash +gnokey maketx call \ + -gas-fee="1ugnot" \ + -gas-wanted="5000000" \ + -broadcast="true" \ + -remote="staging.gno.land:36657" \ + -chainid="test3" \ + -pkgpath="gno.land/r/demo/users" \ + -func="Register" \ + -args="" \ + -args="my_account" \ # (must be at least 6 characters, lowercase alphanumeric with underscore) + -args="" \ + -send="200000000ugnot" \ + my-account + +# username: must be at least 6 characters, lowercase alphanumeric with underscore +``` + +> **Note:** With a user registration fee of 200 GNOT and a gas fee that ranges up to 2 GNOT, you must have around 202 GNOT to complete this transaction. After registering as a user, you may replace your address with your `username` when developing or publishing a realm package. + +## Get Account Information + +```bash +# Get account information +gnokey query -remote="staging.gno.land:36657" "auth/accounts/{address}" + +# Get account balance +gnokey query -remote="staging.gno.land:36657" "bank/balances/{address}" + +# Get /r/demo/boards user information +gnokey query -remote="staging.gno.land:36657" -data "gno.land/r/demo/users +my_account" "vm/qrender" +``` + +## Send Tokens + +The following command will send 1,000,000 ugnot (= 1 GNOT) to the address specified in the `to` argument. + +```bash +# Creates and broadcast a token transfer transaction +gnokey maketx send \ + -gas-fee="1ugnot" \ + -gas-wanted="5000000" \ + -broadcast="true" \ + -remote="staging.gno.land:36657" \ + -chainid="test3" \ + -to="{address}" \ # g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5 + -send="{amount}{denom}" \ # 1234ugnot + my-account +``` + +## Create a Board + +Try creating a board called `my_board` on the `gno.land/r/demo/boards` realm with the following command: + +```bash +# Calls the CreateBoard function of gno.land/r/demo/boards +gnokey maketx call \ + -gas-fee="1ugnot" \ + -gas-wanted="5000000" \ + -broadcast="true" \ + -remote "staging.gno.land:36657" \ + -chainid="test3" \ + -pkgpath="gno.land/r/demo/boards" \ + -func="CreateBoard" \ + -args="my_board" \ + my-account +``` diff --git a/docs/how-to-guides/porting-solidity-to-gno.md b/docs/how-to-guides/porting-solidity-to-gno.md new file mode 100644 index 00000000000..85c426c4c83 --- /dev/null +++ b/docs/how-to-guides/porting-solidity-to-gno.md @@ -0,0 +1,680 @@ +--- +id: port-solidity-to-gno +--- + +# Port a Solidity Contract to a Gno Realm + + +## Overview + +This guide shows you how to port a Solidity contract `Simple Auction` to a Gno Realm `auction.gno` with test cases (Test Driven Development (TDD) approach). + +You can check the Solidity contract in this [link](https://docs.soliditylang.org/en/latest/solidity-by-example.html#simple-open-auction), and here's the code for porting. + +```solidity +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.4; +contract SimpleAuction { + // Parameters of the auction. Times are either + // absolute unix timestamps (seconds since 1970-01-01) + // or time periods in seconds. + address payable public beneficiary; + uint public auctionEndTime; + + // Current state of the auction. + address public highestBidder; + uint public highestBid; + + // Allowed withdrawals of previous bids + mapping(address => uint) pendingReturns; + + // Set to true at the end, disallows any change. + // By default initialized to `false`. + bool ended; + + // Events that will be emitted on changes. + event HighestBidIncreased(address bidder, uint amount); + event AuctionEnded(address winner, uint amount); + + // Errors that describe failures. + + // The triple-slash comments are so-called natspec + // comments. They will be shown when the user + // is asked to confirm a transaction or + // when an error is displayed. + + /// The auction has already ended. + error AuctionAlreadyEnded(); + /// There is already a higher or equal bid. + error BidNotHighEnough(uint highestBid); + /// The auction has not ended yet. + error AuctionNotYetEnded(); + /// The function auctionEnd has already been called. + error AuctionEndAlreadyCalled(); + + /// Create a simple auction with `biddingTime` + /// seconds bidding time on behalf of the + /// beneficiary address `beneficiaryAddress`. + constructor( + uint biddingTime, + address payable beneficiaryAddress + ) { + beneficiary = beneficiaryAddress; + auctionEndTime = block.timestamp + biddingTime; + } + + /// Bid on the auction with the value sent + /// together with this transaction. + /// The value will only be refunded if the + /// auction is not won. + function bid() external payable { + // No arguments are necessary, all + // information is already part of + // the transaction. The keyword payable + // is required for the function to + // be able to receive Ether. + + // Revert the call if the bidding + // period is over. + if (block.timestamp > auctionEndTime) + revert AuctionAlreadyEnded(); + + // If the bid is not higher, send the + // money back (the revert statement + // will revert all changes in this + // function execution including + // it having received the money). + if (msg.value <= highestBid) + revert BidNotHighEnough(highestBid); + + if (highestBid != 0) { + // Sending back the money by simply using + // highestBidder.send(highestBid) is a security risk + // because it could execute an untrusted contract. + // It is always safer to let the recipients + // withdraw their money themselves. + pendingReturns[highestBidder] += highestBid; + } + highestBidder = msg.sender; + highestBid = msg.value; + emit HighestBidIncreased(msg.sender, msg.value); + } + + /// Withdraw a bid that was overbid. + function withdraw() external returns (bool) { + uint amount = pendingReturns[msg.sender]; + if (amount > 0) { + // It is important to set this to zero because the recipient + // can call this function again as part of the receiving call + // before `send` returns. + pendingReturns[msg.sender] = 0; + + // msg.sender is not of type `address payable` and must be + // explicitly converted using `payable(msg.sender)` in order + // use the member function `send()`. + if (!payable(msg.sender).send(amount)) { + // No need to call throw here, just reset the amount owing + pendingReturns[msg.sender] = amount; + return false; + } + } + return true; + } + + /// End the auction and send the highest bid + /// to the beneficiary. + function auctionEnd() external { + // It is a good guideline to structure functions that interact + // with other contracts (i.e. they call functions or send Ether) + // into three phases: + // 1. checking conditions + // 2. performing actions (potentially changing conditions) + // 3. interacting with other contracts + // If these phases are mixed up, the other contract could call + // back into the current contract and modify the state or cause + // effects (ether payout) to be performed multiple times. + // If functions called internally include interaction with external + // contracts, they also have to be considered interaction with + // external contracts. + + // 1. Conditions + if (block.timestamp < auctionEndTime) + revert AuctionNotYetEnded(); + if (ended) + revert AuctionEndAlreadyCalled(); + + // 2. Effects + ended = true; + emit AuctionEnded(highestBidder, highestBid); + + // 3. Interaction + beneficiary.transfer(highestBid); + } +} +``` + +These are the basic concepts of the Simple Auction contract: + +* Everyone can send their bids during a bidding period. +* The bids already include sending money / Ether in order to bind the bidders to their bids. +* If the highest bid is raised, the previous highest bidder gets their money back. +* After the end of the bidding period, the contract has to be called manually for the beneficiary to receive their money - contracts cannot activate themselves. + +The contract consists of: + +* A variable declaration +* Initialization by a constructor +* Three functions + +Let's dive into the details of the role of each function, and learn how to port each function into Gno with test cases. + +When writing a test case, the following conditions are often used to determine whether the function has been properly executed: + +* Value matching +* Error status +* Panic status + +Below is a test case helper that will help implement each condition. + +### Gno - Testcase Helper + +[embedmd]:# (../assets/how-to-guides/porting-solidity-to-gno/porting-1.gno go) +```go +func shouldEqual(t *testing.T, got interface{}, expected interface{}) { + t.Helper() + + if got != expected { + t.Errorf("expected %v(%T), got %v(%T)", expected, expected, got, got) + } +} + +func shouldErr(t *testing.T, err error) { + t.Helper() + if err == nil { + t.Errorf("expected an error, but got nil.") + } +} + +func shouldNoErr(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Errorf("expected no error, but got err: %s.", err.Error()) + } +} + +func shouldPanic(t *testing.T, f func()) { + defer func() { + if r := recover(); r == nil { + t.Errorf("should have panic") + } + }() + f() +} + +func shouldNoPanic(t *testing.T, f func()) { + defer func() { + if r := recover(); r != nil { + t.Errorf("should not have panic") + } + }() + f() +} +``` + +## Variable init - Solidity + +[embedmd]:# (../assets/how-to-guides/porting-solidity-to-gno/porting-2.sol solidity) +```solidity +// Parameters of the auction. Times are either +// absolute unix timestamps (seconds since 1970-01-01) +// or time periods in seconds. +address payable public beneficiary; +uint public auctionEndTime; + +// Current state of the auction. +address public highestBidder; +uint public highestBid; + +// Allowed withdrawals of previous bids +mapping(address => uint) pendingReturns; + +// Set to true at the end, disallows any change. +// By default initialized to `false`. +bool ended; + +// Events that will be emitted on changes. +event HighestBidIncreased(address bidder, uint amount); +event AuctionEnded(address winner, uint amount); + +// Errors that describe failures. + +// The triple-slash comments are so-called natspec +// comments. They will be shown when the user +// is asked to confirm a transaction or +// when an error is displayed. + +/// The auction has already ended. +error AuctionAlreadyEnded(); +/// There is already a higher or equal bid. +error BidNotHighEnough(uint highestBid); +/// The auction has not ended yet. +error AuctionNotYetEnded(); +/// The function auctionEnd has already been called. +error AuctionEndAlreadyCalled(); + +/// Create a simple auction with `biddingTime` +/// seconds bidding time on behalf of the +/// beneficiary address `beneficiaryAddress`. +constructor( + uint biddingTime, + address payable beneficiaryAddress +) { + beneficiary = beneficiaryAddress; + auctionEndTime = block.timestamp + biddingTime; +} +``` + +* `address payable public beneficiary;` : Address to receive the amount after the auction's ending. +* `uint public auctionEndTime;` : Auction ending time. +* `address public highestBidder;` : The highest bidder. +* `uint public highestBid;` : The highest bid. +* `mapping(address => uint) pendingReturns;` : Bidder's address and amount to be returned (in case of the highest bid changes). +* `bool ended;` : Whether the auction is closed. + +### Variable init - Gno + +[embedmd]:# (../assets/how-to-guides/porting-solidity-to-gno/porting-3.gno go) +```go +var ( + receiver = std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + auctionEndBlock = std.GetHeight() + uint(300) // in blocks + highestBidder std.Address + highestBid = uint(0) + pendingReturns avl.Tree + ended = false +) +``` + +> **Note:** In Solidity, the Auction ending time is set by a time basis, but in the above case, it's set by a block basis. + +### + +## bid() - Solidity + +[embedmd]:# (../assets/how-to-guides/porting-solidity-to-gno/porting-4.sol solidity) +```solidity +function bid() external payable { + // No arguments are necessary, all + // information is already part of + // the transaction. The keyword payable + // is required for the function to + // be able to receive Ether. + + // Revert the call if the bidding + // period is over. + if (block.timestamp > auctionEndTime) + revert AuctionAlreadyEnded(); + + // If the bid is not higher, send the + // money back (the revert statement + // will revert all changes in this + // function execution including + // it having received the money). + if (msg.value <= highestBid) + revert BidNotHighEnough(highestBid); + + if (highestBid != 0) { + // Sending back the money by simply using + // highestBidder.send(highestBid) is a security risk + // because it could execute an untrusted contract. + // It is always safer to let the recipients + // withdraw their money themselves. + pendingReturns[highestBidder] += highestBid; + } + highestBidder = msg.sender; + highestBid = msg.value; + emit HighestBidIncreased(msg.sender, msg.value); +} +``` + +`bid()` function is for participating in an auction and includes: + +* Determining whether an auction is closed. +* Comparing a new bid with the current highest bid. +* Prepare data to return the bid amount to the existing highest bidder in case of the highest bid is increased. +* Update variables with the top bidder & top bid amount. + +### bid() - Gno + +[embedmd]:# (../assets/how-to-guides/porting-solidity-to-gno/porting-5.gno go) +```go +func Bid() { + if std.GetHeight() > auctionEndBlock { + panic("Exceeded auction end block") + } + + sentCoins := std.GetOrigSend() + if len(sentCoins) != 1 { + panic("Send only one type of coin") + } + + sentAmount := uint(sentCoins[0].Amount) + if sentAmount <= highestBid { + panic("Too few coins sent") + } + + // A new bid is higher than the current highest bid + if sentAmount > highestBid { + // If the highest bid is greater than 0, + if highestBid > 0 { + // Need to return the bid amount to the existing highest bidder + // Create an AVL tree and save + pendingReturns.Set(highestBidder.String(), highestBid) + } + + // Update the top bidder address + highestBidder = std.GetOrigCaller() + // Update the top bid amount + highestBid = sentAmount + } +} +``` + +### bid() - Gno Testcase + +[embedmd]:# (../assets/how-to-guides/porting-solidity-to-gno/porting-6.gno go) +```go +// Bid Function Test - Send Coin +func TestBidCoins(t *testing.T) { + // Sending two types of coins + std.TestSetOrigCaller(bidder01) + std.TestSetOrigSend(std.Coins{{"ugnot", 0}, {"test", 1}}, nil) + shouldPanic(t, Bid) + + // Sending lower amount than the current highest bid + std.TestSetOrigCaller(bidder01) + std.TestSetOrigSend(std.Coins{{"ugnot", 0}}, nil) + shouldPanic(t, Bid) + + // Sending more amount than the current highest bid (exceeded) + std.TestSetOrigCaller(bidder01) + std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) + shouldNoPanic(t, Bid) +} + +// Bid Function Test - Bid by two or more people +func TestBidCoins(t *testing.T) { + // bidder01 bidding with 1 coin + std.TestSetOrigCaller(bidder01) + std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) + shouldNoPanic(t, Bid) + shouldEqual(t, highestBid, 1) + shouldEqual(t, highestBidder, bidder01) + shouldEqual(t, pendingReturns.Size(), 0) + + // bidder02 bidding with 1 coin + std.TestSetOrigCaller(bidder02) + std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) + shouldPanic(t, Bid) + + // bidder02 bidding with 2 coins + std.TestSetOrigCaller(bidder02) + std.TestSetOrigSend(std.Coins{{"ugnot", 2}}, nil) + shouldNoPanic(t, Bid) + shouldEqual(t, highestBid, 2) + shouldEqual(t, highestBidder, bidder02) + shouldEqual(t, pendingReturns.Size(), 1) +} +``` + +### + +## withdraw() - Solidity + +[embedmd]:# (../assets/how-to-guides/porting-solidity-to-gno/porting-7.sol solidity) +```solidity +/// Withdraw a bid that was overbid. +function withdraw() external returns (bool) { + uint amount = pendingReturns[msg.sender]; + if (amount > 0) { + // It is important to set this to zero because the recipient + // can call this function again as part of the receiving call + // before `send` returns. + pendingReturns[msg.sender] = 0; + + // msg.sender is not of type `address payable` and must be + // explicitly converted using `payable(msg.sender)` in order + // use the member function `send()`. + if (!payable(msg.sender).send(amount)) { + // No need to call throw here, just reset the amount owing + pendingReturns[msg.sender] = amount; + return false; + } + } + return true; +} +``` + +`withdraw()` is to return the bid amount to the existing highest bidder in case of the highest bid changes and includes: + +* When called, determine if there's a bid amount to be returned to the address. +* (If there's an amount to be returned) Before returning, set the previously recorded amount to `0` and return the actual amount. + +### withdraw() - Gno + +[embedmd]:# (../assets/how-to-guides/porting-solidity-to-gno/porting-8.gno go) +```go +func Withdraw() { + // Query the return amount to non-highest bidders + amount, _ := pendingReturns.Get(std.GetOrigCaller().String()) + + if amount > 0 { + // If there's an amount, reset the amount first, + pendingReturns.Set(std.GetOrigCaller().String(), 0) + + // Return the exceeded amount + banker := std.GetBanker(std.BankerTypeRealmSend) + pkgAddr := std.GetOrigPkgAddr() + + banker.SendCoins(pkgAddr, std.GetOrigCaller(), std.Coins{{"ugnot", amount.(int64)}}) + } +} +``` + +### + +### withdraw() - Gno Testcase + +[embedmd]:# (../assets/how-to-guides/porting-solidity-to-gno/porting-9.gno go) +```go +// Withdraw Function Test +func TestWithdraw(t *testing.T) { + // If there's no participants for return + shouldEqual(t, pendingReturns.Size(), 0) + + // If there's participants for return (data generation + returnAddr := bidder01.String() + returnAmount := int64(3) + pendingReturns.Set(returnAddr, returnAmount) + shouldEqual(t, pendingReturns.Size(), 1) + shouldEqual(t, pendingReturns.Has(returnAddr), true) + + banker := std.GetBanker(std.BankerTypeRealmSend) + pkgAddr := std.GetOrigPkgAddr() + banker.SendCoins(pkgAddr, std.Address(returnAddr), std.Coins{{"ugnot", returnAmount}}) + shouldEqual(t, banker.GetCoins(std.Address(returnAddr)).String(), "3ugnot") +} +``` + +## auctionEnd() - Solidity + +[embedmd]:# (../assets/how-to-guides/porting-solidity-to-gno/porting-10.sol solidity) +```solidity +/// End the auction and send the highest bid +/// to the beneficiary. +function auctionEnd() external { + // It is a good guideline to structure functions that interact + // with other contracts (i.e. they call functions or send Ether) + // into three phases: + // 1. checking conditions + // 2. performing actions (potentially changing conditions) + // 3. interacting with other contracts + // If these phases are mixed up, the other contract could call + // back into the current contract and modify the state or cause + // effects (ether payout) to be performed multiple times. + // If functions called internally include interaction with external + // contracts, they also have to be considered interaction with + // external contracts. + + // 1. Conditions + if (block.timestamp < auctionEndTime) + revert AuctionNotYetEnded(); + if (ended) + revert AuctionEndAlreadyCalled(); + + // 2. Effects + ended = true; + emit AuctionEnded(highestBidder, highestBid); + + // 3. Interaction + beneficiary.transfer(highestBid); +} +``` + +`auctionEnd()` function is for ending the auction and includes: + +* Determines if the auction should end by comparing the end time. +* Determines if the auction has already ended or not. + * (If not ended) End the auction. + * (If not ended) Send the highest bid amount to the recipient. + +### auctionEnd() - Gno + +[embedmd]:# (../assets/how-to-guides/porting-solidity-to-gno/porting-11.gno go) +```go +func AuctionEnd() { + if std.GetHeight() < auctionEndBlock { + panic("Auction hasn't ended") + } + + if ended { + panic("Auction has ended") + + } + ended = true + + // Send the highest bid to the recipient + banker := std.GetBanker(std.BankerTypeRealmSend) + pkgAddr := std.GetOrigPkgAddr() + + banker.SendCoins(pkgAddr, receiver, std.Coins{{"ugnot", int64(highestBid)}}) +} +``` + +### auctionEnd() - Gno Testcase + +[embedmd]:# (../assets/how-to-guides/porting-solidity-to-gno/porting-12.gno go) +```go +// AuctionEnd() Function Test +func TestAuctionEnd(t *testing.T) { + // Auction is ongoing + shouldPanic(t, AuctionEnd) + + // Auction ends + highestBid = 3 + std.TestSkipHeights(500) + shouldNoPanic(t, AuctionEnd) + shouldEqual(t, ended, true) + + banker := std.GetBanker(std.BankerTypeRealmSend) + shouldEqual(t, banker.GetCoins(receiver).String(), "3ugnot") + + // Auction has already ended + shouldPanic(t, AuctionEnd) + shouldEqual(t, ended, true) +} +``` + +## Precautions for Running Test Cases + +* Each test function should be executed separately one by one, to return all passes without any errors. +* Same as Go, Gno doesn't support `setup()` & `teardown()` functions. So running two or more test functions simultaneously can result in tainted data. +* If you want to do the whole test at once, make it into a single function as below: + +[embedmd]:# (../assets/how-to-guides/porting-solidity-to-gno/porting-13.gno go) +```go +// The whole test +func TestFull(t *testing.T) { + bidder01 := testutils.TestAddress("bidder01") // g1vf5kger9wgcrzh6lta047h6lta047h6lufftkw + bidder02 := testutils.TestAddress("bidder02") // g1vf5kger9wgcryh6lta047h6lta047h6lnhe2x2 + + // Variables test + { + shouldEqual(t, highestBidder, "") + shouldEqual(t, receiver, "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + shouldEqual(t, auctionEndBlock, 423) + shouldEqual(t, highestBid, 0) + shouldEqual(t, pendingReturns.Size(), 0) + shouldEqual(t, ended, false) + } + + // Send two or more types of coins + { + std.TestSetOrigCaller(bidder01) + std.TestSetOrigSend(std.Coins{{"ugnot", 0}, {"test", 1}}, nil) + shouldPanic(t, Bid) + } + + // Send less than the highest bid + { + std.TestSetOrigCaller(bidder01) + std.TestSetOrigSend(std.Coins{{"ugnot", 0}}, nil) + shouldPanic(t, Bid) + } + + // Send more than the highest bid + { + std.TestSetOrigCaller(bidder01) + std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) + shouldNoPanic(t, Bid) + + shouldEqual(t, pendingReturns.Size(), 0) + shouldEqual(t, highestBid, 1) + shouldEqual(t, highestBidder, "g1vf5kger9wgcrzh6lta047h6lta047h6lufftkw") + } + + // Other participants in the auction + { + + // Send less amount than the current highest bid (current: 1) + std.TestSetOrigCaller(bidder02) + std.TestSetOrigSend(std.Coins{{"ugnot", 1}}, nil) + shouldPanic(t, Bid) + + // Send more amount than the current highest bid (exceeded) + std.TestSetOrigCaller(bidder02) + std.TestSetOrigSend(std.Coins{{"ugnot", 2}}, nil) + shouldNoPanic(t, Bid) + + shouldEqual(t, highestBid, 2) + shouldEqual(t, highestBidder, "g1vf5kger9wgcryh6lta047h6lta047h6lnhe2x2") + + shouldEqual(t, pendingReturns.Size(), 1) // Return to the existing bidder + shouldEqual(t, pendingReturns.Has("g1vf5kger9wgcrzh6lta047h6lta047h6lufftkw"), true) + } + + // Auction ends + { + std.TestSkipHeights(150) + shouldPanic(t, AuctionEnd) + shouldEqual(t, ended, false) + + std.TestSkipHeights(301) + shouldNoPanic(t, AuctionEnd) + shouldEqual(t, ended, true) + + banker := std.GetBanker(std.BankerTypeRealmSend) + shouldEqual(t, banker.GetCoins(receiver).String(), "2ugnot") + } +} +``` diff --git a/docs/how-to-guides/simple-contract.md b/docs/how-to-guides/simple-contract.md new file mode 100644 index 00000000000..88c7bce7a69 --- /dev/null +++ b/docs/how-to-guides/simple-contract.md @@ -0,0 +1,151 @@ +--- +id: simple-contract +--- + +# How to write a simple Gno Smart Contract (Realm) + +## Overview + +This guide shows you how to write a simple _Counter_ Smart Contract, or rather a [Realm](../concepts/realms.md), +in [Gno (Gnolang)](../concepts/gno-language.md). For actually deploying the Realm, please see +the [deployment](deploy.md) guide. + +Our _Counter_ Realm will have the following functionality: + +- Keeping track of the current count. +- Incrementing / decrementing the count. +- Fetching the current count value. + +## Prerequisites + +- **Text editor** + +:::info Editor support +The Gno language is based on Go, but it does not have all the bells and whistles in major text editors like Go. +Advanced language features like IntelliSense are still in the works. + +Currently, we officially have language support +for [ViM](https://github.com/gnolang/gno/blob/master/CONTRIBUTING.md#vim-support), +[Emacs](https://github.com/gnolang/gno/blob/master/CONTRIBUTING.md#emacs-support) +and [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=harry-hov.gno). +::: + +## 1. Setting up the work directory + +Gno Realms can be typically written anywhere, under any structure, just like regular Go code. +However, Gno developers have adopted a standard of organizing Gno logic under a specific directory hierarchy, which we +will explore here. + +Create the main working directory for our Realm: + +```bash +mkdir counter-app +``` + +Since we are building a simple _Counter_ Realm, inside our created `counter-app` directory, we can create another +directory named `r`, which stands for `realm`: + +```bash +cd counter-app +mkdir r +``` + +Alternatively, if we were writing a [Gno Package](../concepts/packages.md), we would denote this directory name +as `p` (for `package`). You can learn more about Packages in our [Package development guide](simple-library.md). + +Additionally, we will create another sub-folder that will house our Realm code, named `counter`: + +```bash +cd r +mkdir counter +``` + +After setting up our work directory structure, we should have something like this: + +```text +counter-app/ +├─ r/ +│ ├─ counter/ +│ │ ├─ // source code here +``` + +## 2. Create `counter.gno` + +Now that the work directory structure is set up, we can go into the `counter` sub-folder, and actually create +our _Counter_ Smart Contract: + +```bash +cd counter +touch counter.gno +``` + +:::info Gno file extension +All Gno (Gnolang) source code has the file extension `.gno`. + +This file extension is required for existing gno tools and processes to work. +::: + +We can finally write out the logic of the _Counter_ Smart Contract in `counter.gno`: + +[embedmd]:# (../assets/how-to-guides/simple-contract/counter.gno go) +```go +package counter + +import ( + "gno.land/p/demo/ufmt" +) + +var count int + +func Increment() { + count++ +} + +func Decrement() { + count-- +} + +func Render(_ string) string { + return ufmt.Sprintf("Count: %d", count) +} +``` + +There are a few things happening here, so let's dissect them: + +- We defined the logic of our Realm into a package called `counter`. +- The package-level `count` variable stores the active count for the Realm (it is stateful). +- `Increment` and `Decrement` are public Realm (Smart Contract) methods, and as such are callable by users. +- `Increment` and `Decrement` directly modify the `count` value by making it go up or down (change state). +- Calling the `Render` method would return the `count` value as a formatted string. Learn more about the `Render` + method and how it's used [here](../concepts/realms.md). + +:::info A note on constructors +Gno Realms support a concept taken from other programming languages - _constructors_. + +For example, to initialize the `count` variable with custom logic, we can specify that +logic within an `init` method, that is run **only once** on Realm deployment: + +[embedmd]:# (../assets/how-to-guides/simple-contract/init.gno go) +```go +package counter + +var count int + +// ... + +func init() { + count = 2 * 10 // arbitrary value +} + +// ... +``` + +::: + +## Conclusion + +That's it 🎉 + +You have successfully built a simple _Counter_ Realm that is ready to be deployed on the Gno chain and called by users. +In the upcoming guides, we will see how we can develop more complex Realm logic and have them interact +with outside tools like a wallet application. diff --git a/docs/how-to-guides/simple-library.md b/docs/how-to-guides/simple-library.md new file mode 100644 index 00000000000..3c22afacb5d --- /dev/null +++ b/docs/how-to-guides/simple-library.md @@ -0,0 +1,133 @@ +--- +id: simple-library +--- + +# How to write a simple Gno Library (Package) + +## Overview + +This guide shows you how to write a simple library (Package) in Gno, which can be used by other Packages and Realms. +Packages are _stateless_, meaning they do not hold state like regular Realms (Smart Contracts). To learn more about the +intricacies of Packages, please see the [Packages reference](../concepts/packages.md). + +The Package we will be writing today will be a simple library for suggesting a random tapas dish. +We will define a set list of tapas, and define a method that randomly selects a dish from the list. + +## Prerequisites + +- **Text editor** + +:::info Editor support +The Gno language is based on Go, but it does not have all the bells and whistles in major text editors like Go. +Advanced language features like IntelliSense are still in the works. + +Currently, we officially have language support +for [ViM](https://github.com/gnolang/gno/blob/master/CONTRIBUTING.md#vim-support), +[Emacs](https://github.com/gnolang/gno/blob/master/CONTRIBUTING.md#emacs-support) +and [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=harry-hov.gno). +::: + +## 1. Setting up the work directory + +We discussed Gno folder structures more in detail in +the [simple Smart Contract guide](simple-contract.md#1-setting-up-the-work-directory). +For now, we will just follow some rules outlined there. + +Create the main working directory for our Package: + +```bash +mkdir tapas-lib +``` + +Since we are building a simple tapas Package, inside our created `tapas-lib` directory, we can create another +directory named `p`, which stands for `package`: + +```bash +cd tapas-lib +mkdir p +``` + +Additionally, we will create another subdirectory that will house our Package code, named `tapas`: + +```bash +cd p +mkdir tapas +``` + +After setting up our work directory structure, we should have something like this: + +```text +tapas-lib/ +├─ p/ +│ ├─ tapas/ +│ │ ├─ // source code here +``` + +## 2. Create `tapas.gno` + +Now that the work directory structure is set up, we can go into the `tapas` sub-folder, and actually create +our tapas suggestion library logic: + +```bash +cd tapas +touch tapas.gno +``` + +Inside `tapas.gno`, we will define our library logic: + +[embedmd]:# (../assets/how-to-guides/simple-library/tapas.gno go) +```go +package tapas + +import ( + "gno.land/p/demo/rand" +) + +// List of tapas suggestions +var listOfTapas = []string{ + "Patatas Bravas", + "Gambas al Ajillo", + "Croquetas", + "Tortilla Española", + "Pimientos de Padrón", + "Jamon Serrano", + "Boquerones en Vinagre", + "Calamares a la Romana", + "Pulpo a la Gallega", + "Tostada con Tomate", + "Mejillones en Escabeche", + "Chorizo a la Sidra", + "Cazón en Adobo", + "Banderillas", + "Espárragos a la Parrilla", + "Huevos Rellenos", + "Tuna Empanada", + "Sardinas a la Plancha", +} + +// GetTapaSuggestion randomly selects and returns a tapa suggestion +func GetTapaSuggestion() string { + // Create a new instance of the random number generator. + // Notice that this is from an imported Gno library + generator := rand.New() + + // Generate a random index + randomIndex := generator.Intn(len(listOfTapas)) + + // Return the random suggestion + return listOfTapas[randomIndex] +} +``` + +There are a few things happening here, so let's dissect them: + +- We defined the logic of our library into a package called `tapas`. +- The package imports another gno package, which is deployed at `gno.land/p/demo/rand` +- We use the imported package inside of `GetTapaSuggestion` to generate a random index value for a tapa + +## Conclusion + +That's it 🎉 + +You have successfully built a simple tapas suggestion Package that is ready to be deployed on the Gno chain and imported +by other Packages and Realms. diff --git a/docs/how-to-guides/testing-gno.md b/docs/how-to-guides/testing-gno.md new file mode 100644 index 00000000000..35e4bf77441 --- /dev/null +++ b/docs/how-to-guides/testing-gno.md @@ -0,0 +1,187 @@ +--- +id: testing-gno +--- + +# How to test Gno Code + +## Overview + +In this guide, we will explore the available tooling in testing out the Gno Realms and Packages we write. +We will go over different CLI tools available to developers, gno testing libraries as well as +testing techniques that involve data mocking. + +## Prerequisites + +- **`gno` set up. Reference the [Installation](../getting-started/local-setup.md#3-installing-other-gno-tools) guide + for steps** + +## Example Realm + +For the purpose of this guide, we will be testing the simple *Counter* Realm created in +the [How to write a simple Gno Smart Contract (Realm)](simple-contract.md) guide. + +[embedmd]:# (../assets/how-to-guides/testing-gno/counter-1.gno go) +```go +// counter-app/r/counter/counter.gno + +package counter + +import ( + "gno.land/p/demo/ufmt" +) + +var count int + +func Increment() { + count++ +} + +func Decrement() { + count-- +} + +func Render(_ string) string { + return ufmt.Sprintf("Count: %d", count) +} +``` + +## 1. Writing the Gno test + +Gno tests are written in the same manner and format as regular Go tests, just in `_test.gno` files. + +We can place the Gno tests for the `Counter` Realm in the same directory as `counter.gno`: + +```text +counter-app/ +├─ r/ +│ ├─ counter/ +│ │ ├─ counter.gno +│ │ ├─ counter_test.gno <--- the test source code +``` + +```bash +cd counter +touch counter_test.gno +``` + +What should be tested in this _Counter_ Realm example? +Mainly, we want to verify that: + +- Increment increments the value. +- Decrement decrements the value. +- Render returns a valid formatted value. + +Let's write the required unit tests: + +[embedmd]:# (../assets/how-to-guides/testing-gno/counter-2.gno go) +```go +// counter-app/r/counter/counter_test.gno + +package counter + +import "testing" + +func TestCounter_Increment(t *testing.T) { + // Reset the value + count = 0 + + // Verify the initial value is 0 + if count != 0 { + t.Fatalf("initial value != 0") + } + + // Increment the value + Increment() + + // Verify the initial value is 1 + if count != 1 { + t.Fatalf("initial value != 1") + } +} + +func TestCounter_Decrement(t *testing.T) { + // Reset the value + count = 0 + + // Verify the initial value is 0 + if count != 0 { + t.Fatalf("initial value != 0") + } + + // Decrement the value + Decrement() + + // Verify the initial value is 1 + if count != -1 { + t.Fatalf("initial value != -1") + } +} + +func TestCounter_Render(t *testing.T) { + // Reset the value + count = 0 + + // Verify the Render output + if Render("") != "Count: 0" { + t.Fatalf("invalid Render value") + } +} +``` + +:::warning Testing package-level variables + +In practice, it is not advisable to test and validate package level variables like this, as their value is mutated +between test runs. For the sake of keeping this guide simple, we went ahead and reset the variable value for each test, +however, +you should employ more robust test strategies. + +::: + +## 2. Running the Gno test + +To run the prepared Gno tests, we can utilize the `gno test` CLI tool. + +Simply point it to the location containing our testing source code, and the tests will execute. +For example, we can run the following command from the `counter-app/r/counter` directory: + +```bash +gno test -verbose -root-dir /Users/zmilos/Work/gno . +``` + +Let's look into the different parts of this command: + +- `-verbose` enables the verbose output. +- `-root-dir` specifies the root directory to our cloned `gno` GitHub repository +- `.` specifies the location containing our test files. Since we are already located in that directory, we specify + a `.`. + +Running the test command should produce a successful output: + +```bash +=== RUN TestCounter_Increment +--- PASS: TestCounter_Increment (0.00s) +=== RUN TestCounter_Decrement +--- PASS: TestCounter_Decrement (0.00s) +=== RUN TestCounter_Render +--- PASS: TestCounter_Render (0.00s) +ok ./. 1.00s +``` + +## Additional test support + +As we grow more familiar with Gno development, our Realm / Package logic can become more complex. As such, we need +more robust testing support in the form of mocking values ahead of time that would normally be only available on a +live (deployed) Realm / Package. + +Luckily, the Gno standard library provides ample support for functionality such as setting predefined values ahead of +time, such as the request caller address, or the calling package address. + +You can learn more about these methods, that are importable using the `std` import declaration, +in the [Standard Library](../concepts/standard-library/overview.md) reference section. + +## Conclusion + +That's it 🎉 + +You have successfully written and tested Gno code. Additionally, you have utilized the `gno test` tool, and understood +how it can be configured to make the developer experience smooth. diff --git a/docs/how-to-guides/write-simple-dapp.md b/docs/how-to-guides/write-simple-dapp.md new file mode 100644 index 00000000000..4c5c5fce032 --- /dev/null +++ b/docs/how-to-guides/write-simple-dapp.md @@ -0,0 +1,301 @@ +--- +id: write-simple-dapp +--- + +# How to write a simple dApp on Gno.land + +## Overview + +This guide will show you how to write a complete dApp that combines both a package and a realm. +Our app will allow any user to create a poll, and subsequently vote +YAY or NAY for any poll that has not exceeded the voting deadline. + +## Prerequisites + +- **Text editor** + +## Defining dApp functionality + +Our dApp will consist of a Poll package, which will handle all things related to the Poll struct, +and a Poll Factory realm, which will handle the user-facing functionality and rendering. + +For simplicity, we will define the functionality in plain text, and leave comments explaining the code. + +### Poll Package + +- Defines a `Poll` struct +- Defines a `NewPoll` constructor +- Defines `Poll` field getters +- Defines a `Vote` function +- Defines a `HasVoted` check method +- Defines a `VoteCount` getter method + +[embedmd]:# (../assets/how-to-guides/write-simple-dapp/poll-1.gno go) +```go +package poll + +import ( + "std" + + "gno.land/p/demo/avl" +) + +// Main struct +type Poll struct { + title string + description string + deadline int64 // block height + voters *avl.Tree // addr -> yes / no (bool) +} + +// Getters +func (p Poll) Title() string { + return p.title +} + +func (p Poll) Description() string { + return p.description +} + +func (p Poll) Deadline() int64 { + return p.deadline +} + +func (p Poll) Voters() *avl.Tree { + return p.voters +} + +// Poll instance constructor +func NewPoll(title, description string, deadline int64) *Poll { + return &Poll{ + title: title, + description: description, + deadline: deadline, + voters: avl.NewTree(), + } +} + +// Vote Votes for a user +func (p *Poll) Vote(voter std.Address, vote bool) { + p.Voters().Set(string(voter), vote) +} + +// HasVoted vote: yes - true, no - false +func (p *Poll) HasVoted(address std.Address) (bool, bool) { + vote, exists := p.Voters().Get(string(address)) + if exists { + return true, vote.(bool) + } + return false, false +} + +// VoteCount Returns the number of yay & nay votes +func (p Poll) VoteCount() (int, int) { + var yay int + + p.Voters().Iterate("", "", func(key string, value interface{}) bool { + vote := value.(bool) + if vote == true { + yay = yay + 1 + } + }) + return yay, p.Voters().Size() - yay +} +``` + +A few remarks: + +- We are using the `std` library for accessing blockchain-related functionality and types, such as `std.Address`. +- Since the `map` data type is not deterministic in Go, we need to use the AVL tree structure, defined + under `p/demo/avl`. + It behaves similarly to a map; it maps a key of type `string` onto a value of any type - `interface{}`. +- We are importing the `p/demo/avl` package directly from on-chain storage, which can be accessed through the + path `gno.land/`. + As of October 2023, you can find already-deployed packages & libraries which provide additional Gno functionality in + the [monorepo](https://github.com/gnolang/gno), under the `examples/gno.land` folder. + +:::info +After testing the `Poll` package, we need to deploy it in order to use it in our realm. +Check out the [deployment](deploy.md) guide to learn how to do this. +::: + +### Poll Factory Realm + +Moving on, we can create the Poll Factory realm. + +The realm will contain the following functionality: + +- An exported `NewPoll` method, to allow users to create polls +- An exported `Vote` method, to allow users to pledge votes for any active poll +- A `Render` function to display the realm state + +[embedmd]:# (../assets/how-to-guides/write-simple-dapp/poll-2.gno go) +```go +package poll + +import ( + "std" + + "gno.land/p/demo/avl" + "gno.land/p/demo/poll" + "gno.land/p/demo/ufmt" +) + +// state variables +var ( + polls *avl.Tree // id -> Poll + pollIDCounter int +) + +func init() { + polls = avl.NewTree() + pollIDCounter = 0 +} + +// NewPoll - Creates a new Poll instance +func NewPoll(title, description string, deadline int64) string { + // get block height + if deadline <= std.GetHeight() { + return "Error: Deadline has to be in the future." + } + + // convert int ID to string used in AVL tree + id := ufmt.Sprintf("%d", pollIDCounter) + p := poll.NewPoll(title, description, deadline) + + // add new poll in avl tree + polls.Set(id, p) + + // increment ID counter + pollIDCounter = pollIDCounter + 1 + + return ufmt.Sprintf("Successfully created poll #%s!", id) +} + +// Vote - vote for a specific Poll +// yes - true, no - false +func Vote(pollID int, vote bool) string { + // get txSender + txSender := std.GetOrigCaller() + + id := ufmt.Sprintf("%d", pollID) + // get specific Poll from AVL tree + pollRaw, exists := polls.Get(id) + + if !exists { + return "Error: Poll with specified doesn't exist." + } + + // cast Poll into proper format + poll, _ := pollRaw.(*poll.Poll) + + voted, _ := poll.HasVoted(txSender) + if voted { + return "Error: You've already voted!" + } + + if poll.Deadline() <= std.GetHeight() { + return "Error: Voting for this poll is closed." + } + + // record vote + poll.Vote(txSender, vote) + + // update Poll in tree + polls.Set(id, poll) + + if vote == true { + return ufmt.Sprintf("Successfully voted YAY for poll #%s!", id) + } + return ufmt.Sprintf("Successfully voted NAY for poll #%s!", id) +} +``` + +With that we have written the core functionality of the realm, and all that is left is +the [Render function](http://localhost:3000/explanation/realms). +Its purpose is to help us display the state of the realm in Markdown, by formatting the state into a string buffer: + +[embedmd]:# (../assets/how-to-guides/write-simple-dapp/poll-3.gno go) +```go +func Render(path string) string { + var b bytes.Buffer + + b.WriteString("# Polls!\n\n") + + if polls.Size() == 0 { + b.WriteString("### No active polls currently!") + return b.String() + } + polls.Iterate("", "", func(key string, value interface{}) bool { + + // cast raw data from tree into Poll struct + p := value.(*poll.Poll) + ddl := p.Deadline() + + yay, nay := p.VoteCount() + yayPercent := 0 + nayPercent := 0 + + if yay+nay != 0 { + yayPercent = yay * 100 / (yay + nay) + nayPercent = nay * 100 / (yay + nay) + } + + b.WriteString( + ufmt.Sprintf( + "## Poll #%s: %s\n", + key, // poll ID + p.Title(), + ), + ) + + dropdown := "
\nPoll details
" + + b.WriteString(dropdown + "Description: " + p.Description()) + + b.WriteString( + ufmt.Sprintf("
Voting until block: %d
Current vote count: %d", + p.Deadline(), + p.Voters().Size()), + ) + + b.WriteString( + ufmt.Sprintf("
YAY votes: %d (%d%%)", yay, yayPercent), + ) + b.WriteString( + ufmt.Sprintf("
NAY votes: %d (%d%%)
", nay, nayPercent), + ) + + dropdown = "
\nVote details" + b.WriteString(dropdown) + + p.Voters().Iterate("", "", func(key string, value interface{}) bool { + + voter := key + vote := value.(bool) + + if vote == true { + b.WriteString( + ufmt.Sprintf("
%s voted YAY!", voter), + ) + } else { + b.WriteString( + ufmt.Sprintf("
%s voted NAY!", voter), + ) + } + return false + }) + + b.WriteString("
\n\n") + return false + }) + return b.String() +} +``` + +## Conclusion + +That's it 🎉 + +You have successfully built a simple but fully-fledged dApp using Gno! +Now you're ready to conquer new, more complex dApps in Gno. diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 00000000000..4f165e4ea76 --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,56 @@ +--- +id: overview +slug: / +description: "Gno.land is a Layer 1 blockchain platform that enables the execution of Smart Contracts using an interpreted +version of the Go programming language called Gno." +--- + +# Overview + +## What is Gno.land? + +Gno.land is a Layer 1 blockchain platform that enables the execution of Smart Contracts using an interpreted +version of the Go programming language called Gno (Gno for short). + +### Key Features and Technology + +1. **Interpreted Gno**: Gno.land utilizes the Gno programming language, which is based on Go. It is executed + through a specialized virtual machine called the GnoVM, purpose-built for blockchain development with built-in + determinism and a modified standard library. While Gno + shares similarities with Go in terms of syntax, it currently lacks go routine support. However, this feature is + planned for future development, ensuring deterministic GnoVM executions. +2. **Consensus Protocol - Tendermint2**: Gno.land achieves consensus between blockchain nodes using the Tendermint2 + consensus protocol. This approach ensures secure and reliable network operation. +3. **Inter-Blockchain Communication (IBC)**: In the future, Gno.land will be able to communicate and exchange data with + other blockchain networks within the Cosmos ecosystem through the Inter-Blockchain Communication (IBC) protocol. + +### Why Go-based? + +The decision to base Gno.land's language on Go was influenced by the following factors: + +1. **Standard and Secure Language**: Go is a well-established and secure programming language, widely adopted in the + software development community. By leveraging Go's features, Gno.land benefits from a robust and proven foundation. +2. **User-Friendly**: Go's simplicity and ease of understanding make it beginner-friendly. This accessibility lowers the + entry barrier for developers to create Smart Contracts on the Gno.land platform. + +### How does it compare with Ethereum? + +In comparison to Ethereum, Gno.land offers distinct advantages: + +1. **Transparent and Auditable Smart Contracts**: Gno.land Smart Contracts are fully transparent and auditable by users + because the actual source code is uploaded to the blockchain. In contrast, Ethereum requires contracts to be + precompiled into bytecode, leading to less transparency as bytecode is stored on the blockchain, not the + human-readable source code. + +2. **General-Purpose Language**: Gno.land's Gno is a general-purpose language, similar to Go, extending its + usability beyond the context of blockchain. In contrast, Solidity is designed specifically for Smart Contracts on the + Ethereum platform. + +## Using the Gno.land Documentation + +Gno.land's documentation adopts the [Diataxis](https://diataxis.fr/) framework, ensuring structured and predictable content. It includes: +- A [Getting Started](./getting-started/local-setup.md) section, covering simple instructions on how to begin your journey into Gno.land. +- Concise how-to guides for specific technical tasks. +- Conceptual explanations, offering context and usage insights. +- Detailed reference sections with implementation specifics. +- Tutorials aimed at beginners to build fundamental skills for developing in Gno.land. diff --git a/docs/peace.md b/docs/peace.md deleted file mode 100644 index e76faae1ca3..00000000000 --- a/docs/peace.md +++ /dev/null @@ -1,242 +0,0 @@ -# Peace! - -_or, Everyone is Invited to Gno.land, if you want!_ - -I've never been put in such a difficult position, of having information that I -cannot reveal. And if you know me, you know that I like to speak my mind. But -I cannot say the things that I would rather say, because you get a lot of flack -for saying anything bad about a public chain. - -So I have been sitting on this issue, losing sleep about it for years, because -it leads me to worry about the safety of the hub. From an external person's -point of view, the solution is obvious -- reveal the information for the -betterment of everyone, no matter the consequences, because that is the right -thing to do. As a stakeholder, and I agree with the majority of the community -that peace and silence is better, with exceptions. - -So without turning this into a war of accusations bringing back past drama, -let's just do this: dear core contributors, Ethan Buchman, Zaki Manian, Jack -Zampolin, and everyone, here is my peace plan. - ----------------------------------------- - -## On Prop 69 - -Prop 69 is about adding CosmWASM to the hub. I have repeatedly talked about -the dangers of adding CosmWASM to the hub, including a document shared two -years ago. - -https://github.com/jaekwon/cosmos_roadmap/tree/master/shape_of_cosmos#smart-contracts - -Even before prop 69, I had declared publicly that stakers voting yes to adding -WASM on the hub would not receive airdrops. Primarily, because it increases -the surface area for attack by an order of magnitude. CosmWASM adds two layers -of new complexity to the hub. WASM itself, as well as CosmWASM. WASM as a spec -and its implementations are still maturing, and though available on browsers, -and some blockchains, it still hasn't gone through the gauntlet of time. All -new complex technologies like WASM, Java, Linux, and even Go, in hindsight -have numerous bugs that could have or were used maliciously. The same will be -true of any WASM integration with the hub, and this potential for exploits -combined with the massive potential rewards (especially of pegged PoW tokens) -makes such exploits an inevitability. - -In Juno recently there was a bug that halted the chain for three days. Worse -can happen on the Cosmos Hub. The very identity of the Cosmos Hub (its most -valuable asset is specifically a schelling point brand, of being a "common IBC -hub") is threatened if a bug were to result in the theft or loss of coins. On -platforms like Ethereum or Polkadot, perhaps they would have a better time -rolling back the chain to undo a hack as in the DAO hack. The major difference -with an *IBC hub* is that it cannot simply reverse the transactions of other -chains. - -We have yet to experience such a bug in any of our zones on a major scale, and -have yet to learn how to coordinate in the case of such in an interconnected -web of zones. Where are the planning documents for disaster scenarios? Between -PoS chains with good governance, we will learn how to roll back transactions -across connections, if need be in exceptional circumstances, but we aren't -there yet. This option isn't even available with pegged PoW coins. - -Yes, the contracts that are approved to run will be governance gated, but this -is not enough. For one, even with perfect governance, there are two new pieces -of complexity that will see more zero day bugs in the future for exploitation. -In terms of governance, the contracts are probably going to be written in Rust, -and so suddenly the validators that joined the project by inspecting the Go -code are now required to also audit Rust code. But also, we are now truly -opening the doors to all kinds of contracts to be run, because while governance -does sometimes reject proposals, it is generally accommodating to new features -especially endorsed by core contributors. - -I know of three alternatives: - -(1) we can use IBC to offload features to other zones. For liquid staking -(which should not be the focus of the hub) the hub could allow validators to -restrict the destination of unbonded ATOMs, and smart contracts running on -other zones can distribute those ATOMs according to the logic of whatever -liquid staking contract. This ensures separation of concerns, and a minimal -hub. - -(2) we can use Go plugins to extend the functionality of the chain. _UPDATE_ -Some have said that Go plugins are not mature enough. There are options like -https://github.com/hashicorp/go-plugin. Spawning a sub-process is superior to -running it under a new virtual machine, on top of a new framework, written in a -different language than the rest of the stack. One uses the operating system. -The other invents a new one. - -(3) we can do nothing. if liquid staking is such a big deal, something is wrong -about priorities for a cosmic "hub". If the liquid staking market is larger -than the base non-liquid staking market, the system is open for manipulation -and is insecure. The focus should not be on self-limiting use-cases, but the -infinite market of running validators with replicated security, perhaps running -a simple dex, and most of all innovating on and offering interchain security, -the business of judging validation faults as related to Tendermint, and perhaps -the interpretation and enforcement of self-enforced customs (law) of a -blockchain as defined by its shareholders who defer validation (and perhaps -judicial services) to the Cosmos Hub because it has a reputation for being the -longest ever running proof of stake hub that has never gone down, even as -compared to the upcoming Ethereum2.0. - -And note, I'm not proposing that the ATOM stakers forgo the benefits of -supporting contracts with CosmWASM. I support Juno and Tardigrade and Ethan -Frey’s work, but I also support the Hub running shared security, especially -simple replicated shared security where the validators also validate other -chains. I think this, and interchain staking, are the only profit models needed -for the hub (besides being a hub). NOTE: But those "consumer chains" ought to -be provided with full disclosures that the Cosmos Hub validators do not -maintain their respective software (as it would be impossible to audit all -zones that would benefit from the hub's security) but only offering validation -services as-is. This would force the hub validators to solve process isolation -(and I would much prefer building the protocol to NOT require particular -solutions like Docker, but allows validator choice), or else they would quickly -get slashed from malware (and that would be good to prune those validators from -the hub). - -So many options that don't require putting WASM on the Cosmos Hub. - ------------------------------------- - -## On Incentivized Votes - -In corporations, you can buy shares to influence the outcome of governance -votes. In democracy, this is not allowed because the vote could be bought to -infringe upon the rights of other people. - -What do you do when the chain's own core contributors propose a proposal that -you judge damages the integrity of the system? I think that's a good time to -create a fork of the hub's ATOM distribution led by a new development team. -Sometimes this option is the only option because of safety concerns, and this -is the case for me here. - -### Why is the snapshot date 5/19/2022? - -A snapshot in the past is more vulnerable to insider gaming, because there is -an imbalance of information--only the coordinator knows, and so can game the -premine. - -It is good to give many people the advantage of participating in a snapshot. -Excluding anyone who would have been an ally of a chain, in turn creates -animosity that would rather see another project succeed where they are -included. - -Even before the proposal I had pre-declared that anyone who votes for WASM on -the hub would not receive a gno.land airdrop. The proposer probably knew this -when the proposal was submitted. - -The snapshot date would have been 7/4/2022, because that is Independence Day in -the United States. I originally chose Independence Day because of the general -original mission of Tendermint, Cosmos, Bitcoin, and the crypto spirit; and -because the United States (as flawed as it is) is the best historic ideal of -human liberty we've had since before the days of Rome. - -Then prop 69 was submitted. I had said previously that we would exclude those -who vote in favor of WASM on the hub, but we don't have the tools yet to tally -the movement of tainted ATOMs after the unbonding period for the hub. So I -decided to move the snapshot date to 5/19/2022. - -Now with prop 69, I see that to me, 21 days after the beginning of proposal -\#69, 5/20/2022 (but 5/19/2022 PDT) is a chance to create a new community within -the Cosmos ecosystem that champions safety with a zero tolerance policy and a -mission to develop social coordination tools like the GNO smart contract VM, to -create even better governing bodies than the one we have today. - -### Gno.land and Cosmos Hub - -Now, I feel compelled to exit should prop \#69 pass. But as it is now, 16.57% -are voting YES, while NO and NO WITH VETO have 70.73% and 8.38% of the votes -with turnout at 30%. If the proposal does not pass, I would feel no need to -exit. For as long as the Cosmos Hub remains minimal and secure, we will favor -it as the dominant or only token hub connected to gno.land via the current IBC -implementations for the purpose of interchain token transfers. It's a job that -we'd rather not solve, as specialization is what will get us to the finish line -before other platforms do, and also I'm quite hooked on gnolang programming and -just want to make gnolang apps. Not everybody wants to build a DTCC, but many -would prefer to use it. - -### Airdrop distribution - -When I was asked on Cryptocito what I would have changed if I were to do it all -again, well, I would put the ICF in the hands of the chain. So in gno.land, the -ICF's portion of $GNOT will go to DAOs on gno.land. As for me, I have a -significant amount of ATOMs that voted for NO WITH VETO, but most of my tokens -by far are with the company that I previously founded, then called All in Bits, -Inc. AIB will not receive any $GNOT except by completing negotiations with me, -which is taking a lot longer than is reasonable--or not. - -For reference, for the genesis of the Cosmos Hub, the total distribution for -both entities was 20% of all ATOMs, and today it is still significant. The -total premine that I control directly or indirectly will not exceed 1/3 of the -total $GNOT distribution, but I am considering 20% again. - -Some more guidelines, which may change, so don't take anything here as -financial advice: - - * NO with VETO is slightly better than NO. - * NO is better than ABSTAIN. - * ABSTAIN is better than not voting at all. - * Delegators inherit the votes of the validators (unless delegators override). - * If you vote YES on \#69, you will not receive gno.land $GNOTs. - -Regarding ATOMs locked in IBC channels, those will count as not voting, -which is fine, and corresponding $GNOT will be released once the respective -zone communities create a provable audited distribution given the snapshot date, -which is defined to be exactly when those who voted on #69 can unbond and move their -tokens, which is 21 days from the start of the proposal. Soon we will be more precise -about what that is, but communities please prepare accordingly. If you aren't sure, -leave ATOMs on the hub. - -$ATOM in centralized exchanges will not result in $GNOT in the airdrop. Centralized -exchanges will not be allowed to redeem them even after genesis, even if they are -included. We will try to distinguish between custodial $ATOM vs non-custodial $ATOM -for all centralized exchanges with legally binding attestations, with good faith effort; -and so centralized exchanges would be welcome with their own capital, but not with client -funds. Nothing is guaranteed. - -We reserve the right to change our minds. This project is highly experimental and has -non-zero chances of failure. Nothing I tweet should be construed as financial -advice. And finally, what you do with $ATOM is not my responsibility; I don't have -control of the Cosmos Hub. - -NOTE: If you don't like my airdrop rules, you are free to make your own, and if -you're nice you can even run gno.land contracts if you so want there, or you -can just run a fork of gaia. - -If you have a better ideal for such an exit-drop by tweaking the governance -module, I'd love to hear your feedback, or generally how you think I could have -done this better. Some say that they don't want to see more of this kind of -forking, but I think we ought to celebrate it instead. - ----------------------------------------- - -## Conclusion - -Here's a peace offering. - -Just change your vote from YES to NO, and I will not intervene upon the second -submission of the proposal (and I would even fund its deposit if need be). But -if you instead feel strongly about signaling in favor of CosmWASM, here you can -express it, and I celebrate you, for being different than I, and wish you the -best of luck. That is equivalent to a no-confidence vote on gno.land, and is a -proper way to diss me. Again, I salute you. - -If you can reconsider your vote to be a NO, or even better, a NO WITH VETO, I -welcome you to gno.land. Happy 5/19/2022 (5/20/2022 Europe) Gno.land -Independence Day! diff --git a/docs/reference/gno-js-client/getting-started.md b/docs/reference/gno-js-client/getting-started.md new file mode 100644 index 00000000000..188f084bd56 --- /dev/null +++ b/docs/reference/gno-js-client/getting-started.md @@ -0,0 +1,25 @@ +--- +id: gno-js-getting-started +--- + +# Getting Started + +[@gnolang/gno-js-client](https://github.com/gnolang/gno-js-client) is a JavaScript/TypeScript client implementation for Gno chains. It is an extension of the +[tm2-js-client](https://github.com/gnolang/tm2-js-client), but with Gno-specific functionality. + +## Key Features + +- Provides the ability to interact with Gno Realms / Packages +- Easy interaction with VM-specific ABCI queries + +## Installation + +To install `@gnolang/gno-js-client`, use your preferred package manager: + +```bash +yarn add @gnolang/gno-js-client +``` + +```bash +npm install @gnolang/gno-js-client +``` diff --git a/docs/reference/gno-js-client/gno-provider.md b/docs/reference/gno-js-client/gno-provider.md new file mode 100644 index 00000000000..a5248349d35 --- /dev/null +++ b/docs/reference/gno-js-client/gno-provider.md @@ -0,0 +1,124 @@ +--- +id: gno-js-provider +--- + +# Gno Provider + +The `Gno Provider` is an extension on the `tm2-js-client` `Provider`, +outlined [here](../tm2-js-client/Provider/provider.md). Both JSON-RPC and WS providers are included with the package. + +## Realm Methods + +### getRenderOutput + +Executes the Render(path) method in read-only mode + +#### Parameters + +* `packagePath` **string** the gno package path +* `path` **string** the render path +* `height` **number** the height for querying. + If omitted, the latest height is used (optional, default `0`) + +Returns **Promise** + +#### Usage + +```ts +await provider.getRenderOutput('gno.land/r/demo/demo_realm', ''); +// ## Hello World! +``` + +### getFunctionSignatures + +Fetches public facing function signatures + +#### Parameters + +* `packagePath` **string** the gno package path +* `height` **number** the height for querying. + If omitted, the latest height is used (optional, default `0`) + +Returns **Promise** + +#### Usage + +```ts +await provider.getFunctionSignatures('gno.land/r/demo/foo20'); +/* +[ + { FuncName: 'TotalSupply', Params: null, Results: [ [Object] ] }, + { + FuncName: 'BalanceOf', + Params: [ [Object] ], + Results: [ [Object] ] + }, + { + FuncName: 'Allowance', + Params: [ [Object], [Object] ], + Results: [ [Object] ] + }, + { + FuncName: 'Transfer', + Params: [ [Object], [Object] ], + Results: null + }, + { + FuncName: 'Approve', + Params: [ [Object], [Object] ], + Results: null + }, + { + FuncName: 'TransferFrom', + Params: [ [Object], [Object], [Object] ], + Results: null + }, + { FuncName: 'Faucet', Params: null, Results: null }, + { FuncName: 'Mint', Params: [ [Object], [Object] ], Results: null }, + { FuncName: 'Burn', Params: [ [Object], [Object] ], Results: null }, + { FuncName: 'Render', Params: [ [Object] ], Results: [ [Object] ] } +] + */ +``` + +### evaluateExpression + +Evaluates any expression in readonly mode and returns the results + +#### Parameters + +* `packagePath` **string** the gno package path +* `expression` **string** the expression to be evaluated +* `height` **number** the height for querying. + If omitted, the latest height is used (optional, default `0`) + +Returns **Promise** + +#### Usage + +```ts +await provider.evaluateExpression('gno.land/r/demo/foo20', 'TotalSupply()') +// (10100000000 uint64) +``` + +### getFileContent + +Fetches the file content, or the list of files if the path is a directory + +#### Parameters + +* `packagePath` **string** the gno package path +* `height` **number** the height for querying. + If omitted, the latest height is used (optional, default `0`) + +Returns **Promise** + +#### Usage + +```ts +await provider.getFileContent('gno.land/r/demo/foo20', 'TotalSupply()') +/* +foo20.gno +foo20_test.gno + */ +``` diff --git a/docs/reference/gno-js-client/gno-wallet.md b/docs/reference/gno-js-client/gno-wallet.md new file mode 100644 index 00000000000..7f7c44cd9b0 --- /dev/null +++ b/docs/reference/gno-js-client/gno-wallet.md @@ -0,0 +1,78 @@ +--- +id: gno-js-wallet +--- + +# Gno Wallet + +The `Gno Wallet` is an extension on the `tm2-js-client` `Wallet`, outlined [here](../tm2-js-client/wallet.md). + +## Account Methods + +### transferFunds + +Initiates a native currency transfer transaction between accounts + +#### Parameters + +* `to` **string** the bech32 address of the receiver +* `funds` **Map** the denomination -> value map for funds +* `fee` **TxFee** the custom transaction fee, if any + +Returns **Promise** + +#### Usage + +```ts +let fundsMap = new Map([ + ["ugnot", 10], +]); + +await wallet.transferFunds('g1flk9z2qmkgqeyrl654r3639rzgz7xczdfwwqw7', fundsMap); +// returns the transaction hash +``` + +### callMethod + +Invokes the specified method on a GNO contract + +#### Parameters + +* `path` **string** the gno package / realm path +* `method` **string** the method name +* `args` **string[]** the method arguments, if any +* `funds` **Map** the denomination -> value map for funds +* `fee` **TxFee** the custom transaction fee, if any + +Returns **Promise** + +#### Usage + +```ts +let fundsMap = new Map([ + ["ugnot", 10], +]); + +await wallet.callMethod('gno.land/r/demo/foo20', 'TotalBalance', []); +// returns the transaction hash +``` + +### deployPackage + +Deploys the specified package / realm + +#### Parameters + +* `gnoPackage` **MemPackage** the package / realm to be deployed +* `funds` **Map** the denomination -> value map for funds +* `fee` **TxFee** the custom transaction fee, if any + +Returns **Promise** + +#### Usage + +```ts +const memPackage: MemPackage = // ... + + await wallet.deployPackage(memPackage); +// returns the transaction hash +``` diff --git a/docs/reference/go-gno-compatibility.md b/docs/reference/go-gno-compatibility.md new file mode 100644 index 00000000000..71f46adf836 --- /dev/null +++ b/docs/reference/go-gno-compatibility.md @@ -0,0 +1,314 @@ +--- +id: go-gno-compatibility +--- + +# Go - Gno compatibility + +## Native keywords + +| keyword | support | +|-------------|------------------------| +| break | full | +| case | full | +| const | full | +| continue | full | +| default | full | +| defer | full | +| else | full | +| fallthrough | full | +| for | full | +| func | full | +| go | missing (after launch) | +| goto | full | +| if | full | +| import | full | +| interface | full | +| package | full | +| range | full | +| return | full | +| select | missing (after launch) | +| struct | full | +| switch | full | +| type | full | +| var | full | + +Generics are currently not implemented. + +## Native types + +| type | usage | persistency | +|-----------------------------------------------|------------------------|------------------------------------------------------------| +| `bool` | full | full | +| `byte` | full | full | +| `int`, `int8`, `int16`, `int32`, `int64` | full | full | +| `uint`, `uint8`, `uint16`, `uint32`, `uint64` | full | full | +| `float32`, `float64` | full | full | +| `complex64`, `complex128` | missing (TBD) | missing | +| `uintptr`, `unsafe.Pointer` | missing | missing | +| `string` | full | full | +| `rune` | full | full | +| `interface{}` | full | full | +| `[]T` (slices) | full | full\* | +| `[N]T` (arrays) | full | full\* | +| `map[T1]T2` | full | full\* | +| `func (T1...) T2...` | full | full (needs more tests) | +| `*T` (pointers) | full | full\* | +| `chan T` (channels) | missing (after launch) | missing (after launch) | + +**\*:** depends on `T`/`T1`/`T2` + +Additional native types: + +| type | comment | +|----------|--------------------------------------------------------------------------------------------| +| `bigint` | Based on `math/big.Int` | +| `bigdec` | Based on https://github.com/cockroachdb/apd, (see https://github.com/gnolang/gno/pull/306) | + + +## Stdlibs + +Legend: + +* `nondet`: the standard library in question would require non-deterministic + behaviour to implement as in Go, such as cryptographical randomness or + os/network access. The library may still be implemented at one point, with a + different API. +* `gospec`: the standard library is very Go-specific -- for instance, it is used + for debugging information or for parsing/build Go source code. A Gno version + may exist at one point, likely with a different package name or semantics. +* `gnics`: the standard library requires generics. +* `test`: the standard library is currently available for use exclusively in + test contexts, and may have limited functionality. +* `cmd`: the Go standard library is a command -- a direct equivalent in Gno + would not be useful. +* `tbd`: whether to include the standard library or not is still up for + discussion. +* `todo`: the standard library is to be added, and + [contributions are welcome.](https://github.com/gnolang/gno/issues/1267) +* `part`: the standard library is partially implemented in Gno, and contributions are + welcome to add the missing functionality. +* `full`: the standard library is fully implemented in Gno. + + + +| package | status | +|---------------------------------------------|----------| +| archive/tar | `tbd` | +| archive/zip | `tbd` | +| arena | `improb` | +| bufio | `full` | +| builtin | `full`[^1] | +| bytes | `full` | +| cmd/\* | `cmd` | +| compress/bzip2 | `tbd` | +| compress/flate | `tbd` | +| compress/gzip | `tbd` | +| compress/lzw | `tbd` | +| compress/zlib | `tbd` | +| container/heap | `tbd` | +| container/list | `tbd` | +| container/ring | `tbd` | +| context | `tbd` | +| crypto | `todo` | +| crypto/aes | `todo` | +| crypto/boring | `tbd` | +| crypto/cipher | `part` | +| crypto/des | `tbd` | +| crypto/dsa | `tbd` | +| crypto/ecdh | `tbd` | +| crypto/ecdsa | `tbd` | +| crypto/ed25519 | `tbd` | +| crypto/elliptic | `tbd` | +| crypto/hmac | `todo` | +| crypto/md5 | `test`[^2] | +| crypto/rand | `nondet` | +| crypto/rc4 | `tbd` | +| crypto/rsa | `tbd` | +| crypto/sha1 | `test`[^2] | +| crypto/sha256 | `part`[^3] | +| crypto/sha512 | `tbd` | +| crypto/subtle | `tbd` | +| crypto/tls | `nondet` | +| crypto/tls/fipsonly | `nondet` | +| crypto/x509 | `tbd` | +| crypto/x509/pkix | `tbd` | +| database/sql | `nondet` | +| database/sql/driver | `nondet` | +| debug/buildinfo | `gospec` | +| debug/dwarf | `gospec` | +| debug/elf | `gospec` | +| debug/gosym | `gospec` | +| debug/macho | `gospec` | +| debug/pe | `gospec` | +| debug/plan9obj | `gospec` | +| embed | `tbd` | +| encoding | `full` | +| encoding/ascii85 | `todo` | +| encoding/asn1 | `todo` | +| encoding/base32 | `todo` | +| encoding/base64 | `full` | +| encoding/binary | `part` | +| encoding/csv | `todo` | +| encoding/gob | `tbd` | +| encoding/hex | `full` | +| encoding/json | `todo` | +| encoding/pem | `todo` | +| encoding/xml | `todo` | +| errors | `part` | +| expvar | `tbd` | +| flag | `nondet` | +| fmt | `test`[^4] | +| go/ast | `gospec` | +| go/build | `gospec` | +| go/build/constraint | `gospec` | +| go/constant | `gospec` | +| go/doc | `gospec` | +| go/doc/comment | `gospec` | +| go/format | `gospec` | +| go/importer | `gospec` | +| go/parser | `gospec` | +| go/printer | `gospec` | +| go/scanner | `gospec` | +| go/token | `gospec` | +| go/types | `gospec` | +| hash | `full` | +| hash/adler32 | `full` | +| hash/crc32 | `todo` | +| hash/crc64 | `todo` | +| hash/fnv | `todo` | +| hash/maphash | `todo` | +| html | `todo` | +| html/template | `todo` | +| image | `tbd` | +| image/color | `tbd` | +| image/color/palette | `tbd` | +| image/draw | `tbd` | +| image/gif | `tbd` | +| image/jpeg | `tbd` | +| image/png | `tbd` | +| index/suffixarray | `tbd` | +| io | `full` | +| io/fs | `tbd` | +| io/ioutil | removed[^5] | +| log | `tbd` | +| log/slog | `tbd` | +| log/syslog | `nondet` | +| maps | `gnics` | +| math | `full` | +| math/big | `tbd` | +| math/bits | `full` | +| math/cmplx | `tbd` | +| math/rand | `todo` | +| mime | `tbd` | +| mime/multipart | `tbd` | +| mime/quotedprintable | `tbd` | +| net | `nondet` | +| net/http | `nondet` | +| net/http/cgi | `nondet` | +| net/http/cookiejar | `nondet` | +| net/http/fcgi | `nondet` | +| net/http/httptest | `nondet` | +| net/http/httptrace | `nondet` | +| net/http/httputil | `nondet` | +| net/http/internal | `nondet` | +| net/http/pprof | `nondet` | +| net/mail | `nondet` | +| net/netip | `nondet` | +| net/rpc | `nondet` | +| net/rpc/jsonrpc | `nondet` | +| net/smtp | `nondet` | +| net/textproto | `nondet` | +| net/url | `full` | +| os | `nondet` | +| os/exec | `nondet` | +| os/signal | `nondet` | +| os/user | `nondet` | +| path | `full` | +| path/filepath | `nondet` | +| plugin | `nondet` | +| reflect | `todo` | +| regexp | `full` | +| regexp/syntax | `full` | +| runtime | `gospec` | +| runtime/asan | `gospec` | +| runtime/cgo | `gospec` | +| runtime/coverage | `gospec` | +| runtime/debug | `gospec` | +| runtime/metrics | `gospec` | +| runtime/msan | `gospec` | +| runtime/pprof | `gospec` | +| runtime/race | `gospec` | +| runtime/trace | `gospec` | +| slices | `gnics` | +| sort | `part`[^6] | +| strconv | `part` | +| strings | `full` | +| sync | `tbd` | +| sync/atomic | `tbd` | +| syscall | `nondet` | +| syscall/js | `nondet` | +| testing | `part` | +| testing/fstest | `tbd` | +| testing/iotest | `tbd` | +| testing/quick | `tbd` | +| text/scanner | `todo` | +| text/tabwriter | `todo` | +| text/template | `todo` | +| text/template/parse | `todo` | +| time | `full`[^7] | +| time/tzdata | `tbd` | +| unicode | `full` | +| unicode/utf16 | `tbd` | +| unicode/utf8 | `full` | +| unsafe | `nondet` | + +[^1]: `builtin` is a "fake" package that exists to document the behaviour of + some builtin functions. The "fake" package does not currently exist in Gno, + but [all functions up to Go 1.17 exist](https://pkg.go.dev/builtin@go1.17), + except for those relating to complex or channel types. +[^2]: `crypto/sha1` and `crypto/md5` implement "deprecated" hashing + algorithms, widely considered unsafe for cryptographic hashing. Decision on + whether to include these as part of the official standard libraries is still + pending. +[^3]: `crypto/sha256` is currently only implemented for `Sum256`, which should + still cover a majority of use cases. A full implementation is welcome. +[^4]: like many other encoding packages, `fmt` depends on `reflect` to be added. + For now, package `gno.land/p/demo/ufmt` may do what you need. In test + functions, `fmt` works. +[^5]: `io/ioutil` [is deprecated in Go.](https://pkg.go.dev/io/ioutil) + Its functionality has been moved to packages `os` and `io`. The functions + which have been moved in `io` are implemented in that package. +[^6]: `sort` has the notable omission of `sort.Slice`. You'll need to write a + bit of boilerplate, but you can use `sort.Interface` + `sort.Sort`! +[^7]: `time.Now` returns the block time rather than the system time, for + determinism. Concurrent functionality (such as `time.Ticker`) is not implemented. + +## Tooling (`gno` binary) + +| go command | gno command | comment | +|-------------------|------------------|-----------------------------------------------------------------------| +| go bug | | see https://github.com/gnolang/gno/issues/733 | +| go build | gno build | same intention, limited compatibility | +| go clean | gno clean | same intention, limited compatibility | +| go doc | gno doc | limited compatibility; see https://github.com/gnolang/gno/issues/522 | +| go env | | | +| go fix | | | +| go fmt | | gofmt (& similar tools, like gofumpt) works on gno code. | +| go generate | | | +| go get | | see `gno mod download`. | +| go help | gno $cmd --help | ie. `gno doc --help` | +| go install | | | +| go list | | | +| go mod | gno mod | | +| + go mod init | gno mod init | same behavior | +| + go mod download | gno mod download | same behavior | +| + go mod tidy | gno mod tidy | same behavior | +| | gno precompile | | +| go work | | | +| | gno repl | | +| go run | gno run | | +| go test | gno test | limited compatibility | +| go tool | | | +| go version | | | +| go vet | | | +| golint | gno lint | same intention | diff --git a/docs/reference/rpc-endpoints.md b/docs/reference/rpc-endpoints.md new file mode 100644 index 00000000000..896416c1760 --- /dev/null +++ b/docs/reference/rpc-endpoints.md @@ -0,0 +1,502 @@ +--- +id: rpc-endpoints +--- + +# Gno RPC Endpoints + +## Common Parameters + +#### Response + +| Name | Type | Description | +| --------------- | ------ | --------------------------------- | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | Object | (upon success) The result object. | +| `error` | Object | (upon failure) The error object. | +| `error.code` | Number | The error code. | +| `error.message` | String | The error message. | +| `error.data` | String | The error data. | + +## Health Check + +Call with the `/health` path when verifying that the node is running. + +#### Response + +| Name | Type | Description | +| --------- | ------ | ---------------- | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | Object | {} | + +## Check Node Server Status + +Call with the `/status` path to check the information from a node. + +#### Response + +| Name | Type | Description | +| --------- | ---------------- | ------------------------------------- | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | \[Status Result] | The result of the node server status. | + +#### Status Result + +| Name | Type | Description | +| ---------------- | ------ | ----------------------------------- | +| `node_info` | Object | General information about the node. | +| `sync_info` | Object | The sync information. | +| `validator_info` | Object | The validator information. | + +## Get Network Information + +Call with the `/net_info` path to check the network information from the node. + +#### Response + +| Name | Type | Description | +| --------- | ----------------- | ------------------------ | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | \[NetInfo Result] | The network information. | + +#### NetInfo Result + +| Name | Type | Description | +| ----------- | ---------- | ------------------ | +| `listening` | Boolean | Enables listening. | +| `listeners` | String \[] | List of listeners. | +| `n_peers` | String | Number of peers. | +| `peers` | String \[] | List of peers. | + +## Get Genesis Block Information + +Call with the `/genesis` path to retrieve information about the Genesis block from the node. + +#### Response + +| name | Type | Description | +| --------- | ------ | ------------------------------ | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | Object | The Genesis block information. | + +## Get Consensus Parameters + +Call with the /consensus\_params path to check the consensus algorithm parameters at the specified height. + +#### Parameters + +| Name | Description | +| -------- | ----------------- | +| `height` | The block height. | + +#### Response + +| Name | Type | Description | +| --------- | -------------------------- | ------------------------------------ | +| `jsonrpc` | String | The RPC Version. | +| `id` | String | The response ID. | +| `result` | \[Consensus Params Result] | The consensus parameter information. | + +#### Consensus Params Result + +| Name | Type | Description | +| ----------------------------- | ------ | -------------------------- | +| `block_height` | String | The block height. | +| `consensus_params` | Object | The parameter information. | +| `consensus_params.Block` | Object | The block parameters. | +| `consensus_params.Validator` | Object | The validator parameters. | + +## Get Consensus State + +Call with the `/consensus_state` to get the consensus state of the Gnoland blockchain + +#### Response + +| Name | Type | Description | +| ------- | --------------------------- | -------------------------------- | +| jsonrpc | String | The RPC version. | +| id | String | The response ID. | +| result | \[Consensus State Response] | The consensus state information. | + +#### Consensus State Response + +| Name | Type | Description | +| --------------------------------- | ------ | -------------------------------- | +| `round_state` | Object | The consensus state object. | +| `round_state.height/round/step` | String | The block height / round / step. | +| `round_state.start_time` | String | The round start time. | +| `round_state.proposal_block_hash` | String | The proposal block hash. | +| `round_state.locked_block_hash` | String | The locked block hash. | +| `round_state.valid_block_hash` | String | The valid block hash. | +| `round_state.height_vote_set` | Object | - | + +## Get Commit + +Call with the `/commit` path to retrieve commit information at the specified block height. + +#### Parameters + +| Name | Description | +| -------- | ----------------- | +| `height` | The block height. | + +#### Response + +| Name | Type | Description | +| --------- | ---------------- | ----------------------- | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | \[Commit Result] | The commit information. | + +#### Commit Result + +| Name | Type | Description | +| -------------- | ------- | ------------------------- | +| signed\_header | Object | The signed header object. | +| canonical | Boolean | Returns commit state. | + +## Get Block Information + +Call with the `/block` path to retrieve block information at the specified height. + +#### Parameters + +| Name | Description | +| -------- | ----------------- | +| `height` | The block height. | + +#### Response + +| Name | Type | Description | +| --------- | --------------- | ----------------------- | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | \[Block Result] | The commit information. | + +#### Block Result + +| Name | Type | Description | +| ------------ | ------ | ---------------------- | +| `block_meta` | Object | The block metadata. | +| `block` | Object | The block information. | + +## Get Block Results + +Call with the `/block_results` path to retrieve block processing information at the specified height. + +#### Parameters + +| Name | Description | +| -------- | ----------------- | +| `height` | The block height. | + +#### Response + +| Name | Type | Description | +| --------- | --------------- | ------------------ | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | \[Block Result] | The result object. | + +#### Block Result + +| Name | Type | Description | +| --------- | ------------------------ | ------------------------------------- | +| `height` | Object | The block height. | +| `results` | \[Block Result Info] \[] | The list of block processing results. | + +#### Block Result Info + +| Name | Type | Description | +| --------------------------- | ---------- | -------------------------------- | +| `deliver_tx` | Object \[] | The list of transaction results. | +| `deliver_tx[].ResponseBase` | Object | The transaction response object. | +| `deliver_tx[].GasWanted` | String | Maximum amount of gas to use. | +| `deliver_tx[].GasUsed` | String | Actual gas used. | +| `begin_block` | Object | Previous block information. | +| `end_block` | Object | Next block information. | + +## Get Block List + +Call with the `/blockchain` path to retrieve information about blocks within a specified range. + +#### Parameters + +| Name | Description | +| ----------- | ------------------------- | +| `minHeight` | The minimum block height. | +| `maxHeight` | The maximum block height. | + +#### Response + +| Name | Type | Description | +| --------- | -------------------- | ------------------ | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | \[Blockchain Result] | The result object. | + +#### Blockchain Result + +| Name | Type | Description | +| ------------- | ---------- | --------------------------- | +| `last_height` | String | The latest block height. | +| `block_meta` | Object \[] | The list of block metadata. | + +## Get a No. of Unconfirmed Transactions + +Call with the `/num_unconfirmed_txs` path to get data about unconfirmed transactions. + +#### Response + +| Name | Type | Description | +| --------- | ----------------------------- | ------------------ | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | \[Num Unconfirmed Txs Result] | The result object. | + +#### Num Unconfirmed Txs Result + +| Name | Type | Description | +| ------------- | ------ | --------------------------- | +| `n_txs` | String | The number of transactions. | +| `total` | String | The total number. | +| `total_bytes` | String | Total bytes. | +| `txs` | null | - | + +## Get a List of Unconfirmed Transactions + +Call with the `/unconfirmed_txs` path to get a list of unconfirmed transactions. + +#### Parameters + +| Name | Description | +| ------- | --------------------------------------- | +| `limit` | The maximum transaction numbers to get. | + +#### Response + +| Name | Type | Description | +| --------- | ------------------------- | ------------------ | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | \[Unconfirmed Txs Result] | The result object. | + +#### Unconfirmed Txs Result + +| Name | Type | Description | +| ------------- | ---------- | ----------------------------------- | +| `n_txs` | String | The number of transactions. | +| `total` | String | The total number. | +| `total_bytes` | String | Total bytes. | +| `txs` | Object \[] | A list of unconfirmed transactions. | + +## Get a List of Validators + +Call with the `/validators` path to get a list of validators at a specific height. + +#### Parameters + +| Name | Description | +| -------- | ----------------------------------------- | +| `height` | The block height (default: newest block). | + +#### Response + +| Name | Type | Description | +| --------- | -------------------- | ------------------ | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | \[Validators Result] | The result object. | + +#### Validators Result + +| Name | Type | Description | +| -------------- | ---------------- | ----------------------- | +| `block_height` | Object | The block height. | +| `validators` | \[Validator] \[] | The list of validators. | + +#### Validator + +| Name | Type | Description | +| ------------------- | ---------- | ---------------------------------------- | +| `address` | String | The address of the validator. | +| `pub_key` | Object \[] | The public key object of the validator. | +| `pub_key.@type` | String | The type of validator's public key. | +| `pub_key.value` | String | The value of the validator's public key. | +| `voting_power` | String | Voting power of the validator. | +| `proposer_priority` | String | The priority of the proposer. | + +## Broadcast a Transaction - Asynchronous + +Call with the `/broadcast_tx_async` path to create and broadcast a transaction without waiting for the transaction response. + +#### Parameters + +| Name | Description | +| ---- | ------------------------------------------- | +| `tx` | The value of the signed transaction binary. | + +#### Response + +| Name | Type | Description | +| --------- | --------------------- | ------------------ | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | \[Transaction Result] | The result object. | + +#### Transaction Result + +| Name | Type | Description | +| ----- | ------ | ---------------------------- | +| hash | String | The transaction hash. | +| data | Object | The transaction data object. | +| error | Object | The error object. | +| log | String | The log information. | + +## Broadcast a Transaction - Synchronous + +Call with the `/broadcast_tx_sync` path to create and broadcast a transaction, then wait for the transaction response. + +#### Parameters + +| Name | Description | +| ---- | ------------------------------------------- | +| `tx` | The value of the signed transaction binary. | + +#### Response + +| Name | Type | Description | +| --------- | --------------------- | ------------------ | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | \[Transaction Result] | The result object. | + +#### Transaction Result + +| Name | Type | Description | +| ----- | ------ | ---------------------------- | +| hash | String | The transaction hash. | +| data | Object | The transaction data object. | +| error | Object | The error object. | +| log | String | The log information. | + +## (NOT RECOMMENDED) Broadcast Transaction and Get Commit Information + +Call with the `/broadcast_tx_commit` path to create and broadcast a transaction, then wait for the transaction response and the commit response. + +#### Parameters + +| Name | Description | +| ---- | ------------------------------------------- | +| `tx` | The value of the signed transaction binary. | + +#### Response + +| Name | Type | Description | +| --------- | ---------------------------- | ------------------ | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | \[Transaction Commit Result] | The result object. | + +#### Transaction Commit Result + +| Name | Type | Description | +| ------------ | ------ | ----------------------------------------------------------- | +| `height` | String | The height of the block when the transaction was committed. | +| hash | String | The transaction hash. | +| `deliver_tx` | Object | The delivered transaction information. | +| `check_tx` | Object | The committed transaction information. | + +## ABCI + +### Get ABCI Information + +Call with the `/abci_info` path to get the latest information about the ABCI. + +#### Response + +| Name | Type | Description | +| --------- | ------------------- | ----------------------- | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | \[ABCI Info Result] | The commit information. | + +#### ABCI Info Result + +| Name | Type | Description | +| --------------------------- | ---------------- | -------------------------- | +| `response` | Object | The metadata of the block. | +| `response.ResponseBase` | \[ABCI Response] | The ABCI response data. | +| `response.ABCIVersion` | String | The ABCI version. | +| `response.AppVersion` | String | The app version. | +| `response.LastBlockHeight` | String | The latest block height. | +| `response.LastBlockAppHash` | String | The latest block hash. | + +#### ABCI Response + +| Name | Type | Description | +| ------ | ---------- | --------------------------------- | +| Data | String | The Base64-encoded response data. | +| Error | Object | The ABCI response error object. | +| Events | Object \[] | The list of event objects. | +| Log | String | The ABCI response log. | +| Info | String | The ABCI response information. | + +### Get ABCI Query + +Call with the `/abci_query` to get information via the ABCI Query. + +#### Query + +| Name | Description | +| ------------------------- | ------------------------------------------------------------------ | +| `auth/accounts/{ADDRESS}` | Returns the account information. | +| `bank/balances/{ADDRESS}` | Returns the balance information about the account. | +| `vm/qfuncs` | Returns public facing function signatures as JSON. | +| `vm/qfile` | Returns the file bytes, or list of files if directory. | +| `vm/qrender` | Calls `.Render()` in readonly mode. | +| `vm/qeval` | Evaluates any expression in readonly mode and returns the results. | +| `vm/store` | (not yet supported) Fetches items from the store. | +| `vm/package` | (not yet supported) Fetches a package's files. | + +#### Parameters + +| Name | Description | +| ------------------- | ------------------------------------------------ | +| `path` | The query path. | +| `data` | The data from the query path. | +| (optional) `height` | The block height (default: latest block height). | +| (optional) `prove` | The validation status. | + +#### Response + +| Name | Type | Description | +| --------- | -------------------- | ----------------------- | +| `jsonrpc` | String | The RPC version. | +| `id` | String | The response ID. | +| `result` | \[ABCI Query Result] | The commit information. | + +#### ABCI Query Result + +| Name | Type | Description | +| ----------------------- | ---------------- | -------------------------- | +| `response` | Object | The metadata of the block. | +| `response.ResponseBase` | \[ABCI Response] | The ABCI response data. | +| `response.Key` | String | The key. | +| `response.Value` | String | The value. | +| `response.Proof` | String | The validation ID. | +| `response.Height` | String | The block height. | + +#### ABCI Response + +| Name | Type | Description | +| ------ | ---------- | --------------------------------- | +| Data | String | The Base64-encoded response data. | +| Error | Object | The ABCI response error object. | +| Events | Object \[] | The list of event objects. | +| Log | String | The ABCI response log. | +| Info | String | The ABCI response information. | diff --git a/docs/reference/standard-library/overview.md b/docs/reference/standard-library/overview.md new file mode 100644 index 00000000000..ddf921e74a0 --- /dev/null +++ b/docs/reference/standard-library/overview.md @@ -0,0 +1,12 @@ +--- +id: overview +--- + +# Overview + +This section serves as a reference to the standard libraries available in Gno. + +Gno is designed to offer features similar to those in Go. Therefore, this documentation +does not include in-depth references for libraries that have identical implementation details +as those in Go. If a standard library differs in any way from its usage or implementation in Go, +it will be documented in this section. diff --git a/docs/reference/standard-library/std/address.md b/docs/reference/standard-library/std/address.md new file mode 100644 index 00000000000..707540740a9 --- /dev/null +++ b/docs/reference/standard-library/std/address.md @@ -0,0 +1,29 @@ +--- +id: address +--- + +# Address +Native address type in Gno, conforming to the Bech32 format. + +```go +type Address string +func (a Address) String() string {...} +func (a Address) IsValid() bool {...} +``` + +## String +Get **string** representation of **Address**. + +#### Usage +```go +stringAddr := addr.String() +``` + +--- +## IsValid +Check if **Address** is of a valid format. + +#### Usage +```go +if !address.IsValid() {...} +``` diff --git a/docs/reference/standard-library/std/banker.md b/docs/reference/standard-library/std/banker.md new file mode 100644 index 00000000000..621a447ab84 --- /dev/null +++ b/docs/reference/standard-library/std/banker.md @@ -0,0 +1,96 @@ +--- +id: banker +--- + +# Banker +View concept page [here](../../../concepts/standard-library/banker.md). + +```go +type BankerType uint8 + +const ( + BankerTypeReadonly BankerType = iota + BankerTypeOrigSend + BankerTypeRealmSend + BankerTypeRealmIssue +) + +type Banker interface { + GetCoins(addr Address) (dst Coins) + SendCoins(from, to Address, coins Coins) + IssueCoin(addr Address, denom string, amount int64) + RemoveCoin(addr Address, denom string, amount int64) +} +``` + +## GetBanker +Returns `Banker` of the specified type. + +#### Parameters +- `BankerType` - type of Banker to get: + - `BankerTypeReadonly` - read-only access to coin balances + - `BankerTypeOrigSend` - full access to coins sent with the transaction that calls the banker + - `BankerTypeRealmSend` - full access to coins that the realm itself owns, including the ones sent with the transaction + - `BankerTypeRealmIssue` - able to issue new coins + +#### Usage + +```go +banker := std.GetBanker(std.) +``` +--- + +## GetCoins +Returns `Coins` owned by `Address`. + +#### Parameters +- `addr` **Address** to fetch balances for + +#### Usage + +```go +coins := banker.GetCoins(addr) +``` +--- + +## SendCoins +Sends `coins` from address `from` to address `to`. `coins` needs to be a well-defined +`Coins` slice. + +#### Parameters +- `from` **Address** to send from +- `to` **Address** to send to +- `coins` **Coins** to send + +#### Usage +```go +banker.SendCoins(from, to, coins) +``` +--- + +## IssueCoin +Issues `amount` of coin with a denomination `denom` to address `addr`. + +#### Parameters +- `addr` **Address** to issue coins to +- `denom` **string** denomination of coin to issue +- `amount` **int64** amount of coin to issue + +#### Usage +```go +banker.IssueCoin(addr, denom, amount) +``` +--- + +## RemoveCoin +Removes (burns) `amount` of coin with a denomination `denom` from address `addr`. + +#### Parameters +- `addr` **Address** to remove coins from +- `denom` **string** denomination of coin to remove +- `amount` **int64** amount of coin to remove + +#### Usage +```go +banker.RemoveCoin(addr, denom, amount) +``` diff --git a/docs/reference/standard-library/std/chain.md b/docs/reference/standard-library/std/chain.md new file mode 100644 index 00000000000..bda09b2fe80 --- /dev/null +++ b/docs/reference/standard-library/std/chain.md @@ -0,0 +1,151 @@ +--- +id: chain +--- + +# Chain-related + +## IsOriginCall +```go +func IsOriginCall() bool +``` +Checks if the caller of the function is an EOA. Returns **true** if caller is an EOA, **false** otherwise. + +#### Usage +```go +if !std.IsOriginCall() {...} +``` +--- + +## AssertOriginCall +```go +func AssertOriginCall() +``` +Panics if caller of function is not an EOA. + +#### Usage +```go +std.AssertOriginCall() +``` +--- + +## CurrentRealmPath +```go +func CurrentRealmPath() string +``` +Returns the path of the realm it is called in. + +#### Usage +```go +realmPath := std.CurrentRealmPath() // gno.land/r/demo/users +``` +--- + +## GetChainID +```go +func GetChainID() string +``` +Returns the chain ID. + +#### Usage +```go +chainID := std.GetChainID() // dev | test3 | main ... +``` +--- + +## GetHeight +```go +func GetHeight() int64 +``` +Returns the current block number (height). + +#### Usage +```go +height := std.GetHeight() +``` +--- + +## GetOrigSend +```go +func GetOrigSend() Coins +``` +Returns the `Coins` that were sent along with the calling transaction. + +#### Usage +```go +coinsSent := std.GetOrigSend() +``` +--- + +## GetOrigCaller +```go +func GetOrigCaller() Address +``` +Returns the original signer of the transaction. + +#### Usage +```go +caller := std.GetOrigSend() +``` +--- + +## GetOrigPkgAddr +```go +func GetOrigPkgAddr() string +``` +Returns the address of the first (entry point) realm/package in a sequence of realm/package calls. + +#### Usage +```go +origPkgAddr := std.GetOrigPkgAddr() +``` +--- + +## CurrentRealm +```go +func CurrentRealm() Realm +``` +Returns current Realm object. + +[//]: # (todo link to realm type explanation) +#### Usage +```go +currentRealm := std.CurrentRealm() +``` +--- + +## PrevRealm +```go +func PrevRealm() Realm +``` +Returns the previous caller realm (can be realm or EOA). If caller is am EOA, `pkgpath` will be empty. + +#### Usage +```go +prevRealm := std.PrevRealm() +``` +--- + +## GetCallerAt +```go +func GetCallerAt(n int) Address +``` +Returns the n-th caller of the function, going back in the call trace. + +#### Usage +```go +currentRealm := std.GetCallerAt(1) // returns address of current realm +previousRealm := std.GetCallerAt(2) // returns address of previous realm/caller +std.GetCallerAt(0) // error, n must be > 0 +``` +--- + +## DerivePkgAddr +```go +func DerivePkgAddr(pkgPath string) Address +``` +Derives the Realm address from its `pkgpath` parameter. + +#### Usage +```go +realmAddr := std.DerivePkgAddr("gno.land/r/demo/tamagotchi") // g1a3tu874agjlkrpzt9x90xv3uzncapcn959yte4 +``` diff --git a/docs/reference/standard-library/std/coin.md b/docs/reference/standard-library/std/coin.md new file mode 100644 index 00000000000..a59e0781724 --- /dev/null +++ b/docs/reference/standard-library/std/coin.md @@ -0,0 +1,40 @@ +--- +id: coin +--- + +# Coin +View concept page [here](../../../concepts/standard-library/coin.md). + +```go +type Coin struct { + Denom string `json:"denom"` + Amount int64 `json:"amount"` +} +func (c Coin) String() string {...} +func (c Coin) IsGTE(other Coin) bool {...} +``` + +## String +Returns a string representation of the `Coin` it was called upon. + +#### Usage +```go +coin := std.Coin{"ugnot", 100} +coin.String() // 100ugnot +``` +--- +## IsGTE +Checks if the amount of `other` Coin is greater or equal than amount of Coin `c` it was called upon. +If coins compared are not of the same denomination, `IsGTE` will panic. + +#### Parameters +- `other` **Coin** to compare with + +#### Usage +```go +coin1 := std.Coin{"ugnot", 150} +coin2 := std.Coin{"ugnot", 100} + +coin1.IsGTE(coin2) // true +coin2.IsGTE(coin1) // false +``` \ No newline at end of file diff --git a/docs/reference/standard-library/std/coins.md b/docs/reference/standard-library/std/coins.md new file mode 100644 index 00000000000..0f813178330 --- /dev/null +++ b/docs/reference/standard-library/std/coins.md @@ -0,0 +1,50 @@ +--- +id: coins +--- + +# Coins + +`Coins` is a set of `Coin`, one per denomination. + +```go +type Coins []Coin +func (c Coins) String() string {...} +func (c Coins) AmountOf(denom string) int64 {...} +func (c Coins) Add(other Coins) Coins {...} +``` + +### String +Returns a string representation of the `Coins` set it was called upon. + +#### Usage +```go +coins := std.Coins{std.Coin{"ugnot", 100}, std.Coin{"foo", 150}, std.Coin{"bar", 200}} +coins.String() // 100ugnot,150foo,200bar +``` +--- + +### AmountOf +Returns **int64** amount of specified coin within the `Coins` set it was called upon. Returns `0` if coin specified coin does not exist in the set. + +### Parameters +- `denom` **string** denomination of specified coin + +#### Usage +```go +coins := std.Coins{std.Coin{"ugnot", 100}, std.Coin{"foo", 150}, std.Coin{"bar", 200}} +coins.AmountOf("foo") // 150 +``` +--- + +### Add +Adds (or updates) the amount of specified coins in the `Coins` set. If the specified coin does not exist, `Add` adds it to the set. + +### Parameters +- `other` **Coins** to add to `Coins` set + +#### Usage +```go +coins := // ... +otherCoins := // ... +coins.Add(otherCoins) +``` diff --git a/docs/reference/standard-library/std/testing.md b/docs/reference/standard-library/std/testing.md new file mode 100644 index 00000000000..8c9146c81a1 --- /dev/null +++ b/docs/reference/standard-library/std/testing.md @@ -0,0 +1,91 @@ +--- +id: testing +--- + +# Testing + +```go +func TestCurrentRealm() string +func TestSkipHeights(count int64) +func TestSetOrigCaller(addr Address) +func TestSetOrigPkgAddr(addr Address) +func TestSetOrigSend(sent, spent Coins) +func TestIssueCoins(addr Address, coins Coins) +``` + +## TestCurrentRealm +```go +func TestCurrentRealm() string +``` +Returns the current realm path. + +#### Usage +```go +currentRealmPath := std.TestCurrentRealm() +``` +--- + +## TestSkipHeights +```go +func TestSkipHeights(count int64) +``` +Modifies the block height variable by skipping **count** blocks. + +#### Usage +```go +std.TestSkipHeights(100) +``` +--- + +## TestSetOrigCaller +```go +func TestSetOrigCaller(addr Address) +``` +Sets the current caller of the transaction to **addr**. + +#### Usage +```go +std.TestSetOrigCaller("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") +``` +--- + +## TestSetOrigPkgAddr +```go +func TestSetOrigPkgAddr(addr Address) +``` +Sets the current realm/package address to **addr**. + +#### Usage +```go +std.TestSetOrigPkgAddr("g1ecely4gjy0yl6s9kt409ll330q9hk2lj9ls3ec") +``` +--- + +## TestSetOrigSend +```go +func TestSetOrigSend(sent, spent Coins) +``` +Sets the sent & spent coins for the current context. + +#### Usage +```go +std.TestSetOrigSend(sent, spent Coins) +``` +--- + +## TestIssueCoins +```go +func TestIssueCoins(addr Address, coins Coins) +``` +Issues testing context **coins** to **addr**. +#### Usage +```go +issue := std.Coins{{"coin1", 100}, {"coin2", 200}} +addr := "g1ecely4gjy0yl6s9kt409ll330q9hk2lj9ls3ec" +std.TestIssueCoins(addr, issue) +``` + + + + + diff --git a/docs/reference/tm2-js-client/Provider/json-rpc-provider.md b/docs/reference/tm2-js-client/Provider/json-rpc-provider.md new file mode 100644 index 00000000000..b7700e1d97c --- /dev/null +++ b/docs/reference/tm2-js-client/Provider/json-rpc-provider.md @@ -0,0 +1,22 @@ +--- +id: tm2-js-json-rpc-provider +--- + +# JSON-RPC Provider + +Provider based on JSON-RPC HTTP requests. + +### new JSONRPCProvider + +Creates a new instance of the JSON-RPC Provider + +#### Parameters + +* `baseURL` **string** the JSON-RPC URL of the node + +#### Usage + +```ts +new JSONRPCProvider('http://staging.gno.land:36657'); +// provider is created +``` diff --git a/docs/reference/tm2-js-client/Provider/provider.md b/docs/reference/tm2-js-client/Provider/provider.md new file mode 100644 index 00000000000..da6168eb6c2 --- /dev/null +++ b/docs/reference/tm2-js-client/Provider/provider.md @@ -0,0 +1,443 @@ +--- +id: tm2-js-provider +--- + +# Overview + +A `Provider` is an interface that abstracts the interaction with the Tendermint2 chain, making it easier for users to +communicate with it. Rather than requiring users to understand which endpoints are exposed, what their return types are, +and how they are parsed, the `Provider` abstraction handles all of this behind the scenes. It exposes useful API methods +that users can use and expects concrete types in return. + +Currently, the `tm2-js-client` package provides support for two Provider implementations: + +- [JSON-RPC Provider](json-rpc-provider.md): executes each call as a separate HTTP RPC call. +- [WS Provider](ws-provider.md): executes each call through an active WebSocket connection, which requires closing when + not needed anymore. + +## Account Methods + +### getBalance + +Fetches the denomination balance of the account + +#### Parameters + +* `address` **string** the bech32 address of the account +* `denomination` **string** the balance denomination (optional, default `ugnot`) +* `height` **number** the height for querying. + If omitted, the latest height is used (optional, default `0`) + +Returns **Promise** + +#### Usage + +```ts +await provider.getBalance('g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq', 'atom'); +// 100 +``` + +### getAccountSequence + +Fetches the account sequence + +#### Parameters + +* `address` **string** the bech32 address of the account +* `height` **number** the height for querying. + If omitted, the latest height is used. (optional, default `0`) + +Returns **Promise** + +#### Usage + +```ts +await provider.getAccountSequence('g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq'); +// 42 +``` + +### getAccountNumber + +Fetches the account number. Errors out if the account +is not initialized + +#### Parameters + +* `address` **string** the bech32 address of the account +* `height` **number** the height for querying. + If omitted, the latest height is used (optional, default `0`) + +Returns **Promise** + +#### Usage + +```ts +await provider.getAccountNumber('g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq'); +// 100 +``` + +## Block methods + +### getBlock + +Fetches the block at the specific height, if any + +#### Parameters + +* `height` **number** the height for querying + +Returns **Promise** + +#### Usage + +```ts +await provider.getBlock(1); +/* +{ + block_meta: { + block_id: { + hash: "TxHKEGxFm/4+D7gxOJdVUaR+xTDZzlPrCVXuVm7SqHw=", + parts: { + total: "1", + hash: "+dqI9oyngnnlKyno7y+RxCLEPA9FxWA/MmXyJ4uoJAY=" + } + }, + header: { + version: "v1.0.0-rc.0", + chain_id: "dev", + height: "1", + time: "2023-05-01T10:32:20.807541Z", + num_txs: "0", + total_txs: "0", + app_version: "", + last_block_id: { + hash: null, + parts: { + total: "0", + hash: null + } + }, + last_commit_hash: null, + data_hash: null, + validators_hash: "FnuBaDvDLg4FotGRcZAFvhLkEjkb+kNLaAZrAVhL5Aw=", + next_validators_hash: "FnuBaDvDLg4FotGRcZAFvhLkEjkb+kNLaAZrAVhL5Aw=", + consensus_hash: "uKhnXFmGUkxgQSJf17ogbYLNXDo3UEPwQvzddo4Vkuw=", + app_hash: null, + last_results_hash: null, + proposer_address: "g1vsqzyy9a4h9ah8cxzkaw09rpzy369mkl70lfdk" + } + }, + block: { + header: { + version: "v1.0.0-rc.0", + chain_id: "dev", + height: "1", + time: "2023-05-01T10:32:20.807541Z", + num_txs: "0", + total_txs: "0", + app_version: "", + last_block_id: { + hash: null, + parts: { + total: "0", + hash: null + } + }, + last_commit_hash: null, + data_hash: null, + validators_hash: "FnuBaDvDLg4FotGRcZAFvhLkEjkb+kNLaAZrAVhL5Aw=", + next_validators_hash: "FnuBaDvDLg4FotGRcZAFvhLkEjkb+kNLaAZrAVhL5Aw=", + consensus_hash: "uKhnXFmGUkxgQSJf17ogbYLNXDo3UEPwQvzddo4Vkuw=", + app_hash: null, + last_results_hash: null, + proposer_address: "g1vsqzyy9a4h9ah8cxzkaw09rpzy369mkl70lfdk" + }, + data: { + txs: null + }, + last_commit: { + block_id: { + hash: null, + parts: { + total: "0", + hash: null + } + }, + precommits: null + } + } +} +*/ +``` + +### getBlockResult + +Fetches the block at the specific height, if any + +#### Parameters + +* `height` **number** the height for querying + +Returns **Promise** + +#### Usage + +```ts +await provider.getBlockResult(1); +/* +{ + height: "1", + results: { + deliver_tx: null, + end_block: { + ResponseBase: { + Error: null, + Data: null, + Events: null, + Log: "", + Info: "" + }, + ValidatorUpdates: null, + ConsensusParams: null, + Events: null + }, + begin_block: { + ResponseBase: { + Error: null, + Data: null, + Events: null, + Log: "", + Info: "" + } + } + } +} +*/ +``` + +### getBlockNumber + +Fetches the latest block number from the chain + +Returns **Promise** + +#### Usage + +```ts +await provider.getBlockNumber(); +// 1300 +``` + +## Network methods + +### getNetwork + +Fetches the network information + +Returns **Promise** + +#### Usage + +```ts +await provider.getNetwork(); +/* +{ + listening: true, + listeners: [ + "Listener(@)" + ], + n_peers: "0", + peers: [] +} +*/ +``` + +### getConsensusParams + +Fetches the consensus params for the specific block height + +#### Parameters + +* `height` **number** the height for querying + +Returns **Promise** + +#### Usage + +```ts +await provider.getConsensusParams(1); +/* +{ + block_height: "1", + consensus_params: { + Block: { + MaxTxBytes: "1000000", + MaxDataBytes: "2000000", + MaxBlockBytes: "0", + MaxGas: "10000000", + TimeIotaMS: "100" + }, + Validator: { + PubKeyTypeURLs: [ + "/tm.PubKeyEd25519" + ] + } + } +} +*/ +``` + +### getStatus + +Fetches the current node status + +Returns **Promise** + +#### Usage + +```ts +await provider.getStatus(); +/* +{ + node_info: { + version_set: [ + { + Name: "abci", + Version: "v1.0.0-rc.0", + Optional: false + }, + { + Name: "app", + Version: "", + Optional: false + }, + { + Name: "bft", + Version: "v1.0.0-rc.0", + Optional: false + }, + { + Name: "blockchain", + Version: "v1.0.0-rc.0", + Optional: false + }, + { + Name: "p2p", + Version: "v1.0.0-rc.0", + Optional: false + } + ], + net_address: "g1z0wa6rspsshkm2k7jlqvnjs8jdt4kvg4e9j640@0.0.0.0:26656", + network: "dev", + software: "", + version: "v1.0.0-rc.0", + channels: "QCAhIiMw", + moniker: "voyager.lan", + other: { + tx_index: "off", + rpc_address: "tcp://127.0.0.1:26657" + } + }, + sync_info: { + latest_block_hash: "x5ewEBhf9+MGXbEFkUdOm3RsE40D+plUia2u0PuVfHs=", + latest_app_hash: "7dB/+EmqLqEX2RkH2Zx+GcFo8c2vTs2ttW8urYyyFT4=", + latest_block_height: "55", + latest_block_time: "2023-05-06T11:28:35.643575Z", + catching_up: false + }, + validator_info: { + address: "g1vsqzyy9a4h9ah8cxzkaw09rpzy369mkl70lfdk", + pub_key: { + "@type": "/tm.PubKeyEd25519", + value: "X8ZS1DYu1eJ3HYnZ0OWk+0GgCdI7zA++kgWiprWMs3w=" + }, + voting_power: "0" + } +} +*/ +``` + +### getGasPrice + +**NOTE: Not supported yet** + +Fetches the current (recommended) average gas price + +Returns **Promise** + +### estimateGas + +**NOTE: Not supported yet** + +Estimates the gas limit for the transaction + +#### Parameters + +* `tx` **Tx** the transaction that needs estimating + +Returns **Promise** + +## Transaction methods + +### sendTransaction + +Sends the transaction to the node for committing and returns the transaction hash. +The transaction needs to be signed beforehand. + +#### Parameters + +* `tx` **string** the base64-encoded signed transaction + +Returns **Promise** + +#### Usage + +```ts +await provider.sendTransaction('ZXhhbXBsZSBzaWduZWQgdHJhbnNhY3Rpb24'); +// "dHggaGFzaA==" +``` + +### waitForTransaction + +Waits for the transaction to be committed on the chain. +NOTE: This method will not take in the fromHeight parameter once +proper transaction indexing is added - the implementation should +simply try to fetch the transaction first to see if it's included in a block +before starting to wait for it; Until then, this method should be used +in the sequence: +get latest block -> send transaction -> waitForTransaction(block before send) + +#### Parameters + +* `hash` **string** The transaction hash +* `fromHeight` **number** The block height used to begin the search (optional, default `latest`) +* `timeout` **number** Optional wait timeout in MS (optional, default `15000`) + +Returns **Promise** + +#### Usage + +```ts +await provider.waitForTransaction('ZXhhbXBsZSBzaWduZWQgdHJhbnNhY3Rpb24'); +/* +{ + messages:[], // should be filled with the appropriate message type + fee:{ + gasWanted: "100", + gasFee: "1ugnot" + }, + signatures:[ + { + pubKey:[ + { + type: "/tm.PubKeySecp256k1" + value: "X8ZS1DYu1eJ3HYnZ0OWk+0GgCdI7zA++kgWiprWMs3w=" + } + ], + signature: "X8ZS1DYu1eJ3HYnZ0OWk+0GgCdI7zA++kgWiprWMs3w=" + } + ], + memo: "check out gno.land!" +} +*/ +``` diff --git a/docs/reference/tm2-js-client/Provider/utility.md b/docs/reference/tm2-js-client/Provider/utility.md new file mode 100644 index 00000000000..3f0181ccc39 --- /dev/null +++ b/docs/reference/tm2-js-client/Provider/utility.md @@ -0,0 +1,116 @@ +--- +id: tm2-js-utility +--- + +# Utility Helpers + +## Provider Helpers + +### extractBalanceFromResponse + +Extracts the specific balance denomination from the ABCI response + +#### Parameters + +* `abciData` **(string | null)** the base64-encoded ABCI data +* `denomination` **string** the required denomination + +### extractSequenceFromResponse + +Extracts the account sequence from the ABCI response + +#### Parameters + +* `abciData` **(string | null)** the base64-encoded ABCI data + +Returns **number** + +### extractAccountNumberFromResponse + +Extracts the account number from the ABCI response + +#### Parameters + +* `abciData` **(string | null)** the base64-encoded ABCI data + +Returns **number** + +### waitForTransaction + +Waits for the transaction to be committed to a block in the chain +of the specified provider. This helper does a search for incoming blocks +and checks if a transaction + +#### Parameters + +* `provider` **Provider** the provider instance +* `hash` **string** the base64-encoded hash of the transaction +* `fromHeight` **number** the starting height for the search. If omitted, it is the latest block in the chain ( + optional, default `latest`) +* `timeout` **number** the timeout in MS for the search (optional, default `15000`) + +Returns **Promise** + +## Request Helpers + +### newRequest + +Creates a new JSON-RPC 2.0 request + +#### Parameters + +* `method` **string** the requested method +* `params` **Array?** the requested params, if any + +Returns **RPCRequest** + +### newResponse + +Creates a new JSON-RPC 2.0 response + +#### Parameters + +* `result` **Result** the response result, if any +* `error` **RPCError** the response error, if any + +Returns **RPCResponse** + +### parseABCI + +Parses the base64 encoded ABCI JSON into a concrete type + +#### Parameters + +* `data` **string** the base64-encoded JSON + +Returns **Result** + +### stringToBase64 + +Converts a string into base64 representation + +#### Parameters + +* `str` **string** the raw string + +Returns **string** + +### base64ToUint8Array + +Converts a base64 string into a Uint8Array representation + +#### Parameters + +* `str` **string** the base64-encoded string + +Returns **Uint8Array** + +### uint8ArrayToBase64 + +Converts a Uint8Array into base64 representation + +#### Parameters + +* `data` **Uint8Array** the Uint8Array to be encoded + +Returns **string** diff --git a/docs/reference/tm2-js-client/Provider/ws-provider.md b/docs/reference/tm2-js-client/Provider/ws-provider.md new file mode 100644 index 00000000000..ef91f45d4e2 --- /dev/null +++ b/docs/reference/tm2-js-client/Provider/ws-provider.md @@ -0,0 +1,95 @@ +--- +id: tm2-js-ws-provider +--- + +# WebSocket Provider + +Provider based on WS JSON-RPC requests. + +### new WSProvider + +Creates a new instance of the WebSocket Provider + +#### Parameters + +* `baseURL` **string** the WS URL of the node +* `requestTimeout` **number** the timeout for the WS request (in MS) + +#### Usage + +```ts +new WSProvider('ws://staging.gno.land:36657/ws'); +// provider with WS connection is created +``` + +### closeConnection + +Closes the WS connection. Required when done working +with the WS provider + +#### Usage + +```ts +const wsProvider = new WSProvider('ws://staging.gno.land:36657/ws'); + +wsProvider.closeConnection(); +// WS connection is now closed +``` + +### sendRequest + +Sends a request to the WS connection, and resolves +upon receiving the response + +#### Parameters + +* `request` **RPCRequest** the RPC request + +Returns **Promise>** + +#### Usage + +```ts +const request: RPCRequest = // ... + +const wsProvider = new WSProvider('ws://staging.gno.land:36657/ws'); + +wsProvider.sendRequest(request); +// request is sent over the open WS connection +``` + +### parseResponse + +Parses the result from the response + +#### Parameters + +* `response` **RPCResponse** the response to be parsed + +Returns **Result** + +#### Usage + +```ts +const response: RPCResponse = // ... + +const wsProvider = new WSProvider('ws://staging.gno.land:36657/ws'); + +wsProvider.parseResponse(response); +// response is parsed +``` + +### waitForOpenConnection + +Waits for the WS connection to be established + +Returns **Promise** + +#### Usage + +```ts +const wsProvider = new WSProvider('ws://staging.gno.land:36657/ws'); + +await wsProvider.waitForOpenConnection() +// status of the connection is: CONNECTED +``` diff --git a/docs/reference/tm2-js-client/Signer/key.md b/docs/reference/tm2-js-client/Signer/key.md new file mode 100644 index 00000000000..3c40fa427d2 --- /dev/null +++ b/docs/reference/tm2-js-client/Signer/key.md @@ -0,0 +1,30 @@ +--- +id: tm2-js-key +--- + +# Key Signer + +Private key-based signer instance + +### new KeySigner + +Creates a new instance of the private-key KeySigner + +#### Parameters + +* `privateKey` **Uint8Array** the raw Secp256k1 private key +* `publicKey` **Uint8Array** the raw Secp256k1 public key +* `addressPrefix` **string** the address prefix + +#### Usage + +```ts +// Generate the public / private key from somewhere +const {publicKey, privateKey} = await generateKeyPair( + entropyToMnemonic(generateEntropy()), + index ? index : 0 +); + +new KeySigner(privateKey, publicKey); +// new Secp256k1 key signer created +``` diff --git a/docs/reference/tm2-js-client/Signer/ledger.md b/docs/reference/tm2-js-client/Signer/ledger.md new file mode 100644 index 00000000000..71a7682c3e3 --- /dev/null +++ b/docs/reference/tm2-js-client/Signer/ledger.md @@ -0,0 +1,27 @@ +--- +id: tm2-js-ledger +--- + +# Ledger Signer + +Ledger device-based signer instance + +### new LedgerSigner + +Creates a new instance of the Ledger device signer, using the provided `LedgerConnector` + +#### Parameters + +* `connector` **LedgerConnector** the Ledger connector +* `accountIndex` **number** the desired account index +* `addressPrefix` **string** the address prefix + +#### Usage + +```ts +const accountIndex: number = 10 // for ex. 10th account in the derivation +const connector: LedgerConnector = // ... + +new LedgerSigner(connector, accountIndex); +// new Ledger device signer created +``` diff --git a/docs/reference/tm2-js-client/Signer/signer.md b/docs/reference/tm2-js-client/Signer/signer.md new file mode 100644 index 00000000000..fd3945cc2ae --- /dev/null +++ b/docs/reference/tm2-js-client/Signer/signer.md @@ -0,0 +1,96 @@ +--- +id: tm2-js-signer +--- + +# Overview + +A `Signer` is an interface that abstracts the interaction with a single Secp256k1 key pair. It exposes methods for +signing data, verifying signatures, and getting metadata associated with the key pair, such as the address. + +Currently, the `tm2-js-client` package provides support for two `Signer` implementations: + +- [Key](key.md): a signer that is based on a raw Secp256k1 key pair. +- [Ledger](ledger.md): a signer that is based on a Ledger device, with all interaction flowing through the user's + device. + +## API + +### getAddress + +Returns the address associated with the signer's public key + +Returns **Promise** + +#### Usage + +```ts +await signer.getAddress(); +// "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq" +``` + +### getPublicKey + +Returns the signer's Secp256k1-compressed public key + +Returns **Promise** + +#### Usage + +```ts +await signer.getPublicKey(); +// +``` + +### getPrivateKey + +Returns the signer's actual raw private key + +Returns **Promise** + +#### Usage + +```ts +await signer.getPrivateKey(); +// +``` + +### signData + +Generates a data signature for arbitrary input + +#### Parameters + +* `data` **Uint8Array** the data to be signed + +Returns **Promise** + +#### Usage + +```ts +const dataToSign: Uint8Array = // ... + + await signer.signData(dataToSign); +// +``` + +### verifySignature + +Verifies if the signature matches the provided raw data + +#### Parameters + +* `data` **Uint8Array** the raw data (not-hashed) +* `signature` **Uint8Array** the hashed-data signature + +Returns **Promise** + +#### Usage + +```ts +const signedData: Uint8Array = // ... +const rawData: Uint8Array = // ... + + await signer.verifySignature(rawData, signedData); +// +``` + diff --git a/docs/reference/tm2-js-client/getting-started.md b/docs/reference/tm2-js-client/getting-started.md new file mode 100644 index 00000000000..4a20ce2684f --- /dev/null +++ b/docs/reference/tm2-js-client/getting-started.md @@ -0,0 +1,65 @@ +--- +id: tm2-js-getting-started +--- + +# Getting Started + +[@gnolang/tm2-js-client](https://github.com/gnolang/tm2-js-client) is a JavaScript/TypeScript client implementation for +Tendermint2-based chains. It is designed to make it +easy for developers to interact with TM2 chains, providing a simplified API for account and transaction management. By +doing all the heavy lifting behind the scenes, `@gnolang/tm2-js-client` enables developers to focus on what really +matters - +building their dApps. + +## Key Features + +- JSON-RPC and WebSocket client support via a `Provider` +- Simple account and transaction management API with a `Wallet` +- Designed for easy extension for custom TM2 chains, such as [Gnoland](https://gno.land) + +## Installation + +To install `@gnolang/tm2-js-client`, use your preferred package manager: + +```bash +yarn add @gnolang/tm2-js-client +``` + +```bash +npm install @gnolang/tm2-js-client +``` + +## Common Terminology + +### Provider + +A `Provider` is an interface that abstracts the interaction with the Tendermint2 chain, making it easier for users to +communicate with it. Rather than requiring users to understand which endpoints are exposed, what their return types are, +and how they are parsed, the `Provider` abstraction handles all of this behind the scenes. It exposes useful API methods +that users can use and expects concrete types in return. + +Currently, the `@gnolang/tm2-js-client` package provides support for two Provider implementations: + +- `JSON-RPC Provider`: executes each call as a separate HTTP RPC call. +- `WS Provider`: executes each call through an active WebSocket connection, which requires closing when not needed + anymore. + +### Signer + +A `Signer` is an interface that abstracts the interaction with a single Secp256k1 key pair. It exposes methods for +signing data, verifying signatures, and getting metadata associated with the key pair, such as the address. + +Currently, the `@gnolang/tm2-js-client` package provides support for two `Signer` implementations: + +- `Key`: a signer that is based on a raw Secp256k1 key pair. +- `Ledger`: a signer that is based on a Ledger device, with all interaction flowing through the user's device. + +### Wallet + +A `Wallet` is a user-facing API that is used to interact with an account. A `Wallet` instance is tied to a single key +pair and essentially wraps the given `Provider` for that specific account. + +A wallet can be generated from a randomly generated seed, a private key, or instantiated using a Ledger device. + +Using the `Wallet`, users can easily interact with the Tendermint2 chain using their account without having to worry +about account management. diff --git a/docs/reference/tm2-js-client/wallet.md b/docs/reference/tm2-js-client/wallet.md new file mode 100644 index 00000000000..8a6943dfc9a --- /dev/null +++ b/docs/reference/tm2-js-client/wallet.md @@ -0,0 +1,275 @@ +--- +id: tm2-js-wallet +--- + +# Wallet + +A `Wallet` is a user-facing API that is used to interact with an account. A `Wallet` instance is tied to a single key +pair and essentially wraps the given `Provider` for that specific account. + +A wallet can be generated from a randomly generated seed, a private key, or instantiated using a Ledger device. + +Using the `Wallet`, users can easily interact with the Tendermint2 chain using their account without having to worry +about account management. + +## Initialization + +### createRandom + +Generates a private key-based wallet, using a random seed + +#### Parameters + +* `options?` **AccountWalletOption** the account options + +Returns **Promise** + +#### Usage + +```ts +const wallet: Wallet = await Wallet.createRandom(); +// random wallet created +``` + +### fromMnemonic + +Generates a bip39 mnemonic-based wallet + +#### Parameters + +* `mnemonic` **string** the bip39 mnemonic +* `options?` **CreateWalletOptions** the wallet generation options + +Returns **Promise** + +#### Usage + +```ts +const mnemonic: string = // ... +const wallet: Wallet = await Wallet.fromMnemonic(mnemonic); +// wallet created from mnemonic +``` + +### fromPrivateKey + +Generates a private key-based wallet + +#### Parameters + +* `privateKey` **string** the private key +* `options?` **AccountWalletOption** the wallet generation options + +Returns **Promise** + +#### Usage + +```ts +// Generate the private key from somewhere +const {publicKey, privateKey} = await generateKeyPair( + entropyToMnemonic(generateEntropy()), + index ? index : 0 +); + +const wallet: Wallet = await Wallet.fromPrivateKey(privateKey); +// wallet created from private key +``` + +### fromLedger + +Creates a Ledger-based wallet + +#### Parameters + +* `connector` **LedgerConnector** the Ledger device connector +* `options?` **CreateWalletOptions** the wallet generation options + +Returns **Wallet** + +#### Usage + +```ts +const connector: LedgerConnector = // ... + +const wallet: Wallet = await Wallet.fromLedger(connector); +// wallet created from Ledger device connection +``` + +## Provider Methods + +### connect + +Connects the wallet to the specified Provider + +#### Parameters + +* `provider` **Provider** the active Provider, if any + +#### Usage + +```ts +const provider: Provider = // ... + + wallet.connect(provider); +// Provider connected to Wallet +``` + +### getProvider + +Returns the connected provider, if any + +Returns **Provider** + +#### Usage + +```ts +wallet.getProvider(); +// connected provider, if any (undefined if not) +``` + +## Account Methods + +### getAddress + +Fetches the address associated with the wallet + +Returns **Promise** + +#### Usage + +```ts +await wallet.getAddress(); +// "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq" +``` + +### getSequence + +Fetches the account sequence for the wallet + +#### Parameters + +* `height` **number** the block height (optional, default `latest`) + +#### Usage + +```ts +await wallet.getSequence(); +// 42 +``` + +Returns **Promise** + +### getAccountNumber + +Fetches the account number for the wallet. Errors out if the +account is not initialized + +#### Parameters + +* `height` **number** the block height (optional, default `latest`) + +Returns **Promise** + +#### Usage + +```ts +await wallet.getAccountNumber(); +// 10 +``` + +### getBalance + +Fetches the account balance for the specific denomination + +#### Parameters + +* `denomination` **string** the fund denomination (optional, default `ugnot`) + +Returns **Promise** + +#### Usage + +```ts +await wallet.getBalance('ugnot'); +// 5000 +``` + +### getGasPrice + +Fetches the current (recommended) average gas price + +Returns **Promise** + +#### Usage + +```ts +await wallet.getGasPrice(); +// 63000 +``` + +### estimateGas + +Estimates the gas limit for the transaction + +#### Parameters + +* `tx` **Tx** the transaction that needs estimating + +Returns **Promise** + +#### Usage + +```ts +const tx: Tx = // ... + + await wallet.estimateGas(tx); +// 120000 +``` + +### signTransaction + +Generates a transaction signature, and appends it to the transaction + +#### Parameters + +* `tx` **Tx** the transaction to be signed + +Returns **Promise** + +#### Usage + +```ts +const tx: Tx = // ... + + await wallet.signTransaction(tx); +// transaction with appended signature +``` + +### sendTransaction + +Signs and sends the transaction. Returns the transaction hash (base-64) + +#### Parameters + +* `tx` **Tx** the unsigned transaction + +Returns **Promise** + +#### Usage + +```ts +await wallet.sendTransaction(tx); +// returns the transaction hash +``` + +### getSigner + +Returns the associated signer + +Returns **Signer** + +#### Usage + +```ts +wallet.getSigner(tx); +// Signer instance +``` diff --git a/docs/tasks/001_describe_in_your_words.md b/docs/tasks/001_describe_in_your_words.md deleted file mode 100644 index f9de67ecd25..00000000000 --- a/docs/tasks/001_describe_in_your_words.md +++ /dev/null @@ -1,3 +0,0 @@ -Describe in your words, on your favorite medium, why you are interested in gno.land and gnolang. - -Reply here with a URL link to your written piece as a comment, for rewards. diff --git a/docs/testing-guide.md b/docs/testing-guide.md new file mode 100644 index 00000000000..6019f238f01 --- /dev/null +++ b/docs/testing-guide.md @@ -0,0 +1,86 @@ +# Gnoland Testing Guide + +This guide provides an overview of our testing practices and conventions. While most of our testing aligns with typical Go practices, there are exceptions and specifics you should be aware of. + +## Standard Package Testing + +For most packages, tests are written and executed in the standard Go manner: + +- Tests are located alongside the code they test. +- The `go test` command can be used to execute tests. + +However, as mentioned earlier, there are some exceptions. In the following sections, we will explore our specialized tests and how to work with them. + +## Gno Filetests + +**Location:** `gnovm/test/files` + +These are our custom file-based tests tailored specifically for this project. + +**Execution:** + +From the gnovm directory, There are two main commands to run Gno filetests: + +1. To test native files, use: +``` +make _test.gnolang.native +``` + +2. To test standard libraries, use: +``` +make _test.gnolang.stdlibs +``` + +**Golden Files Update:** + +Golden files are references for expected outputs. Sometimes, after certain updates, these need to be synchronized. To do so: + +1. For native tests: +``` +make _test.gnolang.native.sync +``` + +2. For standard library tests: +``` +make _test.gnolang.stdlibs.sync +``` + +## Integration Tests + +**Location:** `gno.land/**/testdata` + +From the gno.land directory, Integration tests are designed to ensure different parts of the project work cohesively. Specifically: + +1. **InMemory Node Integration Testing:** + Found in `gno.land/cmd/gnoland/testdata`, these are dedicated to running integration tests against a genuine `gnoland` node. + +2. **Integration Features Testing:** + Located in `gno.land/pkg/integration/testdata`, these tests target integrations specific commands. + +These integration tests utilize the `testscript` package and follow the `txtar` file specifications. + +**Documentation:** + +- For general `testscript` package documentation, refer to: [testscript documentation](https://github.com/rogpeppe/go-internal/blob/v1.11.0/testscript/doc.go) + +- For more specific details about our integration tests, consult our extended documentation: [gnoland integration documentation](https://github.com/gnolang/gno/blob/master/gno.land/pkg/integration/doc.go) + +**Execution:** + +To run the integration tests (alongside other packages): + +``` +make _test.pkgs +``` + +**Golden Files Update within txtar:** + +For tests utilizing the `cmp` command inside `txtar` files, golden files can be synchronized using: + +``` +make _test.pkgs.sync +``` + +--- + +As the project evolves, this guide might be updated. diff --git a/examples/Makefile b/examples/Makefile index 5075df198ac..56ad4b30b42 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -19,7 +19,7 @@ build: precompile .PHONY: test test: - go run ../gnovm/cmd/gno test --verbose . + go run ../gnovm/cmd/gno test --verbose ./... .PHONY: lint lint: @@ -27,7 +27,7 @@ lint: .PHONY: test.sync test.sync: - go run ../gnovm/cmd/gno test --verbose --update-golden-tests . + go run ../gnovm/cmd/gno test --verbose --update-golden-tests ./... .PHONY: clean clean: @@ -37,3 +37,12 @@ clean: GOFMT_FLAGS ?= -w fmt: go run -modfile ../misc/devdeps/go.mod mvdan.cc/gofumpt $(GOFMT_FLAGS) `find . -name "*.gno"` + +.PHONY: imports +GOIMPORTS_FLAGS ?= -w +imports: + $(rundep) golang.org/x/tools/cmd/goimports $(GOIMPORTS_FLAGS) . + +.PHONY: tidy +tidy: + find . -name "gno.mod" -execdir go run github.com/gnolang/gno/gnovm/cmd/gno mod tidy \; diff --git a/examples/README.md b/examples/README.md index d0fd8d02952..b112e564d13 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,15 +1,21 @@ # Gnolang examples -Folder contains Gnolang realms and libraries demos. -Share contracts here to improve engine testing, although it's not required. -Consider separate repository for contracts, but this may limit experience due to ongoing gnomod support work. -Main repository can't reference separate code, causing potential development issues. +This folder showcases Gnolang realms and library demos. These examples not only aid in engine testing but also provide a glimpse into the potential of Gnolang's capabilities. + +While sharing contracts here can enhance engine testing, it's not mandatory. If considering a separate repository for contracts, be aware that this might restrict the experience due to the continuous efforts around `gno mod` support. A key point to note is that the main repository cannot reference separate code, which might pose developmental challenges. + +## Personal Realms & Shared Content + +**Prioritizing Shared Content:** As we expand our examples and use-cases, it's essential to prioritize shared content that benefits the broader community. These examples serve as a foundation and reference for all users. + +**Personal Realms Inclusion:** We're open to personal realms, but they must exemplify best practices and inspire others. To maintain our repository's organization, we may decline some realms. If so, consider uploading onchain and keeping source code separately. For higher acceptance odds, offer useful or original examples. + +**Recommended Approach:** +- Use `r/demo` and `p/demo` for generic examples and components that can be imported by others. These are meant to be easily referenced and utilized by the community. +- Personal realms are welcomed if they are easily maintainable with the Continuous Integration (CI) system. If a personal realm becomes cumbersome to maintain or doesn't align with the CI's checks, it might be relocated to a less prominent location or even removed. ## Usage -Our recommendation is to use the [gno](../gnovm/cmd/gno) utility to develop contracts locally before publishing them on-chain. -This approach offers a faster and streamlined workflow, along with additional debugging features. -Simply fork or create new contracts and refer to the Makefile. -Once everything looks good locally, you can then publish it on a localnet or testnet. +Our recommendation is to use the [gno](../gnovm/cmd/gno) utility to develop contracts locally before publishing them on-chain. This approach offers a faster and streamlined workflow, along with additional debugging features. Simply fork or create new contracts and refer to the Makefile. Once everything looks good locally, you can then publish it on a localnet or testnet. -See [`awesome-gno` tutorials](https://github.com/gnolang/awesome-gno#tutorials). +For further guidance and insights, please refer to the [`awesome-gno` tutorials](https://github.com/gnolang/awesome-gno#tutorials). diff --git a/examples/gno.land/p/demo/acl/gno.mod b/examples/gno.land/p/demo/acl/gno.mod index 2aabe3a5645..176cde637bd 100644 --- a/examples/gno.land/p/demo/acl/gno.mod +++ b/examples/gno.land/p/demo/acl/gno.mod @@ -1,6 +1,6 @@ module gno.land/p/demo/acl require ( - "gno.land/p/demo/avl" v0.0.0-latest - "gno.land/p/demo/testutils" v0.0.0-latest + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest ) diff --git a/examples/gno.land/p/demo/avl/z_0_filetest.gno b/examples/gno.land/p/demo/avl/z_0_filetest.gno index 814e19d6d49..e91788ac8eb 100644 --- a/examples/gno.land/p/demo/avl/z_0_filetest.gno +++ b/examples/gno.land/p/demo/avl/z_0_filetest.gno @@ -267,6 +267,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -301,6 +303,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/examples/gno.land/p/demo/avl/z_1_filetest.gno b/examples/gno.land/p/demo/avl/z_1_filetest.gno index 410e9e93601..cdd56a5ad89 100644 --- a/examples/gno.land/p/demo/avl/z_1_filetest.gno +++ b/examples/gno.land/p/demo/avl/z_1_filetest.gno @@ -291,6 +291,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -325,6 +327,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/examples/gno.land/p/demo/blog/blog.gno b/examples/gno.land/p/demo/blog/blog.gno index 62103b52885..1cf37b7ad3a 100644 --- a/examples/gno.land/p/demo/blog/blog.gno +++ b/examples/gno.land/p/demo/blog/blog.gno @@ -13,13 +13,28 @@ import ( ) type Blog struct { - Title string - Prefix string // i.e. r/gnoland/blog: - Posts avl.Tree // slug -> Post + Title string + Prefix string // i.e. r/gnoland/blog: + Posts avl.Tree // slug -> Post + NoBreadcrumb bool +} + +func (b Blog) RenderLastPostsWidget(limit int) string { + output := "" + i := 0 + b.Posts.Iterate("", "", func(key string, value interface{}) bool { + p := value.(*Post) + output += ufmt.Sprintf("- [%s](%s)\n", p.Title, p.URL()) + i++ + return i >= limit + }) + return output } func (b Blog) RenderHome(res *mux.ResponseWriter, req *mux.Request) { - res.Write(breadcrumb([]string{b.Title})) + if !b.NoBreadcrumb { + res.Write(breadcrumb([]string{b.Title})) + } if b.Posts.Size() == 0 { res.Write("No posts.") @@ -47,12 +62,14 @@ func (b Blog) RenderPost(res *mux.ResponseWriter, req *mux.Request) { } p := post.(*Post) - breadStr := breadcrumb([]string{ - ufmt.Sprintf("[%s](%s)", b.Title, b.Prefix), - "p", - p.Title, - }) - res.Write(breadStr) + if !b.NoBreadcrumb { + breadStr := breadcrumb([]string{ + ufmt.Sprintf("[%s](%s)", b.Title, b.Prefix), + "p", + p.Title, + }) + res.Write(breadStr) + } // output += ufmt.Sprintf("## [%s](%s)\n", p.Title, p.URL()) res.Write(p.Body + "\n\n") @@ -75,12 +92,14 @@ func (b Blog) RenderTag(res *mux.ResponseWriter, req *mux.Request) { return } - breadStr := breadcrumb([]string{ - ufmt.Sprintf("[%s](%s)", b.Title, b.Prefix), - "t", - slug, - }) - res.Write(breadStr) + if !b.NoBreadcrumb { + breadStr := breadcrumb([]string{ + ufmt.Sprintf("[%s](%s)", b.Title, b.Prefix), + "t", + slug, + }) + res.Write(breadStr) + } nb := 0 b.Posts.Iterate("", "", func(key string, value interface{}) bool { diff --git a/examples/gno.land/p/demo/blog/gno.mod b/examples/gno.land/p/demo/blog/gno.mod index c8437af1732..65f58e7a0f6 100644 --- a/examples/gno.land/p/demo/blog/gno.mod +++ b/examples/gno.land/p/demo/blog/gno.mod @@ -1,7 +1,7 @@ module gno.land/p/demo/blog require ( - "gno.land/p/demo/avl" v0.0.0-latest - "gno.land/p/demo/ufmt" v0.0.0-latest - "gno.land/p/demo/mux" v0.0.0-latest + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/mux v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest ) diff --git a/examples/gno.land/p/demo/dom/gno.mod b/examples/gno.land/p/demo/dom/gno.mod index e7d0e5a9e75..83ca827cf66 100644 --- a/examples/gno.land/p/demo/dom/gno.mod +++ b/examples/gno.land/p/demo/dom/gno.mod @@ -1,5 +1,3 @@ module gno.land/p/demo/dom -require ( - "gno.land/p/demo/avl" v0.0.0-latest -) +require gno.land/p/demo/avl v0.0.0-latest diff --git a/examples/gno.land/p/demo/flow/gno.mod b/examples/gno.land/p/demo/flow/gno.mod index 4a4d4fb4d82..5adddbfe021 100644 --- a/examples/gno.land/p/demo/flow/gno.mod +++ b/examples/gno.land/p/demo/flow/gno.mod @@ -1 +1 @@ -module "gno.land/p/demo/flow" +module gno.land/p/demo/flow diff --git a/examples/gno.land/p/demo/gnode/gno.mod b/examples/gno.land/p/demo/gnode/gno.mod index e922821f7fd..a93c2051830 100644 --- a/examples/gno.land/p/demo/gnode/gno.mod +++ b/examples/gno.land/p/demo/gnode/gno.mod @@ -1 +1 @@ -module "gno.land/p/demo/gnode" +module gno.land/p/demo/gnode diff --git a/examples/gno.land/p/demo/grc/exts/vault/gno.mod b/examples/gno.land/p/demo/grc/exts/vault/gno.mod index 8b4d4524366..2720bf09d95 100644 --- a/examples/gno.land/p/demo/grc/exts/vault/gno.mod +++ b/examples/gno.land/p/demo/grc/exts/vault/gno.mod @@ -1,6 +1,6 @@ module gno.land/p/demo/grc/exts/vault require ( - "gno.land/p/demo/avl" v0.0.0-latest - "gno.land/p/demo/grc/grc20" v0.0.0-latest + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/grc/grc20 v0.0.0-latest ) diff --git a/examples/gno.land/p/demo/grc/grc1155/gno.mod b/examples/gno.land/p/demo/grc/grc1155/gno.mod index 33a8e55be71..0b2b85d8e86 100644 --- a/examples/gno.land/p/demo/grc/grc1155/gno.mod +++ b/examples/gno.land/p/demo/grc/grc1155/gno.mod @@ -1,7 +1,7 @@ module gno.land/p/demo/grc/grc1155 require ( - "gno.land/p/demo/avl" v0.0.0-latest - "gno.land/r/demo/users" v0.0.0-latest - "gno.land/p/demo/ufmt" v0.0.0-latest + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest + gno.land/r/demo/users v0.0.0-latest ) diff --git a/examples/gno.land/p/demo/grc/grc1155/util.gno b/examples/gno.land/p/demo/grc/grc1155/util.gno index 72a5d73561c..2c6452a1066 100644 --- a/examples/gno.land/p/demo/grc/grc1155/util.gno +++ b/examples/gno.land/p/demo/grc/grc1155/util.gno @@ -7,7 +7,7 @@ import ( const zeroAddress std.Address = "" func isValidAddress(addr std.Address) bool { - if addr.String() == "" { + if !addr.IsValid() { return false } return true diff --git a/examples/gno.land/p/demo/grc/grc20/gno.mod b/examples/gno.land/p/demo/grc/grc20/gno.mod index 5e6e13f834c..fd80766a956 100644 --- a/examples/gno.land/p/demo/grc/grc20/gno.mod +++ b/examples/gno.land/p/demo/grc/grc20/gno.mod @@ -1,7 +1,7 @@ module gno.land/p/demo/grc/grc20 require ( - "gno.land/p/demo/avl" v0.0.0-latest - "gno.land/p/demo/ufmt" v0.0.0-latest - "gno.land/p/demo/grc/exts" v0.0.0-latest + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/grc/exts v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest ) diff --git a/examples/gno.land/p/demo/grc/grc20/util.gno b/examples/gno.land/p/demo/grc/grc20/util.gno index a70edf421ba..2892b036bbd 100644 --- a/examples/gno.land/p/demo/grc/grc20/util.gno +++ b/examples/gno.land/p/demo/grc/grc20/util.gno @@ -5,7 +5,7 @@ import "std" const zeroAddress = std.Address("") func checkIsValidAddress(addr std.Address) error { - if addr.String() == "" { + if !addr.IsValid() { return ErrInvalidAddress } return nil diff --git a/examples/gno.land/p/demo/grc/grc721/basic_nft.gno b/examples/gno.land/p/demo/grc/grc721/basic_nft.gno index b707527c6a4..bec7338db42 100644 --- a/examples/gno.land/p/demo/grc/grc721/basic_nft.gno +++ b/examples/gno.land/p/demo/grc/grc721/basic_nft.gno @@ -69,6 +69,25 @@ func (s *basicNFT) TokenURI(tid TokenID) (string, error) { return uri.(string), nil } +func (s *basicNFT) SetTokenURI(tid TokenID, tURI TokenURI) (bool, error) { + // check for invalid TokenID + if !s.exists(tid) { + return false, ErrInvalidTokenId + } + + // check for the right owner + owner, err := s.OwnerOf(tid) + if err != nil { + return false, err + } + caller := std.PrevRealm().Addr() + if caller != owner { + return false, ErrCallerIsNotOwner + } + s.tokenURIs.Set(string(tid), string(tURI)) + return true, nil +} + // IsApprovedForAll returns true if operator is approved for all by the owner. // Otherwise, returns false func (s *basicNFT) IsApprovedForAll(owner, operator std.Address) bool { diff --git a/examples/gno.land/p/demo/grc/grc721/basic_nft_test.gno b/examples/gno.land/p/demo/grc/grc721/basic_nft_test.gno index 7ad378af2f7..925f1fca44b 100644 --- a/examples/gno.land/p/demo/grc/grc721/basic_nft_test.gno +++ b/examples/gno.land/p/demo/grc/grc721/basic_nft_test.gno @@ -4,6 +4,7 @@ import ( "std" "testing" + "gno.land/p/demo/testutils" "gno.land/r/demo/users" ) @@ -367,3 +368,47 @@ func TestBurn(t *testing.T) { t.Errorf("should result in error") } } + +func TestSetTokenURI(t *testing.T) { + dummy := NewBasicNFT(dummyNFTName, dummyNFTSymbol) + if dummy == nil { + t.Errorf("should not be nil") + } + + addr1 := users.AddressOrName("g1var589z07ppjsjd24ukm4uguzwdt0tw7g47cgm") + addr2 := users.AddressOrName("g1us8428u2a5satrlxzagqqa5m6vmuze025anjlj") + tokenURI := "http://example.com/token" + + std.TestSetOrigCaller(std.Address(addr1)) // addr1 + + dummy.mint(addr1.Resolve(), TokenID("1")) + _, derr := dummy.SetTokenURI(TokenID("1"), TokenURI(tokenURI)) + + if derr != nil { + t.Errorf("Should not result in error ", derr.Error()) + } + + // Test case: Invalid token ID + _, err := dummy.SetTokenURI(TokenID("3"), TokenURI(tokenURI)) + if err != ErrInvalidTokenId { + t.Errorf("Expected error %v, got %v", ErrInvalidTokenId, err) + } + + std.TestSetOrigCaller(std.Address(addr2)) // addr2 + + _, cerr := dummy.SetTokenURI(TokenID("1"), TokenURI(tokenURI)) // addr2 trying to set URI for token 1 + if cerr != ErrCallerIsNotOwner { + t.Errorf("Expected error %v, got %v", ErrCallerIsNotOwner, err) + } + + // Test case: Retrieving TokenURI + std.TestSetOrigCaller(std.Address(addr1)) // addr1 + + dummyTokenURI, err := dummy.TokenURI(TokenID("1")) + if err != nil { + t.Errorf("TokenURI error: %v, ", err.Error()) + } + if dummyTokenURI != tokenURI { + t.Errorf("Expected URI %v, got %v", tokenURI, dummyTokenURI) + } +} diff --git a/examples/gno.land/p/demo/grc/grc721/gno.mod b/examples/gno.land/p/demo/grc/grc721/gno.mod index 229fc3f739c..952e0cb8ee4 100644 --- a/examples/gno.land/p/demo/grc/grc721/gno.mod +++ b/examples/gno.land/p/demo/grc/grc721/gno.mod @@ -1,7 +1,8 @@ module gno.land/p/demo/grc/grc721 require ( - "gno.land/p/demo/avl" v0.0.0-latest - "gno.land/r/demo/users" v0.0.0-latest - "gno.land/p/demo/ufmt" v0.0.0-latest + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest + gno.land/r/demo/users v0.0.0-latest ) diff --git a/examples/gno.land/p/demo/grc/grc721/igrc721.gno b/examples/gno.land/p/demo/grc/grc721/igrc721.gno index d60308e11a1..387547a7e26 100644 --- a/examples/gno.land/p/demo/grc/grc721/igrc721.gno +++ b/examples/gno.land/p/demo/grc/grc721/igrc721.gno @@ -5,6 +5,7 @@ import "std" type IGRC721 interface { BalanceOf(owner std.Address) (uint64, error) OwnerOf(tid TokenID) (std.Address, error) + SetTokenURI(tid TokenID, tURI TokenURI) (bool, error) SafeTransferFrom(from, to std.Address, tid TokenID) error TransferFrom(from, to std.Address, tid TokenID) error Approve(approved std.Address, tid TokenID) error @@ -13,7 +14,10 @@ type IGRC721 interface { IsApprovedForAll(owner, operator std.Address) bool } -type TokenID string +type ( + TokenID string + TokenURI string +) type TransferEvent struct { From std.Address diff --git a/examples/gno.land/p/demo/grc/grc721/util.gno b/examples/gno.land/p/demo/grc/grc721/util.gno index f82ba98194a..bb6bf24d984 100644 --- a/examples/gno.land/p/demo/grc/grc721/util.gno +++ b/examples/gno.land/p/demo/grc/grc721/util.gno @@ -7,7 +7,7 @@ import ( var zeroAddress = std.Address("") func isValidAddress(addr std.Address) error { - if addr.String() == "" { + if !addr.IsValid() { return ErrInvalidAddress } return nil diff --git a/examples/gno.land/p/demo/grc/grc777/gno.mod b/examples/gno.land/p/demo/grc/grc777/gno.mod index 2fddce3f8f8..9fbf2f2b7cd 100644 --- a/examples/gno.land/p/demo/grc/grc777/gno.mod +++ b/examples/gno.land/p/demo/grc/grc777/gno.mod @@ -1,5 +1,3 @@ module gno.land/p/demo/grc/grc777 -require ( - "gno.land/p/demo/grc/exts" v0.0.0-latest -) +require gno.land/p/demo/grc/exts v0.0.0-latest diff --git a/examples/gno.land/p/demo/groups/gno.mod b/examples/gno.land/p/demo/groups/gno.mod index b52ad8b05b1..0e9f7cf2a7c 100644 --- a/examples/gno.land/p/demo/groups/gno.mod +++ b/examples/gno.land/p/demo/groups/gno.mod @@ -1,7 +1,6 @@ module gno.land/p/demo/groups require ( - "gno.land/r/demo/boards" v0.0.0-latest - "gno.land/p/demo/maths" v0.0.0-latest - "gno.land/p/demo/testutils" v0.0.0-latest + gno.land/p/demo/maths v0.0.0-latest + gno.land/r/demo/boards v0.0.0-latest ) diff --git a/examples/gno.land/p/demo/math_eval/int32/gno.mod b/examples/gno.land/p/demo/math_eval/int32/gno.mod index 9a1e634447c..de57497a699 100644 --- a/examples/gno.land/p/demo/math_eval/int32/gno.mod +++ b/examples/gno.land/p/demo/math_eval/int32/gno.mod @@ -1,5 +1,3 @@ module gno.land/p/demo/math_eval/int32 -require ( - "gno.land/p/demo/ufmt" v0.0.0-latest -) +require gno.land/p/demo/ufmt v0.0.0-latest diff --git a/examples/gno.land/p/demo/microblog/gno.mod b/examples/gno.land/p/demo/microblog/gno.mod index fe19b89f777..5964679efa6 100644 --- a/examples/gno.land/p/demo/microblog/gno.mod +++ b/examples/gno.land/p/demo/microblog/gno.mod @@ -1,7 +1,8 @@ module gno.land/p/demo/microblog require ( - "gno.land/p/demo/avl" v0.0.0-latest - "gno.land/p/demo/ufmt" v0.0.0-latest - "gno.land/r/demo/users" v0.0.0-latest + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest + gno.land/r/demo/users v0.0.0-latest ) diff --git a/examples/gno.land/p/demo/mux/gno.mod b/examples/gno.land/p/demo/mux/gno.mod index 13e4736bea8..972a531e14c 100644 --- a/examples/gno.land/p/demo/mux/gno.mod +++ b/examples/gno.land/p/demo/mux/gno.mod @@ -1 +1 @@ -module "gno.land/p/demo/mux" +module gno.land/p/demo/mux diff --git a/examples/gno.land/p/demo/ownable/errors.gno b/examples/gno.land/p/demo/ownable/errors.gno new file mode 100644 index 00000000000..ffbf6ab3f6f --- /dev/null +++ b/examples/gno.land/p/demo/ownable/errors.gno @@ -0,0 +1,8 @@ +package ownable + +import "errors" + +var ( + ErrUnauthorized = errors.New("unauthorized; caller is not owner") + ErrInvalidAddress = errors.New("new owner address is invalid") +) diff --git a/examples/gno.land/p/demo/ownable/gno.mod b/examples/gno.land/p/demo/ownable/gno.mod new file mode 100644 index 00000000000..9a9abb1e661 --- /dev/null +++ b/examples/gno.land/p/demo/ownable/gno.mod @@ -0,0 +1 @@ +module gno.land/p/demo/ownable diff --git a/examples/gno.land/p/demo/ownable/ownable.gno b/examples/gno.land/p/demo/ownable/ownable.gno new file mode 100644 index 00000000000..7f2eac008e1 --- /dev/null +++ b/examples/gno.land/p/demo/ownable/ownable.gno @@ -0,0 +1,57 @@ +package ownable + +import ( + "std" +) + +// Ownable is meant to be used as a top-level object to make your contract ownable OR +// being embedded in a Gno object to manage per-object ownership. +type Ownable struct { + owner std.Address +} + +func New() *Ownable { + return &Ownable{ + owner: std.GetOrigCaller(), + } +} + +// TransferOwnership transfers ownership of the Ownable struct to a new address +func (o *Ownable) TransferOwnership(newOwner std.Address) error { + err := o.CallerIsOwner() + if err != nil { + return err + } + + if !newOwner.IsValid() { + return ErrInvalidAddress + } + + o.owner = newOwner + return nil +} + +// DropOwnership removes the owner, effectively disabling any owner-related actions +// Top-level usage: disables all only-owner actions/functions, +// Embedded usage: behaves like a burn functionality, removing the owner from the struct +func (o *Ownable) DropOwnership() error { + err := o.CallerIsOwner() + if err != nil { + return err + } + + o.owner = "" + return nil +} + +// CallerIsOwner checks if the caller of the function is the Realm's owner +func (o *Ownable) CallerIsOwner() error { + if std.GetOrigCaller() == o.owner { + return nil + } + return ErrUnauthorized +} + +func (o *Ownable) Owner() std.Address { + return o.owner +} diff --git a/examples/gno.land/p/demo/ownable/ownable_test.gno b/examples/gno.land/p/demo/ownable/ownable_test.gno new file mode 100644 index 00000000000..f725795fd47 --- /dev/null +++ b/examples/gno.land/p/demo/ownable/ownable_test.gno @@ -0,0 +1,113 @@ +package ownable + +import ( + "std" + "testing" +) + +var ( + firstCaller = std.Address("g1l9aypkr8xfvs82zeux486ddzec88ty69lue9de") + secondCaller = std.Address("g127jydsh6cms3lrtdenydxsckh23a8d6emqcvfa") +) + +func TestNew(t *testing.T) { + std.TestSetOrigCaller(firstCaller) + + result := New() + if firstCaller != result.owner { + t.Fatalf("Expected %s, got: %s\n", firstCaller, result.owner) + } +} + +func TestOwner(t *testing.T) { + std.TestSetOrigCaller(firstCaller) + + result := New() + resultOwner := result.Owner() + + expected := firstCaller + if resultOwner != expected { + t.Fatalf("Expected %s, got: %s\n", expected, result) + } +} + +func TestTransferOwnership(t *testing.T) { + std.TestSetOrigCaller(firstCaller) + o := New() + + err := o.TransferOwnership(secondCaller) + if err != nil { + t.Fatalf("TransferOwnership failed, %v", err) + } + + result := o.Owner() + if secondCaller != result { + t.Fatalf("Expected: %s, got: %s\n", secondCaller, result) + } +} + +func TestCallerIsOwner(t *testing.T) { + std.TestSetOrigCaller(firstCaller) + + o := New() + unauthorizedCaller := secondCaller + + std.TestSetOrigCaller(unauthorizedCaller) + + err := o.CallerIsOwner() + if err == nil { + t.Fatalf("Expected %s to not be owner\n", unauthorizedCaller) + } +} + +func TestDropOwnership(t *testing.T) { + std.TestSetOrigCaller(firstCaller) + + o := New() + + err := o.DropOwnership() + if err != nil { + t.Fatalf("DropOwnership failed, %v", err) + } + + owner := o.Owner() + if owner != "" { + t.Fatalf("Expected owner to be empty, not %s\n", owner) + } +} + +// Errors + +func TestErrUnauthorized(t *testing.T) { + std.TestSetOrigCaller(firstCaller) + + o := New() + + std.TestSetOrigCaller(secondCaller) + + err := o.TransferOwnership(firstCaller) + if err != ErrUnauthorized { + t.Fatalf("Should've been ErrUnauthorized, was %v", err) + } + + err = o.DropOwnership() + if err != ErrUnauthorized { + t.Fatalf("Should've been ErrUnauthorized, was %v", err) + } +} + +func TestErrInvalidAddress(t *testing.T) { + std.TestSetOrigCaller(firstCaller) + + o := New() + + err := o.TransferOwnership("") + if err != ErrInvalidAddress { + t.Fatalf("Should've been ErrInvalidAddress, was %v", err) + } + + err = o.TransferOwnership("10000000001000000000100000000010000000001000000000") + if err != ErrInvalidAddress { + t.Fatalf("Should've been ErrInvalidAddress, was %v", err) + } +} diff --git a/examples/gno.land/p/demo/pausable/gno.mod b/examples/gno.land/p/demo/pausable/gno.mod new file mode 100644 index 00000000000..08c7a4f7e5f --- /dev/null +++ b/examples/gno.land/p/demo/pausable/gno.mod @@ -0,0 +1,3 @@ +module gno.land/p/demo/pausable + +require gno.land/p/demo/ownable v0.0.0-latest diff --git a/examples/gno.land/p/demo/pausable/pausable.gno b/examples/gno.land/p/demo/pausable/pausable.gno new file mode 100644 index 00000000000..eae3456ba61 --- /dev/null +++ b/examples/gno.land/p/demo/pausable/pausable.gno @@ -0,0 +1,49 @@ +package pausable + +import "gno.land/p/demo/ownable" + +type Pausable struct { + *ownable.Ownable + paused bool +} + +// New returns a new Pausable struct with non-paused state as default +func New() *Pausable { + return &Pausable{ + Ownable: ownable.New(), + paused: false, + } +} + +// NewFromOwnable is the same as New, but with a pre-existing top-level ownable +func NewFromOwnable(ownable *ownable.Ownable) *Pausable { + return &Pausable{ + Ownable: ownable, + paused: false, + } +} + +// IsPaused checks if Pausable is paused +func (p Pausable) IsPaused() bool { + return p.paused +} + +// Pause sets the state of Pausable to true, meaning all pausable functions are paused +func (p *Pausable) Pause() error { + if err := p.CallerIsOwner(); err != nil { + return err + } + + p.paused = true + return nil +} + +// Unpause sets the state of Pausable to false, meaning all pausable functions are resumed +func (p *Pausable) Unpause() error { + if err := p.CallerIsOwner(); err != nil { + return err + } + + p.paused = false + return nil +} diff --git a/examples/gno.land/p/demo/pausable/pausable_test.gno b/examples/gno.land/p/demo/pausable/pausable_test.gno new file mode 100644 index 00000000000..cc95c457573 --- /dev/null +++ b/examples/gno.land/p/demo/pausable/pausable_test.gno @@ -0,0 +1,77 @@ +package pausable + +import ( + "std" + "testing" + + "gno.land/p/demo/ownable" +) + +var ( + firstCaller = std.Address("g1l9aypkr8xfvs82zeux486ddzec88ty69lue9de") + secondCaller = std.Address("g127jydsh6cms3lrtdenydxsckh23a8d6emqcvfa") +) + +func TestNew(t *testing.T) { + std.TestSetOrigCaller(firstCaller) + + result := New() + + if result.paused != false { + t.Fatalf("Expected result to be unpaused, got %t\n", result.paused) + } + + if result.Owner() != firstCaller { + t.Fatalf("Expected %s, got %s\n", firstCaller, result.Owner()) + } +} + +func TestNewFromOwnable(t *testing.T) { + std.TestSetOrigCaller(firstCaller) + o := ownable.New() + + std.TestSetOrigCaller(secondCaller) + result := NewFromOwnable(o) + + if result.Owner() != firstCaller { + t.Fatalf("Expected %s, got %s\n", firstCaller, result.Owner()) + } +} + +func TestSetUnpaused(t *testing.T) { + std.TestSetOrigCaller(firstCaller) + + result := New() + result.Unpause() + + if result.IsPaused() { + t.Fatalf("Expected result to be unpaused, got %t\n", result.IsPaused()) + } +} + +func TestSetPaused(t *testing.T) { + std.TestSetOrigCaller(firstCaller) + + result := New() + result.Pause() + + if !result.IsPaused() { + t.Fatalf("Expected result to be paused, got %t\n", result.IsPaused()) + } +} + +func TestIsPaused(t *testing.T) { + std.TestSetOrigCaller(firstCaller) + + result := New() + + if result.IsPaused() { + t.Fatalf("Expected result to be unpaused, got %t\n", result.IsPaused()) + } + + result.Pause() + + if !result.IsPaused() { + t.Fatalf("Expected result to be paused, got %t\n", result.IsPaused()) + } +} diff --git a/examples/gno.land/p/demo/rand/gno.mod b/examples/gno.land/p/demo/rand/gno.mod index 66082ea873e..098af152648 100644 --- a/examples/gno.land/p/demo/rand/gno.mod +++ b/examples/gno.land/p/demo/rand/gno.mod @@ -1,3 +1,3 @@ // Draft -module gno.land/p/demo/rand +module gno.land/p/demo/rand diff --git a/examples/gno.land/p/demo/seqid/README.md b/examples/gno.land/p/demo/seqid/README.md new file mode 100644 index 00000000000..8b96f99cfed --- /dev/null +++ b/examples/gno.land/p/demo/seqid/README.md @@ -0,0 +1,36 @@ +# seqid + +``` +package seqid // import "gno.land/p/demo/seqid" + +Package seqid provides a simple way to have sequential IDs which will be ordered +correctly when inserted in an AVL tree. + +Sample usage: + + var id seqid.ID + var users avl.Tree + + func NewUser() { + users.Set(id.Next().Binary(), &User{ ... }) + } + +TYPES + +type ID uint64 + An ID is a simple sequential ID generator. + +func FromBinary(b string) (ID, bool) + FromBinary creates a new ID from the given string. + +func (i ID) Binary() string + Binary returns a big-endian binary representation of the ID, suitable to be + used as an AVL key. + +func (i *ID) Next() ID + Next advances the ID i. It will panic if increasing ID would overflow. + +func (i *ID) TryNext() (ID, bool) + TryNext increases i by 1 and returns its value. It returns true if + successful, or false if the increment would result in an overflow. +``` diff --git a/examples/gno.land/p/demo/seqid/gno.mod b/examples/gno.land/p/demo/seqid/gno.mod new file mode 100644 index 00000000000..63e6a1fb551 --- /dev/null +++ b/examples/gno.land/p/demo/seqid/gno.mod @@ -0,0 +1 @@ +module gno.land/p/demo/seqid diff --git a/examples/gno.land/p/demo/seqid/seqid.gno b/examples/gno.land/p/demo/seqid/seqid.gno new file mode 100644 index 00000000000..8cb5366ef44 --- /dev/null +++ b/examples/gno.land/p/demo/seqid/seqid.gno @@ -0,0 +1,57 @@ +// Package seqid provides a simple way to have sequential IDs which will be +// ordered correctly when inserted in an AVL tree. +// +// Sample usage: +// +// var id seqid.ID +// var users avl.Tree +// +// func NewUser() { +// users.Set(id.Next().Binary(), &User{ ... }) +// } +package seqid + +import "encoding/binary" + +// An ID is a simple sequential ID generator. +type ID uint64 + +// Next advances the ID i. +// It will panic if increasing ID would overflow. +func (i *ID) Next() ID { + next, ok := i.TryNext() + if !ok { + panic("seqid: next ID overflows uint64") + } + return next +} + +const maxID ID = 1<<64 - 1 + +// TryNext increases i by 1 and returns its value. +// It returns true if successful, or false if the increment would result in +// an overflow. +func (i *ID) TryNext() (ID, bool) { + if *i == maxID { + // Addition will overflow. + return 0, false + } + *i++ + return *i, true +} + +// Binary returns a big-endian binary representation of the ID, +// suitable to be used as an AVL key. +func (i ID) Binary() string { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, uint64(i)) + return string(buf) +} + +// FromBinary creates a new ID from the given string. +func FromBinary(b string) (ID, bool) { + if len(b) != 8 { + return 0, false + } + return ID(binary.BigEndian.Uint64([]byte(b))), true +} diff --git a/examples/gno.land/p/demo/seqid/seqid_test.gno b/examples/gno.land/p/demo/seqid/seqid_test.gno new file mode 100644 index 00000000000..c6f57960177 --- /dev/null +++ b/examples/gno.land/p/demo/seqid/seqid_test.gno @@ -0,0 +1,43 @@ +package seqid + +import ( + "fmt" + "strings" + "testing" +) + +func TestID(t *testing.T) { + var i ID + + for j := 0; j < 100; j++ { + i.Next() + } + if i != 100 { + t.Fatalf("invalid: wanted %d got %d", 100, i) + } +} + +func TestID_Overflow(t *testing.T) { + i := ID(maxID) + + defer func() { + err := recover() + if !strings.Contains(fmt.Sprint(err), "next ID overflows") { + t.Errorf("did not overflow") + } + }() + + i.Next() +} + +func TestID_Binary(t *testing.T) { + var i ID + prev := i.Binary() + + for j := 0; j < 1000; j++ { + cur := i.Next().Binary() + if cur <= prev { + t.Fatalf("cur %x <= prev %x", cur, prev) + } + } +} diff --git a/examples/gno.land/p/demo/svg/gno.mod b/examples/gno.land/p/demo/svg/gno.mod index adb2acf1350..0af7ba0636d 100644 --- a/examples/gno.land/p/demo/svg/gno.mod +++ b/examples/gno.land/p/demo/svg/gno.mod @@ -1,5 +1,3 @@ module gno.land/p/demo/svg -require ( - "gno.land/p/demo/ufmt" v0.0.0-latest -) +require gno.land/p/demo/ufmt v0.0.0-latest diff --git a/examples/gno.land/p/demo/tamagotchi/gno.mod b/examples/gno.land/p/demo/tamagotchi/gno.mod new file mode 100644 index 00000000000..58441284a6b --- /dev/null +++ b/examples/gno.land/p/demo/tamagotchi/gno.mod @@ -0,0 +1,3 @@ +module gno.land/p/demo/tamagotchi + +require gno.land/p/demo/ufmt v0.0.0-latest diff --git a/examples/gno.land/p/demo/tamagotchi/tamagotchi.gno b/examples/gno.land/p/demo/tamagotchi/tamagotchi.gno new file mode 100644 index 00000000000..4e0cb6cb2b2 --- /dev/null +++ b/examples/gno.land/p/demo/tamagotchi/tamagotchi.gno @@ -0,0 +1,175 @@ +package tamagotchi + +import ( + "time" + + "gno.land/p/demo/ufmt" +) + +// Tamagotchi structure +type Tamagotchi struct { + name string + hunger int + happiness int + health int + age int + maxAge int + sleepy int + created time.Time + lastUpdated time.Time +} + +func New(name string) *Tamagotchi { + now := time.Now() + return &Tamagotchi{ + name: name, + hunger: 50, + happiness: 50, + health: 50, + maxAge: 100, + lastUpdated: now, + created: now, + } +} + +func (t *Tamagotchi) Name() string { + t.update() + return t.name +} + +func (t *Tamagotchi) Hunger() int { + t.update() + return t.hunger +} + +func (t *Tamagotchi) Happiness() int { + t.update() + return t.happiness +} + +func (t *Tamagotchi) Health() int { + t.update() + return t.health +} + +func (t *Tamagotchi) Age() int { + t.update() + return t.age +} + +func (t *Tamagotchi) Sleepy() int { + t.update() + return t.sleepy +} + +// Feed method for Tamagotchi +func (t *Tamagotchi) Feed() { + t.update() + if t.dead() { + return + } + t.hunger = bound(t.hunger-10, 0, 100) +} + +// Play method for Tamagotchi +func (t *Tamagotchi) Play() { + t.update() + if t.dead() { + return + } + t.happiness = bound(t.happiness+10, 0, 100) +} + +// Heal method for Tamagotchi +func (t *Tamagotchi) Heal() { + t.update() + + if t.dead() { + return + } + t.health = bound(t.health+10, 0, 100) +} + +func (t Tamagotchi) dead() bool { return t.health == 0 } + +// Update applies changes based on the duration since the last update +func (t *Tamagotchi) update() { + if t.dead() { + return + } + + now := time.Now() + if t.lastUpdated == now { + return + } + + duration := now.Sub(t.lastUpdated) + elapsedMins := int(duration.Minutes()) + + t.hunger = bound(t.hunger+elapsedMins, 0, 100) + t.happiness = bound(t.happiness-elapsedMins, 0, 100) + t.health = bound(t.health-elapsedMins, 0, 100) + t.sleepy = bound(t.sleepy+elapsedMins, 0, 100) + + // age is hours since created + t.age = int(now.Sub(t.created).Hours()) + if t.age > t.maxAge { + t.age = t.maxAge + t.health = 0 + } + if t.health == 0 { + t.sleepy = 0 + t.happiness = 0 + t.hunger = 0 + } + + t.lastUpdated = now +} + +// Face returns an ASCII art representation of the Tamagotchi's current state +func (t *Tamagotchi) Face() string { + t.update() + return t.face() +} + +func (t *Tamagotchi) face() string { + switch { + case t.health == 0: + return "😵" // dead face + case t.health < 30: + return "😷" // sick face + case t.happiness < 30: + return "😢" // sad face + case t.hunger > 70: + return "😫" // hungry face + case t.sleepy > 70: + return "😴" // sleepy face + default: + return "😃" // happy face + } +} + +// Markdown method for Tamagotchi +func (t *Tamagotchi) Markdown() string { + t.update() + return ufmt.Sprintf(`# %s %s + +* age: %d +* hunger: %d +* happiness: %d +* health: %d +* sleepy: %d`, + t.name, t.Face(), + t.age, t.hunger, t.happiness, t.health, t.sleepy, + ) +} + +func bound(n, min, max int) int { + if n < min { + return min + } + if n > max { + return max + } + return n +} diff --git a/examples/gno.land/p/demo/tamagotchi/z0_filetest.gno b/examples/gno.land/p/demo/tamagotchi/z0_filetest.gno new file mode 100644 index 00000000000..36163065e7f --- /dev/null +++ b/examples/gno.land/p/demo/tamagotchi/z0_filetest.gno @@ -0,0 +1,106 @@ +package main + +import ( + "std" + "time" + + "internal/os_test" + + "gno.land/p/demo/tamagotchi" +) + +func main() { + t := tamagotchi.New("Gnome") + + println("\n-- INITIAL\n") + println(t.Markdown()) + + println("\n-- WAIT 20 minutes\n") + os_test.Sleep(20 * time.Minute) + println(t.Markdown()) + + println("\n-- FEEDx3, PLAYx2, HEALx4\n") + t.Feed() + t.Feed() + t.Feed() + t.Play() + t.Play() + t.Heal() + t.Heal() + t.Heal() + t.Heal() + println(t.Markdown()) + + println("\n-- WAIT 20 minutes\n") + os_test.Sleep(20 * time.Minute) + println(t.Markdown()) + + println("\n-- WAIT 20 hours\n") + os_test.Sleep(20 * time.Hour) + println(t.Markdown()) + + println("\n-- WAIT 20 hours\n") + os_test.Sleep(20 * time.Hour) + println(t.Markdown()) +} + +// Output: +// -- INITIAL +// +// # Gnome 😃 +// +// * age: 0 +// * hunger: 50 +// * happiness: 50 +// * health: 50 +// * sleepy: 0 +// +// -- WAIT 20 minutes +// +// # Gnome 😃 +// +// * age: 0 +// * hunger: 70 +// * happiness: 30 +// * health: 30 +// * sleepy: 20 +// +// -- FEEDx3, PLAYx2, HEALx4 +// +// # Gnome 😃 +// +// * age: 0 +// * hunger: 40 +// * happiness: 50 +// * health: 70 +// * sleepy: 20 +// +// -- WAIT 20 minutes +// +// # Gnome 😃 +// +// * age: 0 +// * hunger: 60 +// * happiness: 30 +// * health: 50 +// * sleepy: 40 +// +// -- WAIT 20 hours +// +// # Gnome 😵 +// +// * age: 20 +// * hunger: 0 +// * happiness: 0 +// * health: 0 +// * sleepy: 0 +// +// -- WAIT 20 hours +// +// # Gnome 😵 +// +// * age: 20 +// * hunger: 0 +// * happiness: 0 +// * health: 0 +// * sleepy: 0 diff --git a/examples/gno.land/p/demo/tests/gno.mod b/examples/gno.land/p/demo/tests/gno.mod index 229c2f62d9c..5d80e106567 100644 --- a/examples/gno.land/p/demo/tests/gno.mod +++ b/examples/gno.land/p/demo/tests/gno.mod @@ -1,6 +1,6 @@ module gno.land/p/demo/tests require ( - "gno.land/p/demo/tests/subtests" v0.0.0-latest - "gno.land/r/demo/tests" v0.0.0-latest + gno.land/p/demo/tests/subtests v0.0.0-latest + gno.land/r/demo/tests v0.0.0-latest ) diff --git a/examples/gno.land/p/demo/tests/subtests/gno.mod b/examples/gno.land/p/demo/tests/subtests/gno.mod index 26ec7c4879a..c8333722809 100644 --- a/examples/gno.land/p/demo/tests/subtests/gno.mod +++ b/examples/gno.land/p/demo/tests/subtests/gno.mod @@ -1,4 +1,4 @@ module gno.land/p/demo/tests/subtests -// TODO: this file should not exist. +// TODO: this file should not exist. // This is a temporary workaround. Until https://github.com/gnolang/gno/issues/852 diff --git a/examples/gno.land/p/demo/tests/tests.gno b/examples/gno.land/p/demo/tests/tests.gno index f0e4173f313..1a2c2526d01 100644 --- a/examples/gno.land/p/demo/tests/tests.gno +++ b/examples/gno.land/p/demo/tests/tests.gno @@ -8,6 +8,8 @@ import ( rtests "gno.land/r/demo/tests" ) +const World = "world" + // IncCounter demonstrates that it's possible to call a realm function from // a package. So a package can potentially write into the store, by calling // an other realm. diff --git a/examples/gno.land/p/demo/tests/tests_test.gno b/examples/gno.land/p/demo/tests/tests_test.gno new file mode 100644 index 00000000000..49caf2a0294 --- /dev/null +++ b/examples/gno.land/p/demo/tests/tests_test.gno @@ -0,0 +1,18 @@ +package tests_test + +import ( + "testing" + + "gno.land/p/demo/tests" +) + +var World = "WORLD" + +func TestGetHelloWorld(t *testing.T) { + // tests.World is 'world' + s := "hello " + tests.World + World + const want = "hello worldWORLD" + if s != want { + t.Errorf("got %q want %q", s, want) + } +} diff --git a/examples/gno.land/p/demo/ui/gno.mod b/examples/gno.land/p/demo/ui/gno.mod index e71ee2d1ab1..41f5cb78d83 100644 --- a/examples/gno.land/p/demo/ui/gno.mod +++ b/examples/gno.land/p/demo/ui/gno.mod @@ -1 +1 @@ -module "gno.land/p/demo/ui" +module gno.land/p/demo/ui diff --git a/examples/gno.land/p/demo/ui/ui.gno b/examples/gno.land/p/demo/ui/ui.gno index efa185914a0..dd21d0510eb 100644 --- a/examples/gno.land/p/demo/ui/ui.gno +++ b/examples/gno.land/p/demo/ui/ui.gno @@ -1,6 +1,9 @@ package ui -import "strings" +import ( + "strconv" + "strings" +) type DOM struct { // metadata @@ -56,6 +59,17 @@ func (dom DOM) String() string { return output } +type Jumbotron []DomStringer + +func (j Jumbotron) String(dom DOM) string { + output := `
` + "\n\n" + for _, elem := range j { + output += elem.String(dom) + "\n" + } + output += `
` + "\n" + return output +} + // XXX: rename Element to Div? type Element []DomStringer @@ -88,6 +102,26 @@ func (b Breadcrumb) String(dom DOM) string { return output } +type Columns struct { + MaxWidth int + Columns []Element +} + +func (c *Columns) Append(elems ...Element) { + c.Columns = append(c.Columns, elems...) +} + +func (c Columns) String(dom DOM) string { + output := `
` + "\n" + for _, entry := range c.Columns { + output += `
` + "\n\n" + output += entry.String(dom) + output += "
\n" + } + output += "
\n" + return output +} + type Link struct { Text string Path string @@ -104,8 +138,14 @@ func (l Link) String(dom DOM) string { case l.Path != "" && l.URL != "": panic("a link should have a path or a URL, not both.") case l.Path != "": + if l.Text == "" { + l.Text = l.Path + } url = dom.Prefix + l.Path case l.URL != "": + if l.Text == "" { + l.Text = l.URL + } url = l.URL } @@ -151,6 +191,7 @@ type ( Italic string Code string Paragraph string + Quote string HR struct{} ) @@ -160,6 +201,7 @@ func (text H3) String(_ DOM) string { return "### " + string(text) + "\n" func (text H4) String(_ DOM) string { return "#### " + string(text) + "\n" } func (text H5) String(_ DOM) string { return "##### " + string(text) + "\n" } func (text H6) String(_ DOM) string { return "###### " + string(text) + "\n" } +func (text Quote) String(_ DOM) string { return "> " + string(text) + "\n" } func (text Bold) String(_ DOM) string { return "**" + string(text) + "**" } func (text Italic) String(_ DOM) string { return "_" + string(text) + "_" } func (text Paragraph) String(_ DOM) string { return "\n" + string(text) + "\n" } diff --git a/examples/gno.land/r/demo/art/gnoface/gno.mod b/examples/gno.land/r/demo/art/gnoface/gno.mod index 33d644206d6..bc17ee9df3b 100644 --- a/examples/gno.land/r/demo/art/gnoface/gno.mod +++ b/examples/gno.land/r/demo/art/gnoface/gno.mod @@ -1,6 +1,6 @@ module gno.land/r/demo/art/gnoface require ( - "gno.land/p/demo/rand" v0.0.0-latest - "gno.land/p/demo/ufmt" v0.0.0-latest + gno.land/p/demo/rand v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/art/millipede/gno.mod b/examples/gno.land/r/demo/art/millipede/gno.mod index 10e3d2f1474..346e3a1673c 100644 --- a/examples/gno.land/r/demo/art/millipede/gno.mod +++ b/examples/gno.land/r/demo/art/millipede/gno.mod @@ -1,5 +1,3 @@ module gno.land/r/demo/art/millipede -require ( - "gno.land/p/demo/ufmt" v0.0.0-latest -) +require gno.land/p/demo/ufmt v0.0.0-latest diff --git a/examples/gno.land/r/demo/boards/README.md b/examples/gno.land/r/demo/boards/README.md index 2729df055b1..849998b04e9 100644 --- a/examples/gno.land/r/demo/boards/README.md +++ b/examples/gno.land/r/demo/boards/README.md @@ -9,7 +9,7 @@ name ["gno.land/r/demo/boards"](https://gno.land/r/demo/boards/) NOTE: Where you see `-remote localhost:26657` here, that flag can be replaced with `-remote test3.gno.land:36657` if you have $GNOT on the testnet. -(To use the testnet, also replace `-chainid dev` with `-chainid testchain` .) +(To use the testnet, also replace `-chainid dev` with `-chainid test3` .) ### Build `gnokey` (and other tools). diff --git a/examples/gno.land/r/demo/boards/gno.mod b/examples/gno.land/r/demo/boards/gno.mod index 882d97fec83..434ad019883 100644 --- a/examples/gno.land/r/demo/boards/gno.mod +++ b/examples/gno.land/r/demo/boards/gno.mod @@ -1,6 +1,6 @@ module gno.land/r/demo/boards require ( - "gno.land/p/demo/avl" v0.0.0-latest - "gno.land/r/demo/users" v0.0.0-latest + gno.land/p/demo/avl v0.0.0-latest + gno.land/r/demo/users v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/boards/misc.gno b/examples/gno.land/r/demo/boards/misc.gno index e6235e1d0ab..ccb6af0b6a9 100644 --- a/examples/gno.land/r/demo/boards/misc.gno +++ b/examples/gno.land/r/demo/boards/misc.gno @@ -80,9 +80,9 @@ func summaryOf(str string, length int) string { func displayAddressMD(addr std.Address) string { user := users.GetUserByAddress(addr) if user == nil { - return "[" + addr.String() + "](/r/users:" + addr.String() + ")" + return "[" + addr.String() + "](/r/demo/users:" + addr.String() + ")" } else { - return "[@" + user.Name() + "](/r/users:" + user.Name() + ")" + return "[@" + user.Name() + "](/r/demo/users:" + user.Name() + ")" } } diff --git a/examples/gno.land/r/demo/boards/post.gno b/examples/gno.land/r/demo/boards/post.gno index d14800e7cec..f35cf23628c 100644 --- a/examples/gno.land/r/demo/boards/post.gno +++ b/examples/gno.land/r/demo/boards/post.gno @@ -162,6 +162,15 @@ func (post *Post) GetReplyFormURL() string { "&body.type=textarea" } +func (post *Post) GetRepostFormURL() string { + return "/r/demo/boards?help&__func=CreateRepost" + + "&bid=" + post.board.id.String() + + "&postid=" + post.id.String() + + "&title.type=textarea" + + "&body.type=textarea" + + "&dstBoardID.type=textarea" +} + func (post *Post) GetDeleteFormURL() string { return "/r/demo/boards?help&__func=DeletePost" + "&bid=" + post.board.id.String() + @@ -170,6 +179,17 @@ func (post *Post) GetDeleteFormURL() string { } func (post *Post) RenderSummary() string { + if post.repostBoard != 0 { + dstBoard := getBoard(post.repostBoard) + if dstBoard == nil { + panic("repostBoard does not exist") + } + thread := dstBoard.GetThread(PostID(post.parentID)) + if thread == nil { + return "reposted post does not exist" + } + return "Repost: " + post.GetSummary() + "\n" + thread.RenderSummary() + } str := "" if post.title != "" { str += "## [" + summaryOf(post.title, 80) + "](" + post.GetURL() + ")\n" @@ -179,7 +199,8 @@ func (post *Post) RenderSummary() string { str += "\\- " + displayAddressMD(post.creator) + "," str += " [" + post.createdAt.Format("2006-01-02 3:04pm MST") + "](" + post.GetURL() + ")" str += " \\[[x](" + post.GetDeleteFormURL() + ")]" - str += " (" + strconv.Itoa(post.replies.Size()) + " replies)" + "\n" + str += " (" + strconv.Itoa(post.replies.Size()) + " replies)" + str += " (" + strconv.Itoa(post.reposts.Size()) + " reposts)" + "\n" return str } @@ -196,6 +217,9 @@ func (post *Post) RenderPost(indent string, levels int) string { str += indent + "\\- " + displayAddressMD(post.creator) + ", " str += "[" + post.createdAt.Format("2006-01-02 3:04pm (MST)") + "](" + post.GetURL() + ")" str += " \\[[reply](" + post.GetReplyFormURL() + ")]" + if post.IsThread() { + str += " \\[[repost](" + post.GetRepostFormURL() + ")]" + } str += " \\[[x](" + post.GetDeleteFormURL() + ")]\n" if levels > 0 { if post.replies.Size() > 0 { diff --git a/examples/gno.land/r/demo/boards/z_0_filetest.gno b/examples/gno.land/r/demo/boards/z_0_filetest.gno index 5c3fc7b204e..f5611d3d6c3 100644 --- a/examples/gno.land/r/demo/boards/z_0_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_0_filetest.gno @@ -30,10 +30,10 @@ func main() { // ## [First Post (title)](/r/demo/boards:test_board/1) // // Body of the first post. (body) -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm UTC](/r/demo/boards:test_board/1) \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] (0 replies) +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm UTC](/r/demo/boards:test_board/1) \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] (0 replies) (0 reposts) // // ---------------------------------------- // ## [Second Post (title)](/r/demo/boards:test_board/2) // // Body of the second post. (body) -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm UTC](/r/demo/boards:test_board/2) \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=2)] (1 replies) +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm UTC](/r/demo/boards:test_board/2) \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=2)] (1 replies) (0 reposts) diff --git a/examples/gno.land/r/demo/boards/z_10_c_filetest.gno b/examples/gno.land/r/demo/boards/z_10_c_filetest.gno index f161c8cedee..51c597d7998 100644 --- a/examples/gno.land/r/demo/boards/z_10_c_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_10_c_filetest.gno @@ -35,14 +35,14 @@ func main() { // # First Post in (title) // // Body of the first post. (body) -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=1&title.type=textarea&body.type=textarea&dstBoardID.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] // // > First reply of the First post // > -// > \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=2&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=2)] +// > \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=2&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=2)] // // ---------------------------------------------------- // # First Post in (title) // // Body of the first post. (body) -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=1&title.type=textarea&body.type=textarea&dstBoardID.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] diff --git a/examples/gno.land/r/demo/boards/z_10_filetest.gno b/examples/gno.land/r/demo/boards/z_10_filetest.gno index 7a24efe09ca..f32d69c8056 100644 --- a/examples/gno.land/r/demo/boards/z_10_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_10_filetest.gno @@ -33,7 +33,7 @@ func main() { // # First Post in (title) // // Body of the first post. (body) -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=1&title.type=textarea&body.type=textarea&dstBoardID.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] // // ---------------------------------------------------- // thread does not exist with id: 1 diff --git a/examples/gno.land/r/demo/boards/z_11_d_filetest.gno b/examples/gno.land/r/demo/boards/z_11_d_filetest.gno index a519d4f6a94..b3803c505f2 100644 --- a/examples/gno.land/r/demo/boards/z_11_d_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_11_d_filetest.gno @@ -35,18 +35,18 @@ func main() { // # First Post in (title) // // Body of the first post. (body) -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=1&title.type=textarea&body.type=textarea&dstBoardID.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] // // > First reply of the First post // > -// > \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=2&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=2)] +// > \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=2&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=2)] // // ---------------------------------------------------- // # First Post in (title) // // Body of the first post. (body) -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=1&title.type=textarea&body.type=textarea&dstBoardID.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] // // > Edited: First reply of the First post // > -// > \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=2&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=2)] +// > \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=2&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=2)] diff --git a/examples/gno.land/r/demo/boards/z_11_filetest.gno b/examples/gno.land/r/demo/boards/z_11_filetest.gno index 2d96d46dc98..937673c8bd4 100644 --- a/examples/gno.land/r/demo/boards/z_11_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_11_filetest.gno @@ -33,10 +33,10 @@ func main() { // # First Post in (title) // // Body of the first post. (body) -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=1&title.type=textarea&body.type=textarea&dstBoardID.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] // // ---------------------------------------------------- // # Edited: First Post in (title) // // Edited: Body of the first post. (body) -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=1&title.type=textarea&body.type=textarea&dstBoardID.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] diff --git a/examples/gno.land/r/demo/boards/z_12_a_filetest.gno b/examples/gno.land/r/demo/boards/z_12_a_filetest.gno new file mode 100644 index 00000000000..82fc4b44a8c --- /dev/null +++ b/examples/gno.land/r/demo/boards/z_12_a_filetest.gno @@ -0,0 +1,32 @@ +// PKGPATH: gno.land/r/boards_test +package boards_test + +// SEND: 200000000ugnot + +import ( + "std" + + "gno.land/p/demo/testutils" + "gno.land/r/demo/boards" + "gno.land/r/demo/users" +) + +func main() { + users.Register("", "gnouser", "my profile") + // create a post via registered user + bid1 := boards.CreateBoard("test_board1") + pid := boards.CreateThread(bid1, "First Post (title)", "Body of the first post. (body)") + bid2 := boards.CreateBoard("test_board2") + + // create a repost via anon user + test2 := testutils.TestAddress("test2") + std.TestSetOrigCaller(test2) + std.TestSetOrigSend(std.Coins{{"ugnot", 9000000}}, nil) + + rid := boards.CreateRepost(bid1, pid, "", "Check this out", bid2) + println(rid) + println(boards.Render("test_board1")) +} + +// Error: +// please register, otherwise minimum fee 100000000 is required if anonymous diff --git a/examples/gno.land/r/demo/boards/z_12_b_filetest.gno b/examples/gno.land/r/demo/boards/z_12_b_filetest.gno new file mode 100644 index 00000000000..af2f66594db --- /dev/null +++ b/examples/gno.land/r/demo/boards/z_12_b_filetest.gno @@ -0,0 +1,24 @@ +// PKGPATH: gno.land/r/boards_test +package boards_test + +// SEND: 200000000ugnot + +import ( + "gno.land/r/demo/boards" + "gno.land/r/demo/users" +) + +func main() { + users.Register("", "gnouser", "my profile") + bid1 := boards.CreateBoard("test_board1") + pid := boards.CreateThread(bid1, "First Post (title)", "Body of the first post. (body)") + bid2 := boards.CreateBoard("test_board2") + + // create a repost to a non-existing board + rid := boards.CreateRepost(5, pid, "", "Check this out", bid2) + println(rid) + println(boards.Render("test_board1")) +} + +// Error: +// src board not exist diff --git a/examples/gno.land/r/demo/boards/z_12_c_filetest.gno b/examples/gno.land/r/demo/boards/z_12_c_filetest.gno new file mode 100644 index 00000000000..6cb6d34bf49 --- /dev/null +++ b/examples/gno.land/r/demo/boards/z_12_c_filetest.gno @@ -0,0 +1,24 @@ +// PKGPATH: gno.land/r/boards_test +package boards_test + +// SEND: 200000000ugnot + +import ( + "gno.land/r/demo/boards" + "gno.land/r/demo/users" +) + +func main() { + users.Register("", "gnouser", "my profile") + bid1 := boards.CreateBoard("test_board1") + boards.CreateThread(bid1, "First Post (title)", "Body of the first post. (body)") + bid2 := boards.CreateBoard("test_board2") + + // create a repost to a non-existing thread + rid := boards.CreateRepost(bid1, 5, "", "Check this out", bid2) + println(rid) + println(boards.Render("test_board1")) +} + +// Error: +// thread not exist diff --git a/examples/gno.land/r/demo/boards/z_12_d_filetest.gno b/examples/gno.land/r/demo/boards/z_12_d_filetest.gno new file mode 100644 index 00000000000..eb21e8dd5ba --- /dev/null +++ b/examples/gno.land/r/demo/boards/z_12_d_filetest.gno @@ -0,0 +1,24 @@ +// PKGPATH: gno.land/r/boards_test +package boards_test + +// SEND: 200000000ugnot + +import ( + "gno.land/r/demo/boards" + "gno.land/r/demo/users" +) + +func main() { + users.Register("", "gnouser", "my profile") + bid1 := boards.CreateBoard("test_board1") + pid := boards.CreateThread(bid1, "First Post (title)", "Body of the first post. (body)") + boards.CreateBoard("test_board2") + + // create a repost to a non-existing destination board + rid := boards.CreateRepost(bid1, pid, "", "Check this out", 5) + println(rid) + println(boards.Render("test_board1")) +} + +// Error: +// dst board not exist diff --git a/examples/gno.land/r/demo/boards/z_12_filetest.gno b/examples/gno.land/r/demo/boards/z_12_filetest.gno new file mode 100644 index 00000000000..59ab07d17d8 --- /dev/null +++ b/examples/gno.land/r/demo/boards/z_12_filetest.gno @@ -0,0 +1,40 @@ +// PKGPATH: gno.land/r/boards_test +package boards_test + +// SEND: 200000000ugnot + +import ( + "gno.land/r/demo/boards" + "gno.land/r/demo/users" +) + +var ( + bid1 boards.BoardID + bid2 boards.BoardID + pid boards.PostID +) + +func init() { + users.Register("", "gnouser", "my profile") + + bid1 = boards.CreateBoard("test_board1") + pid = boards.CreateThread(bid1, "First Post (title)", "Body of the first post. (body)") + bid2 = boards.CreateBoard("test_board2") +} + +func main() { + rid := boards.CreateRepost(bid1, pid, "", "Check this out", bid2) + println(rid) + println(boards.Render("test_board2")) +} + +// Output: +// 1 +// \[[post](/r/demo/boards?help&__func=CreateThread&bid=2&body.type=textarea)] +// +// ---------------------------------------- +// Repost: Check this out +// ## [First Post (title)](/r/demo/boards:test_board1/1) +// +// Body of the first post. (body) +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm UTC](/r/demo/boards:test_board1/1) \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] (0 replies) (1 reposts) diff --git a/examples/gno.land/r/demo/boards/z_2_filetest.gno b/examples/gno.land/r/demo/boards/z_2_filetest.gno index 1e0b55a3952..2a6b937c875 100644 --- a/examples/gno.land/r/demo/boards/z_2_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_2_filetest.gno @@ -32,7 +32,7 @@ func main() { // # Second Post (title) // // Body of the second post. (body) -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=2&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=2)] +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=2&body.type=textarea)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=2&title.type=textarea&body.type=textarea&dstBoardID.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=2)] // // > Reply of the second post -// > \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=3&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=3)] +// > \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=3&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=3)] diff --git a/examples/gno.land/r/demo/boards/z_3_filetest.gno b/examples/gno.land/r/demo/boards/z_3_filetest.gno index f4160e16126..0d25dae08ae 100644 --- a/examples/gno.land/r/demo/boards/z_3_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_3_filetest.gno @@ -34,7 +34,7 @@ func main() { // # Second Post (title) // // Body of the second post. (body) -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=2&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=2)] +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=2&body.type=textarea)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=2&title.type=textarea&body.type=textarea&dstBoardID.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=2)] // // > Reply of the second post -// > \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=3&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=3)] +// > \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=3&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=3)] diff --git a/examples/gno.land/r/demo/boards/z_4_filetest.gno b/examples/gno.land/r/demo/boards/z_4_filetest.gno index 749566ea5bc..3b168ed5c58 100644 --- a/examples/gno.land/r/demo/boards/z_4_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_4_filetest.gno @@ -37,13 +37,13 @@ func main() { // # Second Post (title) // // Body of the second post. (body) -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=2&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=2)] +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=2&body.type=textarea)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=2&title.type=textarea&body.type=textarea&dstBoardID.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=2)] // // > Reply of the second post -// > \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=3&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=3)] +// > \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=3&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=3)] // // > Second reply of the second post -// > \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/4) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=4&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=4)] +// > \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/4) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=4&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=4)] // Realm: // switchrealm["gno.land/r/demo/users"] @@ -374,7 +374,7 @@ func main() { // "Escaped": true, // "ObjectID": "336074805fc853987abe6f7fe3ad97a6a6f3077a:2" // }, -// "Index": "188", +// "Index": "189", // "TV": null // } // } @@ -541,7 +541,7 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "8164abed5231309c88497013f7da72a1b5d427b0", +// "Hash": "25ffc45509708ca0ae17271cb4c3a1dfb367b965", // "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:115" // } // }, @@ -847,7 +847,7 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "5b4b593f1d4b37cb99166247ea28174f91087fdd", +// "Hash": "a8e67b9881af89ca2ec2f05778bf7528a54a5833", // "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:82" // } // }, @@ -865,7 +865,7 @@ func main() { // }, // "V": { // "@type": "/gno.RefValue", -// "Hash": "7e9fd9bb5e90a06c7751585cd80f23aedddde25b", +// "Hash": "d8ae14a4620e3c6dedabd76cd0c5d7e3c205d647", // "ObjectID": "f6dbf411da22e67d74cd7ddba6a76cd7e14a4822:83" // } // }, diff --git a/examples/gno.land/r/demo/boards/z_5_c_filetest.gno b/examples/gno.land/r/demo/boards/z_5_c_filetest.gno index e372b7cc430..bb9d7a57010 100644 --- a/examples/gno.land/r/demo/boards/z_5_c_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_5_c_filetest.gno @@ -33,7 +33,7 @@ func main() { // # First Post (title) // // Body of the first post. (body) -// \- [g1w3jhxapjta047h6lta047h6lta047h6laqcyu4](/r/users:g1w3jhxapjta047h6lta047h6lta047h6laqcyu4), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] +// \- [g1w3jhxapjta047h6lta047h6lta047h6laqcyu4](/r/demo/users:g1w3jhxapjta047h6lta047h6lta047h6laqcyu4), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=1&body.type=textarea)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=1&title.type=textarea&body.type=textarea&dstBoardID.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] // // > Reply of the first post -// > \- [g1w3jhxapjta047h6lta047h6lta047h6laqcyu4](/r/users:g1w3jhxapjta047h6lta047h6lta047h6laqcyu4), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=2&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=2)] +// > \- [g1w3jhxapjta047h6lta047h6lta047h6laqcyu4](/r/demo/users:g1w3jhxapjta047h6lta047h6lta047h6laqcyu4), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/1/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=1&postid=2&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=2)] diff --git a/examples/gno.land/r/demo/boards/z_5_filetest.gno b/examples/gno.land/r/demo/boards/z_5_filetest.gno index 421d9440785..76fb4ee432c 100644 --- a/examples/gno.land/r/demo/boards/z_5_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_5_filetest.gno @@ -33,11 +33,11 @@ func main() { // # Second Post (title) // // Body of the second post. (body) -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=2&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=2)] +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=2&body.type=textarea)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=2&title.type=textarea&body.type=textarea&dstBoardID.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=2)] // // > Reply of the second post -// > \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=3&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=3)] +// > \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=3&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=3)] // // > Second reply of the second post // > -// > \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/4) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=4&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=4)] +// > \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/4) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=4&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=4)] diff --git a/examples/gno.land/r/demo/boards/z_6_filetest.gno b/examples/gno.land/r/demo/boards/z_6_filetest.gno index 2a392808d30..c1cd84d35a7 100644 --- a/examples/gno.land/r/demo/boards/z_6_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_6_filetest.gno @@ -35,15 +35,15 @@ func main() { // # Second Post (title) // // Body of the second post. (body) -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=2&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=2)] +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=2&body.type=textarea)] \[[repost](/r/demo/boards?help&__func=CreateRepost&bid=1&postid=2&title.type=textarea&body.type=textarea&dstBoardID.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=2)] // // > Reply of the second post -// > \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=3&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=3)] +// > \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=3&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=3)] // > // > > First reply of the first reply // > > -// > > \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/5) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=5&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=5)] +// > > \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/5) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=5&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=5)] // // > Second reply of the second post // > -// > \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/4) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=4&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=4)] +// > \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/4) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=4&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=4)] diff --git a/examples/gno.land/r/demo/boards/z_7_filetest.gno b/examples/gno.land/r/demo/boards/z_7_filetest.gno index 1294f72c1be..343ec8f909e 100644 --- a/examples/gno.land/r/demo/boards/z_7_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_7_filetest.gno @@ -28,4 +28,4 @@ func main() { // ## [First Post (title)](/r/demo/boards:test_board/1) // // Body of the first post. (body) -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm UTC](/r/demo/boards:test_board/1) \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] (0 replies) +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm UTC](/r/demo/boards:test_board/1) \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=1&postid=1)] (0 replies) (0 reposts) diff --git a/examples/gno.land/r/demo/boards/z_8_filetest.gno b/examples/gno.land/r/demo/boards/z_8_filetest.gno index 5ba0b34ad30..226dae62b3f 100644 --- a/examples/gno.land/r/demo/boards/z_8_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_8_filetest.gno @@ -35,10 +35,10 @@ func main() { // _[see thread](/r/demo/boards:test_board/2)_ // // Reply of the second post -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=3&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=3)] +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/3) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=3&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=3)] // // _[see all 1 replies](/r/demo/boards:test_board/2/3)_ // // > First reply of the first reply // > -// > \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/5) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=5&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=5)] +// > \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:test_board/2/5) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=1&threadid=2&postid=5&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=1&threadid=2&postid=5)] diff --git a/examples/gno.land/r/demo/boards/z_9_filetest.gno b/examples/gno.land/r/demo/boards/z_9_filetest.gno index 66f8950732b..f2703bc1622 100644 --- a/examples/gno.land/r/demo/boards/z_9_filetest.gno +++ b/examples/gno.land/r/demo/boards/z_9_filetest.gno @@ -34,4 +34,4 @@ func main() { // # First Post in (title) // // Body of the first post. (body) -// \- [@gnouser](/r/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:second_board/1/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=2&threadid=1&postid=1&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=2&threadid=1&postid=1)] +// \- [@gnouser](/r/demo/users:gnouser), [2009-02-13 11:31pm (UTC)](/r/demo/boards:second_board/1/1) \[[reply](/r/demo/boards?help&__func=CreateReply&bid=2&threadid=1&postid=1&body.type=textarea)] \[[x](/r/demo/boards?help&__func=DeletePost&bid=2&threadid=1&postid=1)] diff --git a/examples/gno.land/r/demo/foo1155/gno.mod b/examples/gno.land/r/demo/foo1155/gno.mod index 07c05ff5ef3..6fdf18a1658 100644 --- a/examples/gno.land/r/demo/foo1155/gno.mod +++ b/examples/gno.land/r/demo/foo1155/gno.mod @@ -1,7 +1,7 @@ module gno.land/r/demo/foo1155 require ( - "gno.land/p/demo/ufmt" v0.0.0-latest - "gno.land/p/demo/grc/grc1155" v0.0.0-latest - "gno.land/r/demo/users" v0.0.0-latest + gno.land/p/demo/grc/grc1155 v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest + gno.land/r/demo/users v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/foo20/gno.mod b/examples/gno.land/r/demo/foo20/gno.mod index 1dbe9e01e4f..516690ee66c 100644 --- a/examples/gno.land/r/demo/foo20/gno.mod +++ b/examples/gno.land/r/demo/foo20/gno.mod @@ -1,7 +1,7 @@ module gno.land/r/demo/foo20 require ( - "gno.land/p/demo/ufmt" v0.0.0-latest - "gno.land/p/demo/grc/grc20" v0.0.0-latest - "gno.land/r/demo/users" v0.0.0-latest + gno.land/p/demo/grc/grc20 v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest + gno.land/r/demo/users v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/foo721/gno.mod b/examples/gno.land/r/demo/foo721/gno.mod index b34ffd2b3fe..46c19e6ae55 100644 --- a/examples/gno.land/r/demo/foo721/gno.mod +++ b/examples/gno.land/r/demo/foo721/gno.mod @@ -1,7 +1,7 @@ module gno.land/r/demo/foo721 require ( - "gno.land/p/demo/ufmt" v0.0.0-latest - "gno.land/p/demo/grc/grc721" v0.0.0-latest - "gno.land/r/demo/users" v0.0.0-latest + gno.land/p/demo/grc/grc721 v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest + gno.land/r/demo/users v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/groups/README.md b/examples/gno.land/r/demo/groups/README.md index 1db5ae56b51..ecdd5065903 100644 --- a/examples/gno.land/r/demo/groups/README.md +++ b/examples/gno.land/r/demo/groups/README.md @@ -8,7 +8,7 @@ ### - create group - ./build/gnokey maketx call -func "CreateGroup" -args "dao_trinity_ngo" -gas-fee "1000000ugnot" -gas-wanted 4000000 -broadcast true -chainid dev -remote 0.0.0.0:26657 -pkgpath "gno.land/r/demo/groups" test1 + ./build/gnokey maketx call -func "CreateGroup" -args "dao_trinity_ngo" -gas-fee "1000000ugnot" -gas-wanted 4000000 -broadcast -chainid dev -remote 0.0.0.0:26657 -pkgpath "gno.land/r/demo/groups" test1 ### - add member diff --git a/examples/gno.land/r/demo/groups/gno.mod b/examples/gno.land/r/demo/groups/gno.mod index d97acbecc7a..fc6756e13e2 100644 --- a/examples/gno.land/r/demo/groups/gno.mod +++ b/examples/gno.land/r/demo/groups/gno.mod @@ -1,6 +1,6 @@ module gno.land/r/demo/groups require ( - "gno.land/p/demo/avl" v0.0.0-latest - "gno.land/r/demo/users" v0.0.0-latest + gno.land/p/demo/avl v0.0.0-latest + gno.land/r/demo/users v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/keystore/gno.mod b/examples/gno.land/r/demo/keystore/gno.mod index 88d59c9ccd6..af0b907c259 100644 --- a/examples/gno.land/r/demo/keystore/gno.mod +++ b/examples/gno.land/r/demo/keystore/gno.mod @@ -1,6 +1,7 @@ module gno.land/r/demo/keystore require ( - "gno.land/p/demo/ufmt" v0.0.0-latest - "gno.land/p/demo/avl" v0.0.0-latest + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/math_eval/gno.mod b/examples/gno.land/r/demo/math_eval/gno.mod index 69d4e8c459b..0e3fcfe6e9b 100644 --- a/examples/gno.land/r/demo/math_eval/gno.mod +++ b/examples/gno.land/r/demo/math_eval/gno.mod @@ -1,6 +1,6 @@ module gno.land/r/demo/math_eval require ( - "gno.land/p/demo/ufmt" v0.0.0-latest - "gno.land/p/demo/math_eval/int32" v0.0.0-latest + gno.land/p/demo/math_eval/int32 v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/microblog/gno.mod b/examples/gno.land/r/demo/microblog/gno.mod index 3d79a38d5fd..f496b1008ce 100644 --- a/examples/gno.land/r/demo/microblog/gno.mod +++ b/examples/gno.land/r/demo/microblog/gno.mod @@ -1,6 +1,6 @@ module gno.land/r/demo/microblog require ( - "gno.land/p/demo/microblog" v0.0.0-latest - "gno.land/r/demo/users" v0.0.0-latest + gno.land/p/demo/microblog v0.0.0-latest + gno.land/r/demo/users v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/nft/gno.mod b/examples/gno.land/r/demo/nft/gno.mod index 2fefdbd1907..89e0055be51 100644 --- a/examples/gno.land/r/demo/nft/gno.mod +++ b/examples/gno.land/r/demo/nft/gno.mod @@ -1,6 +1,6 @@ module gno.land/r/demo/nft require ( - "gno.land/p/demo/avl" v0.0.0-latest - "gno.land/p/demo/grc/grc721" v0.0.0-latest + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/grc/grc721 v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/releases_example/gno.mod b/examples/gno.land/r/demo/releases_example/gno.mod index 85bcd07f232..22f640fe797 100644 --- a/examples/gno.land/r/demo/releases_example/gno.mod +++ b/examples/gno.land/r/demo/releases_example/gno.mod @@ -1,5 +1,3 @@ module gno.land/r/demo/releases_example -require ( - "gno.land/p/demo/releases" v0.0.0-latest -) +require gno.land/p/demo/releases v0.0.0-latest diff --git a/examples/gno.land/r/demo/tamagotchi/gno.mod b/examples/gno.land/r/demo/tamagotchi/gno.mod new file mode 100644 index 00000000000..b7a2deea2c2 --- /dev/null +++ b/examples/gno.land/r/demo/tamagotchi/gno.mod @@ -0,0 +1,6 @@ +module gno.land/r/demo/tamagotchi + +require ( + gno.land/p/demo/tamagotchi v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest +) diff --git a/examples/gno.land/r/demo/tamagotchi/realm.gno b/examples/gno.land/r/demo/tamagotchi/realm.gno new file mode 100644 index 00000000000..f8f62c9fc7a --- /dev/null +++ b/examples/gno.land/r/demo/tamagotchi/realm.gno @@ -0,0 +1,53 @@ +package tamagotchi + +import ( + "std" + + "gno.land/p/demo/tamagotchi" + "gno.land/p/demo/ufmt" +) + +var t *tamagotchi.Tamagotchi + +func init() { + Reset("gnome#0") +} + +func Reset(optionalName string) string { + name := optionalName + if name == "" { + height := std.GetHeight() + name = ufmt.Sprintf("gnome#%d", height) + } + + t = tamagotchi.New(name) + + return ufmt.Sprintf("A new tamagotchi is born. Their name is %s %s.", t.Name(), t.Face()) +} + +func Feed() string { + t.Feed() + return t.Markdown() +} + +func Play() string { + t.Play() + return t.Markdown() +} + +func Heal() string { + t.Heal() + return t.Markdown() +} + +func Render(path string) string { + tama := t.Markdown() + links := `Actions: +* [Feed](/r/demo/tamagotchi?help&__func=Feed) +* [Play](/r/demo/tamagotchi?help&__func=Play) +* [Heal](/r/demo/tamagotchi?help&__func=Heal) +* [Reset](/r/demo/tamagotchi?help&__func=Reset) +` + + return tama + "\n\n" + links +} diff --git a/examples/gno.land/r/demo/tamagotchi/z0_filetest.gno b/examples/gno.land/r/demo/tamagotchi/z0_filetest.gno new file mode 100644 index 00000000000..373d737b3b5 --- /dev/null +++ b/examples/gno.land/r/demo/tamagotchi/z0_filetest.gno @@ -0,0 +1,28 @@ +package main + +import ( + "std" + "time" + + "gno.land/r/demo/tamagotchi" +) + +func main() { + tamagotchi.Reset("tamagnotchi") + println(tamagotchi.Render("")) +} + +// Output: +// # tamagnotchi 😃 +// +// * age: 0 +// * hunger: 50 +// * happiness: 50 +// * health: 50 +// * sleepy: 0 +// +// Actions: +// * [Feed](/r/demo/tamagotchi?help&__func=Feed) +// * [Play](/r/demo/tamagotchi?help&__func=Play) +// * [Heal](/r/demo/tamagotchi?help&__func=Heal) +// * [Reset](/r/demo/tamagotchi?help&__func=Reset) diff --git a/examples/gno.land/r/demo/tests/gno.mod b/examples/gno.land/r/demo/tests/gno.mod index 23dd3760157..9c5162f848e 100644 --- a/examples/gno.land/r/demo/tests/gno.mod +++ b/examples/gno.land/r/demo/tests/gno.mod @@ -1,6 +1,6 @@ module gno.land/r/demo/tests require ( - "gno.land/p/demo/testutils" v0.0.0-latest - "gno.land/r/demo/tests/subtests" v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest + gno.land/r/demo/tests/subtests v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/tests/subtests/gno.mod b/examples/gno.land/r/demo/tests/subtests/gno.mod index 80db73b4c15..9f466ff77b9 100644 --- a/examples/gno.land/r/demo/tests/subtests/gno.mod +++ b/examples/gno.land/r/demo/tests/subtests/gno.mod @@ -1,4 +1,4 @@ module gno.land/r/demo/tests/subtests -// TODO: this file should not exist. +// TODO: this file should not exist. // This is a temporary workaround. Until https://github.com/gnolang/gno/issues/852 diff --git a/examples/gno.land/r/demo/tests/tests.gno b/examples/gno.land/r/demo/tests/tests.gno index fb49b2273ae..0094ad2ae35 100644 --- a/examples/gno.land/r/demo/tests/tests.gno +++ b/examples/gno.land/r/demo/tests/tests.gno @@ -44,7 +44,7 @@ func (t *TestRealmObject) Modify() { } //---------------------------------------- -// Test helpers to test a particualr realm bug. +// Test helpers to test a particular realm bug. type TestNode struct { Name string diff --git a/examples/gno.land/r/demo/tests_foo/gno.mod b/examples/gno.land/r/demo/tests_foo/gno.mod index b19d8a21de1..226271ae4b0 100644 --- a/examples/gno.land/r/demo/tests_foo/gno.mod +++ b/examples/gno.land/r/demo/tests_foo/gno.mod @@ -1,5 +1,3 @@ module gno.land/r/demo/tests_foo -require ( - "gno.land/r/demo/tests" v0.0.0-latest -) +require gno.land/r/demo/tests v0.0.0-latest diff --git a/examples/gno.land/r/demo/types/gno.mod b/examples/gno.land/r/demo/types/gno.mod index 5709668ef2a..0e86e5d5676 100644 --- a/examples/gno.land/r/demo/types/gno.mod +++ b/examples/gno.land/r/demo/types/gno.mod @@ -1,5 +1,3 @@ module gno.land/r/demo/types -require ( - "gno.land/p/demo/avl" v0.0.0-latest -) +require gno.land/p/demo/avl v0.0.0-latest diff --git a/examples/gno.land/r/demo/ui/gno.mod b/examples/gno.land/r/demo/ui/gno.mod index 597c0f388a4..42be8cec3f0 100644 --- a/examples/gno.land/r/demo/ui/gno.mod +++ b/examples/gno.land/r/demo/ui/gno.mod @@ -1,5 +1,3 @@ module gno.land/r/demo/ui -require ( - "gno.land/p/demo/ui" v0.0.0-latest -) +require gno.land/p/demo/ui v0.0.0-latest diff --git a/examples/gno.land/r/demo/users/gno.mod b/examples/gno.land/r/demo/users/gno.mod index 055b5816871..edd20eb2721 100644 --- a/examples/gno.land/r/demo/users/gno.mod +++ b/examples/gno.land/r/demo/users/gno.mod @@ -1,6 +1,3 @@ module gno.land/r/demo/users -require ( - "gno.land/p/demo/avl" v0.0.0-latest - "gno.land/p/demo/testutils" v0.0.0-latest -) +require gno.land/p/demo/avl v0.0.0-latest diff --git a/examples/gno.land/r/demo/wugnot/gno.mod b/examples/gno.land/r/demo/wugnot/gno.mod new file mode 100644 index 00000000000..1f03ded515c --- /dev/null +++ b/examples/gno.land/r/demo/wugnot/gno.mod @@ -0,0 +1,6 @@ +module gno.land/r/demo/wugnot + +require ( + gno.land/p/demo/grc/grc20 v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest +) diff --git a/examples/gno.land/r/demo/wugnot/wugnot.gno b/examples/gno.land/r/demo/wugnot/wugnot.gno new file mode 100644 index 00000000000..82c3c43db89 --- /dev/null +++ b/examples/gno.land/r/demo/wugnot/wugnot.gno @@ -0,0 +1,126 @@ +package wugnot + +import ( + "std" + "strings" + + "gno.land/p/demo/grc/grc20" + "gno.land/p/demo/ufmt" +) + +var ( + // wugnot is the admin token, able to mint and burn. + wugnot *grc20.AdminToken = grc20.NewAdminToken("wrapped GNOT", "wugnot", 0) + // WUGNOT is the banker usable by users directly. + WUGNOT = wugnot.GRC20() +) + +const ( + ugnotMinDeposit uint64 = 1000 + wugnotMinDeposit uint64 = 1 +) + +// wrapper. +// + +func Deposit() { + caller := std.PrevRealm().Addr() + sent := std.GetOrigSend() + amount := sent.AmountOf("ugnot") + + if uint64(amount) < ugnotMinDeposit { + panic(ufmt.Sprintf("Deposit below minimum: %d/%d ugnot.", amount, ugnotMinDeposit)) + } + wugnot.Mint(caller, uint64(amount)) +} + +func Withdraw(amount uint64) { + if amount < wugnotMinDeposit { + panic(ufmt.Sprintf("Deposit below minimum: %d/%d wugnot.", amount, wugnotMinDeposit)) + } + + caller := std.PrevRealm().Addr() + pkgaddr := std.GetOrigPkgAddr() + + callerBal, _ := wugnot.BalanceOf(caller) + if callerBal < amount { + panic(ufmt.Sprintf("Insufficient balance: %d available, %d needed.", callerBal, amount)) + } + + // send swapped ugnots to caller + banker := std.GetBanker(std.BankerTypeRealmSend) + send := std.Coins{{"ugnot", int64(amount)}} + banker.SendCoins(pkgaddr, caller, send) + wugnot.Burn(caller, amount) +} + +// render. +// + +func Render(path string) string { + parts := strings.Split(path, "/") + c := len(parts) + + switch { + case path == "": + return wugnot.RenderHome() + case c == 2 && parts[0] == "balance": + owner := std.Address(parts[1]) + balance, _ := wugnot.BalanceOf(owner) + return ufmt.Sprintf("%d\n", balance) + default: + return "404\n" + } +} + +// XXX: if we could call WUGNOT.XXX instead of XXX from gnokey, then, all the following lines would not be needed. + +// direct getters. +// XXX: remove them in favor of q_call wugnot.XXX + +func TotalSupply() uint64 { + return wugnot.TotalSupply() +} + +func BalanceOf(owner std.Address) uint64 { + balance, err := wugnot.BalanceOf(owner) + if err != nil { + panic(err) + } + return balance +} + +func Allowance(owner, spender std.Address) uint64 { + allowance, err := wugnot.Allowance(owner, spender) + if err != nil { + panic(err) + } + return allowance +} + +// setters. +// + +func Transfer(to std.Address, amount uint64) { + caller := std.PrevRealm().Addr() + err := wugnot.Transfer(caller, to, amount) + if err != nil { + panic(err) + } +} + +func Approve(spender std.Address, amount uint64) { + caller := std.PrevRealm().Addr() + err := wugnot.Approve(caller, spender, amount) + if err != nil { + panic(err) + } +} + +func TransferFrom(from, to std.Address, amount uint64) { + caller := std.PrevRealm().Addr() + err := wugnot.TransferFrom(caller, from, to, amount) + if err != nil { + panic(err) + } +} diff --git a/examples/gno.land/r/demo/wugnot/z0_filetest.gno b/examples/gno.land/r/demo/wugnot/z0_filetest.gno new file mode 100644 index 00000000000..fa2f45682b1 --- /dev/null +++ b/examples/gno.land/r/demo/wugnot/z0_filetest.gno @@ -0,0 +1,75 @@ +// PKGPATH: gno.land/r/demo/wugnot_test +package wugnot_test + +import ( + "fmt" + "std" + + "gno.land/p/demo/testutils" + "gno.land/r/demo/wugnot" +) + +var ( + addr1 = testutils.TestAddress("test1") + addrc = std.DerivePkgAddr("gno.land/r/demo/wugnot") + addrt = std.DerivePkgAddr("gno.land/r/demo/wugnot_test") +) + +func main() { + std.TestSetOrigPkgAddr(addrc) + std.TestIssueCoins(addrc, std.Coins{{"ugnot", 100000001}}) // TODO: remove this + + // issue ugnots + std.TestIssueCoins(addr1, std.Coins{{"ugnot", 100000001}}) + + // print initial state + printBalances() + // println(wugnot.Render("queues")) + // println("A -", wugnot.Render("")) + + std.TestSetOrigCaller(addr1) + std.TestSetOrigSend(std.Coins{{"ugnot", 123_400}}, nil) + wugnot.Deposit() + printBalances() + wugnot.Withdraw(4242) + printBalances() +} + +func printBalances() { + printSingleBalance := func(name string, addr std.Address) { + wugnotBal := wugnot.BalanceOf(addr) + std.TestSetOrigCaller(addr) + abanker := std.GetBanker(std.BankerTypeOrigSend) + acoins := abanker.GetCoins(addr).AmountOf("ugnot") + bbanker := std.GetBanker(std.BankerTypeRealmIssue) + bcoins := bbanker.GetCoins(addr).AmountOf("ugnot") + cbanker := std.GetBanker(std.BankerTypeRealmSend) + ccoins := cbanker.GetCoins(addr).AmountOf("ugnot") + dbanker := std.GetBanker(std.BankerTypeReadonly) + dcoins := dbanker.GetCoins(addr).AmountOf("ugnot") + fmt.Printf("| %-13s | addr=%s | wugnot=%-5d | ugnot=%-9d %-9d %-9d %-9d |\n", + name, addr, wugnotBal, acoins, bcoins, ccoins, dcoins) + } + println("-----------") + printSingleBalance("wugnot_test", addrt) + printSingleBalance("wugnot", addrc) + printSingleBalance("addr1", addr1) + println("-----------") +} + +// Output: +// ----------- +// | wugnot_test | addr=g19rmydykafrqyyegc8uuaxxpzqwzcnxraj2dev9 | wugnot=0 | ugnot=200000000 200000000 200000000 200000000 | +// | wugnot | addr=g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6 | wugnot=0 | ugnot=100000001 100000001 100000001 100000001 | +// | addr1 | addr=g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7 | wugnot=0 | ugnot=100000001 100000001 100000001 100000001 | +// ----------- +// ----------- +// | wugnot_test | addr=g19rmydykafrqyyegc8uuaxxpzqwzcnxraj2dev9 | wugnot=123400 | ugnot=200000000 200000000 200000000 200000000 | +// | wugnot | addr=g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6 | wugnot=0 | ugnot=100000001 100000001 100000001 100000001 | +// | addr1 | addr=g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7 | wugnot=0 | ugnot=100000001 100000001 100000001 100000001 | +// ----------- +// ----------- +// | wugnot_test | addr=g19rmydykafrqyyegc8uuaxxpzqwzcnxraj2dev9 | wugnot=119158 | ugnot=200004242 200004242 200004242 200004242 | +// | wugnot | addr=g1pf6dv9fjk3rn0m4jjcne306ga4he3mzmupfjl6 | wugnot=0 | ugnot=99995759 99995759 99995759 99995759 | +// | addr1 | addr=g1w3jhxap3ta047h6lta047h6lta047h6l4mfnm7 | wugnot=0 | ugnot=100000001 100000001 100000001 100000001 | +// ----------- diff --git a/examples/gno.land/r/gnoland/blog/gno.mod b/examples/gno.land/r/gnoland/blog/gno.mod index a8b4f3ceaa9..1d64238cdc8 100644 --- a/examples/gno.land/r/gnoland/blog/gno.mod +++ b/examples/gno.land/r/gnoland/blog/gno.mod @@ -1,6 +1,6 @@ module gno.land/r/gnoland/blog require ( - "gno.land/p/demo/avl" v0.0.0-latest - "gno.land/p/demo/blog" v0.0.0-latest + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/blog v0.0.0-latest ) diff --git a/examples/gno.land/r/gnoland/blog/gnoblog.gno b/examples/gno.land/r/gnoland/blog/gnoblog.gno index 2982ea88489..cad84507614 100644 --- a/examples/gno.land/r/gnoland/blog/gnoblog.gno +++ b/examples/gno.land/r/gnoland/blog/gnoblog.gno @@ -23,3 +23,7 @@ func AddComment(postSlug, comment string) { func Render(path string) string { return b.Render(path) } + +func RenderLastPostsWidget(limit int) string { + return b.RenderLastPostsWidget(limit) +} diff --git a/examples/gno.land/r/gnoland/faucet/gno.mod b/examples/gno.land/r/gnoland/faucet/gno.mod index 75e85df326b..693b0e795cf 100644 --- a/examples/gno.land/r/gnoland/faucet/gno.mod +++ b/examples/gno.land/r/gnoland/faucet/gno.mod @@ -1,7 +1,7 @@ module gno.land/r/gnoland/faucet require ( - "gno.land/p/demo/avl" v0.0.0-latest - "gno.land/p/demo/testutils" v0.0.0-latest - "gno.land/p/demo/ufmt" v0.0.0-latest + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest ) diff --git a/examples/gno.land/r/gnoland/faucet/z2_filetest.gno b/examples/gno.land/r/gnoland/faucet/z2_filetest.gno index 1791cd91989..9b59d7c07d3 100644 --- a/examples/gno.land/r/gnoland/faucet/z2_filetest.gno +++ b/examples/gno.land/r/gnoland/faucet/z2_filetest.gno @@ -16,11 +16,11 @@ func main() { ) std.TestSetOrigCaller(adminaddr) err := faucet.AdminAddController(controlleraddr1) - if err != nil { + if err != "" { panic(err) } err = faucet.AdminAddController(controlleraddr2) - if err != nil { + if err != "" { panic(err) } println(faucet.Render("")) diff --git a/examples/gno.land/r/gnoland/faucet/z3_filetest.gno b/examples/gno.land/r/gnoland/faucet/z3_filetest.gno index 1dca56811a9..682a619256a 100644 --- a/examples/gno.land/r/gnoland/faucet/z3_filetest.gno +++ b/examples/gno.land/r/gnoland/faucet/z3_filetest.gno @@ -18,21 +18,21 @@ func main() { ) std.TestSetOrigCaller(adminaddr) err := faucet.AdminAddController(controlleraddr1) - if err != nil { + if err != "" { panic(err) } err = faucet.AdminAddController(controlleraddr2) - if err != nil { + if err != "" { panic(err) } std.TestSetOrigCaller(controlleraddr1) err = faucet.Transfer(testaddr1, 1000000) - if err != nil { + if err != "" { panic(err) } std.TestSetOrigCaller(controlleraddr2) err = faucet.Transfer(testaddr1, 2000000) - if err != nil { + if err != "" { panic(err) } println(faucet.Render("")) diff --git a/examples/gno.land/r/gnoland/home/gno.mod b/examples/gno.land/r/gnoland/home/gno.mod new file mode 100644 index 00000000000..2864958930c --- /dev/null +++ b/examples/gno.land/r/gnoland/home/gno.mod @@ -0,0 +1,7 @@ +module gno.land/r/gnoland/home + +require ( + gno.land/p/demo/ufmt v0.0.0-latest + gno.land/p/demo/ui v0.0.0-latest + gno.land/r/gnoland/blog v0.0.0-latest +) diff --git a/examples/gno.land/r/gnoland/home/home.gno b/examples/gno.land/r/gnoland/home/home.gno new file mode 100644 index 00000000000..a7796dc3f46 --- /dev/null +++ b/examples/gno.land/r/gnoland/home/home.gno @@ -0,0 +1,248 @@ +package home + +import ( + "std" + + "gno.land/p/demo/ufmt" + "gno.land/p/demo/ui" + blog "gno.land/r/gnoland/blog" +) + +// XXX: p/demo/ui API is crappy, we need to make it more idiomatic +// XXX: use an updatable block system to update content from a DAO +// XXX: var blocks avl.Tree + +func Render(_ string) string { + dom := ui.DOM{Prefix: "r/gnoland/home:"} + dom.Title = "Welcome to Gno.land" + + // body + dom.Body.Append(introSection()...) + dom.Body.Append(ui.Jumbotron(worxDAO())) + dom.Body.Append(packageStaffPicks()...) + dom.Body.Append(ui.HR{}) + dom.Body.Append( + ui.Columns{3, []ui.Element{ + lastBlogposts(4), + upcomingEvents(4), + lastContributions(4), + }}, + ) + dom.Body.Append(ui.Jumbotron(discoverLinks())) + + // footer + dom.Footer.Append( + ui.Columns{2, []ui.Element{ + socialLinks(), + quoteOfTheBlock(), + }}, + ) + + // Testnet disclaimer + dom.Footer.Append( + ui.HR{}, + ui.Bold("This is a testnet."), + ui.Text("Package names are not guaranteed to be available for production."), + ) + + return dom.String() +} + +func lastBlogposts(limit int) ui.Element { + posts := blog.RenderLastPostsWidget(limit) + return ui.Element{ + ui.H3("Last Blogposts"), + ui.Text(posts), + } +} + +func lastContributions(limit int) ui.Element { + return ui.Element{ + ui.H3("Last Contributions"), + ui.Text("TODO: import r/gh"), + ui.Link{Text: "#1134", URL: "https://github.com/gnolang/gno/pull/1134"}, + } +} + +func upcomingEvents(limit int) ui.Element { + return ui.Element{ + ui.H3("Upcoming Events"), + ui.Text("TODO: import r/gnoland/events"), + } +} + +func introSection() ui.Element { + return ui.Element{ + ui.H3("An interpretation of the Go (Golang) programming language for advanced developers and intrepid pioneers to build succinct, composable smart contracts for social coordination."), + ui.Paragraph("If you’re concerned about information censorship and want to contribute to the #GnoWorldOrder, follow our socials to find out how."), + ui.Paragraph("Gno.land is in building mode. If you want to help lay the foundations of a fairer and freer world through innovative ideas and exceptional code, join us today."), + } +} + +func worxDAO() ui.Element { + // WorxDAO + // XXX(manfred): please, let me finish a v0, then we can iterate + // highest level == highest responsibility + // teams are responsible for components they don't owne + // flag : realm maintainers VS facilitators + // teams + // committee of trustees to create the directory + // each directory is a name, has a parent and have groups + // homepage team - blocks aggregating events + // XXX: TODO + /*` + # Directory + + * gno.land (owned by group) + * + * gnovm + * gnolang (language) + * gnovm + - current challenges / concerns / issues + * tm2 + * amino + * + + ## Contributors + ``*/ + return ui.Element{ + ui.H3("Contributions (WorxDAO & GoR)"), + ui.Text(`TODO: GoR dashboard + WorxDAO topics`), + } +} + +func quoteOfTheBlock() ui.Element { + quotes := []string{ + "Gno is for Truth.", + "Gno is for Social Coordination.", + "Gno is _not only_ for DeFi.", + "Now, you Gno.", + "Come for the Go, Stay for the Gno.", + } + height := std.GetHeight() + idx := int(height) % len(quotes) + qotb := quotes[idx] + + return ui.Element{ + ui.H3(ufmt.Sprintf("Quote of the ~Day~Block#%d", height)), + ui.Quote(qotb), + } +} + +func socialLinks() ui.Element { + return ui.Element{ + ui.H3("Socials"), + ui.BulletList{ + // XXX: improve UI to support a nice GO api for such links + ui.Text("Check out our [community projects](https://github.com/gnolang/awesome-gno)"), + ui.Text("![Discord](static/img/ico-discord.svg) [Discord](https://discord.gg/S8nKUqwkPn)"), + ui.Text("![Twitter](static/img/ico-twitter.svg) [Twitter](https://twitter.com/_gnoland)"), + ui.Text("![Youtube](static/img/ico-youtube.svg) [Youtube](https://www.youtube.com/@_gnoland)"), + ui.Text("![Telegram](static/img/ico-telegram.svg) [Telegram](https://t.me/gnoland)"), + }, + } +} + +func packageStaffPicks() ui.Element { + // XXX: make it modifiable from a DAO + return ui.Element{ + ui.H3("Explore New Packages and Realms"), + ui.Columns{ + 3, + []ui.Element{ + { + ui.H4("r/gnoland"), + ui.BulletList{ + ui.Link{URL: "r/gnoland/blog"}, + ui.Link{URL: "r/gnoland/dao"}, + ui.Link{URL: "r/gnoland/faucet"}, + ui.Link{URL: "r/gnoland/home"}, + ui.Link{URL: "r/gnoland/pages"}, + }, + ui.H4("r/system"), + ui.BulletList{ + ui.Link{URL: "r/system/names"}, + ui.Link{URL: "r/system/rewards"}, + ui.Link{URL: "r/system/validators"}, + }, + }, { + ui.H4("r/demo"), + ui.BulletList{ + ui.Link{URL: "r/demo/boards"}, + ui.Link{URL: "r/demo/users"}, + ui.Link{URL: "r/demo/banktest"}, + ui.Link{URL: "r/demo/foo20"}, + ui.Link{URL: "r/demo/foo721"}, + ui.Link{URL: "r/demo/microblog"}, + ui.Link{URL: "r/demo/nft"}, + ui.Link{URL: "r/demo/types"}, + ui.Link{URL: "r/demo/art"}, + ui.Link{URL: "r/demo/groups"}, + ui.Text("..."), + }, + }, { + ui.H4("p/demo"), + ui.BulletList{ + ui.Link{URL: "p/demo/avl"}, + ui.Link{URL: "p/demo/blog"}, + ui.Link{URL: "p/demo/ui"}, + ui.Link{URL: "p/demo/ufmt"}, + ui.Link{URL: "p/demo/merkle"}, + ui.Link{URL: "p/demo/bf"}, + ui.Link{URL: "p/demo/flow"}, + ui.Link{URL: "p/demo/gnode"}, + ui.Link{URL: "p/demo/grc/grc20"}, + ui.Link{URL: "p/demo/grc/grc721"}, + ui.Text("..."), + }, + }, + }, + }, + } +} + +func discoverLinks() ui.Element { + return ui.Element{ + ui.Text(`
+
+ +### Learn about Gno.land + +- [About](/about) +- [GitHub](https://github.com/gnolang) +- [Subscribe](#subscribe) +- [Tokenomics (soon)](#) +- [Blog](/blog) +- [Events](/events) +- [Partners, Fund, Grants](/partners) + +
+ +
+ +### Build with Gno + +- [Gno dev with CLI (soon)](#) +- [Explore the Universe](/ecosystem) +- [Test in the browser (soon)](#) +- [About the Gno Language](/gnolang) +- [Docs/ Tutorials](https://github.com/gnolang) +- [Gno by example](https://gno-by-example.com/) +- [Getting started video (soon)](#) + +
+
+ +### Explore the universe + +- [Discover demo packages](https://github.com/gnolang/gno/tree/master/examples) +- [Install Gno Key instructions](/r/demo/boards:testboard/5) +- [Testnets 3](https://test3.gno.land/) +- [Testnets 2](https://test2.gno.land/) +- [Explorer links(soon)](#) +- [Testnet Tokens (faucet)](https://test3.gno.land/faucet) + +
+
`), + } +} diff --git a/examples/gno.land/r/gnoland/home/home_filetest.gno b/examples/gno.land/r/gnoland/home/home_filetest.gno new file mode 100644 index 00000000000..014bdf96136 --- /dev/null +++ b/examples/gno.land/r/gnoland/home/home_filetest.gno @@ -0,0 +1,179 @@ +package main + +import "gno.land/r/gnoland/home" + +func main() { + println(home.Render("")) +} + +// Output: +// # Welcome to Gno.land +// +// ### An interpretation of the Go (Golang) programming language for advanced developers and intrepid pioneers to build succinct, composable smart contracts for social coordination. +// +// +// If you’re concerned about information censorship and want to contribute to the #GnoWorldOrder, follow our socials to find out how. +// +// +// Gno.land is in building mode. If you want to help lay the foundations of a fairer and freer world through innovative ideas and exceptional code, join us today. +// +//
+// +// ### Contributions (WorxDAO & GoR) +// +// TODO: GoR dashboard + WorxDAO topics +//
+// +// ### Explore New Packages and Realms +// +//
+//
+// +// #### r/gnoland +// +// - [r/gnoland/blog](r/gnoland/blog) +// - [r/gnoland/dao](r/gnoland/dao) +// - [r/gnoland/faucet](r/gnoland/faucet) +// - [r/gnoland/home](r/gnoland/home) +// - [r/gnoland/pages](r/gnoland/pages) +// +// #### r/system +// +// - [r/system/names](r/system/names) +// - [r/system/rewards](r/system/rewards) +// - [r/system/validators](r/system/validators) +// +//
+//
+// +// #### r/demo +// +// - [r/demo/boards](r/demo/boards) +// - [r/demo/users](r/demo/users) +// - [r/demo/banktest](r/demo/banktest) +// - [r/demo/foo20](r/demo/foo20) +// - [r/demo/foo721](r/demo/foo721) +// - [r/demo/microblog](r/demo/microblog) +// - [r/demo/nft](r/demo/nft) +// - [r/demo/types](r/demo/types) +// - [r/demo/art](r/demo/art) +// - [r/demo/groups](r/demo/groups) +// - ... +// +//
+//
+// +// #### p/demo +// +// - [p/demo/avl](p/demo/avl) +// - [p/demo/blog](p/demo/blog) +// - [p/demo/ui](p/demo/ui) +// - [p/demo/ufmt](p/demo/ufmt) +// - [p/demo/merkle](p/demo/merkle) +// - [p/demo/bf](p/demo/bf) +// - [p/demo/flow](p/demo/flow) +// - [p/demo/gnode](p/demo/gnode) +// - [p/demo/grc/grc20](p/demo/grc/grc20) +// - [p/demo/grc/grc721](p/demo/grc/grc721) +// - ... +// +//
+//
+// +// +// --- +// +//
+//
+// +// ### Last Blogposts +// +// +//
+//
+// +// ### Upcoming Events +// +// TODO: import r/gnoland/events +//
+//
+// +// ### Last Contributions +// +// TODO: import r/gh +// [#1134](https://github.com/gnolang/gno/pull/1134) +//
+//
+// +//
+// +//
+//
+// +// ### Learn about Gno.land +// +// - [About](/about) +// - [GitHub](https://github.com/gnolang) +// - [Subscribe](#subscribe) +// - [Tokenomics (soon)](#) +// - [Blog](/blog) +// - [Events](/events) +// - [Partners, Fund, Grants](/partners) +// +//
+// +//
+// +// ### Build with Gno +// +// - [Gno dev with CLI (soon)](#) +// - [Explore the Universe](/ecosystem) +// - [Test in the browser (soon)](#) +// - [About the Gno Language](/gnolang) +// - [Docs/ Tutorials](https://github.com/gnolang) +// - [Gno by example](https://gno-by-example.com/) +// - [Getting started video (soon)](#) +// +//
+//
+// +// ### Explore the universe +// +// - [Discover demo packages](https://github.com/gnolang/gno/tree/master/examples) +// - [Install Gno Key instructions](/r/demo/boards:testboard/5) +// - [Testnets 3](https://test3.gno.land/) +// - [Testnets 2](https://test2.gno.land/) +// - [Explorer links(soon)](#) +// - [Testnet Tokens (faucet)](https://test3.gno.land/faucet) +// +//
+//
+//
+// +// +//
+//
+// +// ### Socials +// +// - Check out our [community projects](https://github.com/gnolang/awesome-gno) +// - ![Discord](static/img/ico-discord.svg) [Discord](https://discord.gg/S8nKUqwkPn) +// - ![Twitter](static/img/ico-twitter.svg) [Twitter](https://twitter.com/_gnoland) +// - ![Youtube](static/img/ico-youtube.svg) [Youtube](https://www.youtube.com/@_gnoland) +// - ![Telegram](static/img/ico-telegram.svg) [Telegram](https://t.me/gnoland) +// +//
+//
+// +// ### Quote of the ~Day~Block#123 +// +// > Now, you Gno. +// +//
+//
+// +// +// --- +// +// **This is a testnet.** +// Package names are not guaranteed to be available for production. diff --git a/examples/gno.land/r/gnoland/pages/gno.mod b/examples/gno.land/r/gnoland/pages/gno.mod index 0f5c4076509..31e9ad2c85b 100644 --- a/examples/gno.land/r/gnoland/pages/gno.mod +++ b/examples/gno.land/r/gnoland/pages/gno.mod @@ -1,6 +1,6 @@ module gno.land/r/gnoland/pages require ( - "gno.land/p/demo/avl" v0.0.0-latest - "gno.land/p/demo/blog" v0.0.0-latest + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/blog v0.0.0-latest ) diff --git a/gno.land/cmd/gnoweb/pages/ABOUT.md b/examples/gno.land/r/gnoland/pages/page_about.gno similarity index 55% rename from gno.land/cmd/gnoweb/pages/ABOUT.md rename to examples/gno.land/r/gnoland/pages/page_about.gno index a5678a7349a..80c43c1741d 100644 --- a/gno.land/cmd/gnoweb/pages/ABOUT.md +++ b/examples/gno.land/r/gnoland/pages/page_about.gno @@ -1,12 +1,20 @@ -# About Gno.land +package gnopages -Gno.land is a platform to write smart contracts in Gnolang (Gno). -Using an interpreted version of the general-purpose programming language Golang (Go), developers can write smart contracts and other blockchain apps without having to learn a language that’s exclusive to a single ecosystem. +func init() { + path := "about" + title := "Gno.land Is A Platform To Write Smart Contracts In Gno" + // XXX: description := "On Gno.land, developers write smart contracts and other blockchain apps using Gno without learning a language that’s exclusive to a single ecosystem." + body := `# About Gno.land + +Gno.land is a platform to write smart contracts in Gno. +Using an interpreted version of the general-purpose programming language Go (Golang), developers can write smart contracts and other blockchain apps without having to learn a language that’s exclusive to a single ecosystem. Web2 developers can easily contribute to web3 and start building a more transparent, accountable world. The Gno transaction token, GNOT, and the contributor memberships power the platform, which runs on a variation of Proof of Stake. Proof of Contribution rewards contributors from technical and non-technical backgrounds, fairly and for life with GNOT. This consensus mechanism also achieves higher security with fewer validators, optimizing resources for a greener, more sustainable, and enduring blockchain ecosystem. -Any blockchain using Gnolang achieves succinctness, composability, expressivity, and completeness not found in any other smart contract platform. -By observing a minimal structure, the design can endure over time and challenge the regime of information censorship we’re living in today. +Any blockchain using Gno achieves succinctness, composability, expressivity, and completeness not found in any other smart contract platform. +By observing a minimal structure, the design can endure over time and challenge the regime of information censorship we’re living in today.` + _ = b.NewPost("", path, title, body, nil) +} diff --git a/examples/gno.land/r/gnoland/pages/page_ecosystem.gno b/examples/gno.land/r/gnoland/pages/page_ecosystem.gno new file mode 100644 index 00000000000..68969c44529 --- /dev/null +++ b/examples/gno.land/r/gnoland/pages/page_ecosystem.gno @@ -0,0 +1,35 @@ +package gnopages + +func init() { + var ( + path = "ecosystem" + title = "Discover Gno.land Ecosystem Projects & Initiatives" + // XXX: description = "Dive further into the Gno.land ecosystem and discover the core infrastructure, projects, smart contracts, and tooling we’re building." + body = `# Gno Ecosystem + +## Gno.land Space + +For the best onboarding experience, head over to [Gno.land Space](https://www.gnoland.space/) open ecosystem. Here you can set up your Gno wallet, explore existing community-written Gno smart contracts (realms), and become part of our vibrant community by joining [Gno.land Discord](https://discord.com/invite/x76qK4ttHC). + +## Gno Studio (IDE) + +Gno IDE is a web-based application helping builders quickly spin up Gno realms and packages right on their browsers. Offering a smooth and intuitive UX for building on Gno, you’ll find multiple modes for customizability with all the features you’d expect from an IDE, such as auto compilation in the editor, debugging, and extensive testing capability. + +## Gnoscan + +Developed by the Onbloc team, Gnoscan is Gno.land’s blockchain explorer. Anyone can use Gnoscan to easily find information that resides on the Gno.land blockchain, such as wallet addresses, TX hashes, blocks, and contracts. Gnoscan makes our on-chain data easy to read and intuitive to discover. [Go to Gnoscan.](https://gnoscan.io/) + +## Adena + +Adena is a user-friendly non-custodial wallet for Gno.land. Open-source and developed by Onbloc, Adena currently powers all transactions on Gno.land, allowing gnomes to interact easily with the chain. With an emphasis on UX, Adena is built to handle millions of realms and tokens with a high-quality interface, support for NFTs and custom tokens, and seamless integration. [Get started here.](https://adena.app/) + +## Gnoswap + +Gnoswap is currently under development and led by the Onbloc team. Gnoswap will be the first DEX on Gno.land and is an automated market maker (AMM) protocol written in Gnolang that allows for permissionless token exchanges on the platform. + +## Gno.land Developer Portal + +Through the Gno.land Developer Portal, new developers can explore the exciting world of Gnolang (Gno), a novel programming language that powers the Gno.land blockchain. If you want to interact with Gno.land, start writing a realm, build a dApp, or even port a Solidity contract to a Gnolang realm, you’ll find the resources to [get started here](https://docs.onbloc.xyz/).` + ) + _ = b.NewPost("", path, title, body, nil) +} diff --git a/examples/gno.land/r/gnoland/pages/page_events.gno b/examples/gno.land/r/gnoland/pages/page_events.gno new file mode 100644 index 00000000000..18e7faeb3d3 --- /dev/null +++ b/examples/gno.land/r/gnoland/pages/page_events.gno @@ -0,0 +1,151 @@ +package gnopages + +func init() { + var ( + path = "events" + title = "Gno.land Core Team Attends Industry Events & Meetups" + // XXX: description = "If you’re interested in learning more about Gno.land, you can join us at major blockchain industry events throughout the year either in person or virtually." + body = `# Events + +If you’re interested in building web3 with us, catch up with Gno.land in person at one of our industry events. We’re looking to connect with developers and like-minded thinkers who can contribute to the growth of our platform. + +--- + +## Upcoming Events + +
+
+ +### EthCC + +- **Come Meet Us at our Booth** +- Paris, July 17 - 20, 2023 +- Manfred Touron + +[Learn more](https://www.ethcc.io/) + +
+
+ +### Nebular Summit Gno.land for Developers + +- Paris, July 24 - 25, 2023 +- Manfred Touron + +[Learn more](https://www.nebular.builders/) + +
+
+ +### GopherCon EU + +- **Come Meet Us at our Booth** +- Berlin, July 26 - 29, 2023 + +[Learn more](https://gophercon.eu/) + +
+ +
+ +### GopherCon US + +- **Come Meet Us at our Booth** +- San Diego, September 26 - 29, 2023 + +[Learn more](https://www.gophercon.com/) + +
+
+ +--- + +## Past Events + +
+ +
+ +### Eth Seoul + +- **The Evolution of Smart Contracts: A Journey into Gno.land** +- Seoul, June 3, 2023 +- Manfred Touron + +[Learn more](https://2023.ethseoul.org/) + +
+
+ +### BUIDL Asia + +- **Proof of Contribution in Gno.land** +- Seoul, June 6, 2023 +- Manfred Touron + +[Learn more](https://www.buidl.asia/) + +
+
+ +### Game Developer Conference + +- **Side Event: Web3 Gaming Apps Powered by Gno** +- San Francisco, Mach 23, 2023 +- Jae Kwon + +[Watch the talk](https://www.youtube.com/watch?v=IJ0xel8lr4c) + +
+
+ +### EthDenver + +- **Side Event: Discover Gno.land** +- Denver, Feb 24 - Mar 5, 2023 +- Jae Kwon + +[Watch the talk](https://www.youtube.com/watch?v=IJ0xel8lr4c) + +
+
+ +### Istanbul Blockchain Week + +- Istanbul, Nov 14 - 17, 2022 +- Manfred Touron + +[Watch the talk](https://www.youtube.com/watch?v=JX0gdWT0Cg4) + +
+
+ +### Web Summit Buckle Up and Build with Cosmos + +- Lisbon, Nov 1 - 4, 2022 +- Manfred Touron + +
+
+ +### Cosmoverse + +- Medallin, Sept 26 - 28, 2022 +- Manfred Touron + +[Watch the talk](https://www.youtube.com/watch?v=6s1zG7hgxMk) + +
+
+ +### Berlin Blockchain Week Buckle Up and Build with Cosmos + +- Berlin, Sept 11 - 18, 2022 + +[Watch the talk](https://www.youtube.com/watch?v=hCLErPgnavI) + +
+
` + ) + _ = b.NewPost("", path, title, body, nil) +} diff --git a/examples/gno.land/r/gnoland/pages/page_gnolang.gno b/examples/gno.land/r/gnoland/pages/page_gnolang.gno new file mode 100644 index 00000000000..ecbadab9f01 --- /dev/null +++ b/examples/gno.land/r/gnoland/pages/page_gnolang.gno @@ -0,0 +1,43 @@ +package gnopages + +func init() { + var ( + path = "gnolang" + title = "Gno Is a Complete Language for Blockchain" + // XXX: description = "Gnolang (Gno) is an interpretation of the popular Golang (Go) language for blockchain created by Tendermint and Cosmos founder Jae Kwon." + body = `# About the Gno, the Language for Gno.land + +[Gno](https://github.com/gnolang/gno/blob/master/LICENSE.md) is an interpretation of the widely-used Go (Golang) programming language for blockchain created by Cosmos co-founder Jae Kwon in 2022 to mark a new era in smart contracting. Gno is ~99% identical to Go, so Go programmers can start coding in Gno right away, with a minimal learning curve. For example, Gno comes with blockchain-specific standard libraries, but any code that doesn’t use blockchain-specific logic can run in Go with minimal processing. Libraries that don’t make sense in the blockchain context, such as network or operating-system access, are not available in Gno. Otherwise, Gno loads and uses many standard libraries that power Go, so most of the parsing of the source code is the same. + +Under the hood, the Gno code is parsed into an abstract syntax tree (AST) and the AST itself is used in the interpreter, rather than bytecode as in many virtual machines such as Java, Python, or Wasm. This makes even the GnoVM accessible to any Go programmer. The novel design of the intuitive GnoVM interpreter allows Gno to freeze and resume the program by persisting and loading the entire memory state. Gno is deterministic, auto-persisted, and auto-Merkle-ized, allowing (smart contract) programs to be succinct, as the programmer doesn’t have to serialize and deserialize objects to persist them into a database (unlike programming applications with the Cosmos SDK). + +## How Gno Differs from Go + +![Gno and Go differences](static/img/gno-language/go-and-gno.jpg) + +The composable nature of Go/Gno allows for type-checked interactions between contracts, making Gno.land safer and more powerful, as well as operationally cheaper and faster. Smart contracts on Gno.land are light, simple, more focused, and easily interoperable—a network of interconnected contracts rather than siloed monoliths that limit interactions with other contracts. + +![Example of Gno code](static/img/gno-language/code-example.jpg) + +## Gno Inherits Go’s Built-in Security Features + +Go supports secure programming through exported/non-exported fields, enabling a “least-authority” design. It is easy to create objects and APIs that expose only what should be accessible to callers while hiding what should not be simply by the capitalization of letters, thus allowing a succinct representation of secure logic that can be called by multiple users. + +Another major advantage of Go is that the language comes with an ecosystem of great tooling, like the compiler and third-party tools that statically analyze code. Gno inherits these advantages from Go directly to create a smart contract programming language that provides embedding, composability, type-check safety, and garbage collection, helping developers to write secure code relying on the compiler, parser, and interpreter to give warning alerts for common mistakes. + +## Gno vs Solidity + +The most widely-adopted smart contract language today is Ethereum’s EVM-compatible Solidity. With bytecode built from the ground up and Turing complete, Solidity opened up a world of possibilities for decentralized applications (dApps) and there are currently more than 10 million contracts deployed on Ethereum. However, Solidity provides limited tooling and its EVM has a stack limit and computational inefficiencies. + +Solidity is designed for one purpose only (writing smart contracts) and is bound by the limitations of the EVM. In addition, developers have to learn several languages if they want to understand the whole stack or work across different ecosystems. Gno aspires to exceed Solidity on multiple fronts (and other smart contract languages like CosmWasm or Substrate) as every part of the stack is written in Gno. It’s easy for developers to understand the entire system just by studying a relatively small code base. + +## Gno Is Essential for the Wider Adoption of Web3 + +Gno makes imports as easy as they are in web2 with runtime-based imports for seamless dependency flow comprehension, and support for complex structs, beyond primitive types. Gno is ultimately cost-effective as dependencies are loaded once, enabling remote function calls as local, and providing automatic and independent per-realm state persistence. + +Using Gno, developers can rapidly accelerate application development and adopt a modular structure by reusing and reassembling existing modules without building from scratch. They can embed one structure inside another in an intuitive way while preserving localism, and the language specification is simple, successfully balancing practicality and minimalism. + +The Go language is so well designed that the Gno smart contract system will become the new gold standard for smart contract development and other blockchain applications. As a programming language that is universally adopted, secure, composable, and complete, Gno is essential for the broader adoption of web3 and its sustainable growth.` + ) + _ = b.NewPost("", path, title, body, nil) +} diff --git a/examples/gno.land/r/gnoland/pages/page_gor.gno b/examples/gno.land/r/gnoland/pages/page_gor.gno new file mode 100644 index 00000000000..3a6bb022e09 --- /dev/null +++ b/examples/gno.land/r/gnoland/pages/page_gor.gno @@ -0,0 +1,221 @@ +package gnopages + +func init() { + path := "gor" + title := "Game of Realms Content For The Best Contributors" + // XXX: description := "Game of Realms is the first high-stakes competition held in two phases to find the best contributors to the Gno.land platform with a 133,700 ATOM prize pool." + body := `# Game of Realms + +
+ +### Game of Realms + +The first high-stakes contest will see participants compete for tiered membership to co-own the Gno.land blockchain. A series of complex technical and non-technical tasks will challenge contributors to create innovative patterns that push the chain to new limits. Start building the foundation for tomorrow through key smart contracts and other contributions that change our understanding of the world. + +
+ +The competition is currently in phase one – for advanced developers only. + +Once the necessary tools to start phase two are ready, we’ll open up the competition to newer devs and non-technical contributors. + +If you want to stack ATOM rewards and play a key role in the success of Gno.land and web3, read more about Game of Realms or open a [PR](https://github.com/gnolang/gno/) today. + +
+ +
+
+
+ +## Phase I. (ongoing) + +- + +- + +- + +
+
+ +## Phase II. (Locked) + +
+
+
+ +
+ +
+ +## Evaluation DAO + +This complex challenge seeks your skills in DAO development and implementation and is one of the most important challenges of phase one. The Evaluation DAO will ensure that contributions in Game of Realms and the Gno.land platform are fairly rewarded. + +
+ + + + + + + +
+ +Game of Realms participants and core contributors are still in discussions, proposing additional ideas, and seeing how the proposal for the Evaluation DAO evolves over time. + +
+ + + +
+ +See [GitHub issue 519](https://github.com/gnolang/gno/issues/519) for the most up-to-date discussion so far on how voting should work for the DAO, what the responsibilities are, how to join, etc. + +
+ + + + + + + + + + + + + + + + + +
+
+ +
+ +## Tutorials + +To progress to phase two of the competition, we need high-quality tutorials, guides, and documentation from phase one participants. Help to create materials that will onboard more contributors to Gno.land. + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ +## Governance Module + +Can you define and implement a governance contract suite that rivals existing ones, such as the Cosmos Hub? Show us how! We’re looking for the fairest and most efficient governance solution possible. + +
+ + + + + + + +
+ +Game of Realms participants and core contributors have made significant progress teaming up to complete this challenge but discussions and additional ideas are still ongoing. + +
+ + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +## Register Now + + +
+
+ +
+
+ + +
+ +
+ + +
+ + +
+
+
+ +` + _ = b.NewPost("", path, title, body, nil) +} diff --git a/examples/gno.land/r/gnoland/pages/page_partners.gno b/examples/gno.land/r/gnoland/pages/page_partners.gno new file mode 100644 index 00000000000..440302437fa --- /dev/null +++ b/examples/gno.land/r/gnoland/pages/page_partners.gno @@ -0,0 +1,21 @@ +package gnopages + +func init() { + path := "partners" + title := "Partners" + // XXX: description := """ + body := `## Partnerships + +### Fund and Grants Program + +Are you a builder, tinkerer, or researcher? If you’re looking to create awesome dApps, tooling, infrastructure, or smart contract libraries on Gno.land, you can apply for a grant. The Gno.land Ecosystem Fund and Grants program provides financial contributions for individuals and teams to innovate on the platform. + +
+ +[More information here](https://github.com/gnolang/ecosystem-fund-grants) + +
+` + + _ = b.NewPost("", path, title, body, nil) +} diff --git a/examples/gno.land/r/gnoland/pages/page_start.gno b/examples/gno.land/r/gnoland/pages/page_start.gno new file mode 100644 index 00000000000..a36ec6e52b1 --- /dev/null +++ b/examples/gno.land/r/gnoland/pages/page_start.gno @@ -0,0 +1,21 @@ +package gnopages + +func init() { + path := "start" + title := "Getting Started with Gno" + // XXX: description := "" + + // TODO: codegen to use README files here + + /* TODO: port previous message: This is a demo of Gno smart contract programming. This document was + constructed by Gno onto a smart contract hosted on the data Realm + name ["gno.land/r/demo/boards"](https://gno.land/r/demo/boards/) + ([github](https://github.com/gnolang/gno/tree/master/examples/gno.land/r/demo/boards)). + */ + body := `## Getting Started with Gno + +- [Install Gno Key](/r/demo/boards:testboard/5) +- TODO: add more links +` + _ = b.NewPost("", path, title, body, nil) +} diff --git a/examples/gno.land/r/gnoland/pages/page_testnets.gno b/examples/gno.land/r/gnoland/pages/page_testnets.gno new file mode 100644 index 00000000000..b6c09ab71ee --- /dev/null +++ b/examples/gno.land/r/gnoland/pages/page_testnets.gno @@ -0,0 +1,19 @@ +package gnopages + +func init() { + path := "testnets" + title := "Gno.land Testnets" + // XXX: description := """ + body := `## Other testnets + +- **[staging.gno.land](https://staging.gno.land) (wiped every commit to master)** +- _[test3.gno.land](https://test3.gno.land) (latest)_ +- _[test2.gno.land](https://test2.gno.land) (archive)_ +- _[test1.gno.land](https://test1.gno.land) (archive)_ + +## Local devnet + +See CONTRIBUTING.md on GitHub. +` + _ = b.NewPost("", path, title, body, nil) +} diff --git a/examples/gno.land/r/gnoland/pages/page_tokenomics.gno b/examples/gno.land/r/gnoland/pages/page_tokenomics.gno new file mode 100644 index 00000000000..de899ae0a70 --- /dev/null +++ b/examples/gno.land/r/gnoland/pages/page_tokenomics.gno @@ -0,0 +1,11 @@ +package gnopages + +func init() { + var ( + path = "tokenomics" + title = "Gno.land Tokenomics" + // XXX: description = """ + body = `Lorem Ipsum` + ) + _ = b.NewPost("", path, title, body, nil) +} diff --git a/examples/gno.land/r/gnoland/pages/pages.gno b/examples/gno.land/r/gnoland/pages/pages.gno index dbc3d855880..6e1f117d1d5 100644 --- a/examples/gno.land/r/gnoland/pages/pages.gno +++ b/examples/gno.land/r/gnoland/pages/pages.gno @@ -4,16 +4,12 @@ import ( "gno.land/p/demo/blog" ) -var b = &blog.Blog{ - Title: "Gnoland's Pages", - Prefix: "/r/gnoland/pages:", -} +// TODO: switch from p/blog to p/pages -func init() { - _ = b.NewPost("", "gor", "Game of Realms", "Lorem Ipsum", nil) - _ = b.NewPost("", "events", "Events", "Lorem Ipsum", nil) - _ = b.NewPost("", "tokenomics", "Tokenomics", "Lorem Ipsum", nil) - _ = b.NewPost("", "start", "Getting Started", "Lorem Ipsum", nil) +var b = &blog.Blog{ + Title: "Gnoland's Pages", + Prefix: "/r/gnoland/pages:", + NoBreadcrumb: true, } func Render(path string) string { diff --git a/examples/gno.land/r/gnoland/pages/pages_test.gno b/examples/gno.land/r/gnoland/pages/pages_test.gno index 1a43153e2c8..0119ac78985 100644 --- a/examples/gno.land/r/gnoland/pages/pages_test.gno +++ b/examples/gno.land/r/gnoland/pages/pages_test.gno @@ -6,48 +6,42 @@ import ( "testing" ) -func TestPackage(t *testing.T) { - std.TestSetOrigCaller(std.Address("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq")) - - author := std.GetOrigCaller() - - // by default, lorem ipsum posts - { - got := Render("") - expected := ` -# Gnoland's Pages - -
- -## [Events](/r/gnoland/pages:p/events) -**[Learn More](/r/gnoland/pages:p/events)** - -
- -## [Game of Realms](/r/gnoland/pages:p/gor) -**[Learn More](/r/gnoland/pages:p/gor)** - -
- -## [Getting Started](/r/gnoland/pages:p/start) -**[Learn More](/r/gnoland/pages:p/start)** - -
- -## [Tokenomics](/r/gnoland/pages:p/tokenomics) -**[Learn More](/r/gnoland/pages:p/tokenomics)** - -
-` - assertMDEquals(t, got, expected) +func TestHome(t *testing.T) { + printedOnce := false + got := Render("") + expectedSubtrings := []string{ + "/r/gnoland/pages:p/events", + "/r/gnoland/pages:p/tokenomics", + "/r/gnoland/pages:p/start", + "/r/gnoland/pages:p/gor", + "/r/gnoland/pages:p/about", + "/r/gnoland/pages:p/gnolang", + } + for _, substring := range expectedSubtrings { + if !strings.Contains(got, substring) { + if !printedOnce { + println(got) + printedOnce = true + } + t.Errorf("expected %q, but not found.", substring) + } } } -func assertMDEquals(t *testing.T, got, expected string) { - t.Helper() - expected = strings.TrimSpace(expected) - got = strings.TrimSpace(got) - if expected != got { - t.Errorf("invalid render output.\nexpected %q.\ngot %q.", expected, got) +func TestAbout(t *testing.T) { + printedOnce := false + got := Render("p/about") + expectedSubtrings := []string{ + "# About Gno.land", + "Gno.land is a platform to write smart contracts in Gno.", + } + for _, substring := range expectedSubtrings { + if !strings.Contains(got, substring) { + if !printedOnce { + println(got) + printedOnce = true + } + t.Errorf("expected %q, but not found.", substring) + } } } diff --git a/examples/gno.land/r/manfred/README.md b/examples/gno.land/r/manfred/README.md new file mode 100644 index 00000000000..e562f6eeb70 --- /dev/null +++ b/examples/gno.land/r/manfred/README.md @@ -0,0 +1,41 @@ +# Manfred's Personal Realms on gno.land + +## Introduction + +Welcome to the root directory of Manfred's personal realms on `gno.land`. Each +realm within this directory serves a specific purpose and offers a glimpse into +Manfred's interests, projects, and more. + +## ⚠️ Disclaimer + +This is a very early iteration and is subject to change frequently. The +structure and content of these realms are still in the experimental phase. The +concept was inspired by Jae Kwon's idea to manage his to-do list in +`jaekwon/home`. If you're using or referencing these realms, be prepared for +regular updates and modifications. + +## Structure + +- `manfred/home`: This is the main realm where Manfred lists items and topics of + interest to him. + `manfred/config`: Configuration used by other realms. +- `manfred/...`: Coming soon. + +## Usage + +Each realm can be accessed and interacted with individually. Typically, realms +implement a `Render()` function which presents the main content or functionality +of that realm. + +## Access Control + +All realms in this directory have a built-in security check to ensure that only +Manfred can interact with the contents in a meaningful way. Unauthorized access +attempts might be able to see the content but will not be able to make +modifications or access certain functionalities. + +## Contributions & Collaboration + +These realms are personal by nature. If you've stumbled upon them and have +suggestions or want to collaborate on a particular topic, please reach out +directly to Manfred. diff --git a/examples/gno.land/r/manfred/config/config.gno b/examples/gno.land/r/manfred/config/config.gno new file mode 100644 index 00000000000..23e90df50ff --- /dev/null +++ b/examples/gno.land/r/manfred/config/config.gno @@ -0,0 +1,20 @@ +package config + +import "std" + +var addr = std.Address("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq") + +func Addr() std.Address { + return addr +} + +func UpdateAddr(newAddr std.Address) { + AssertIsAdmin() + addr = newAddr +} + +func AssertIsAdmin() { + if std.GetOrigCaller() != addr { + panic("restricted area") + } +} diff --git a/examples/gno.land/r/manfred/config/gno.mod b/examples/gno.land/r/manfred/config/gno.mod new file mode 100644 index 00000000000..516bf38528e --- /dev/null +++ b/examples/gno.land/r/manfred/config/gno.mod @@ -0,0 +1 @@ +module gno.land/r/manfred/config diff --git a/examples/gno.land/r/manfred/home/gno.mod b/examples/gno.land/r/manfred/home/gno.mod new file mode 100644 index 00000000000..6e7aac70cc7 --- /dev/null +++ b/examples/gno.land/r/manfred/home/gno.mod @@ -0,0 +1,3 @@ +module gno.land/r/manfred/home + +require gno.land/r/manfred/config v0.0.0-latest diff --git a/examples/gno.land/r/manfred/home/home.gno b/examples/gno.land/r/manfred/home/home.gno new file mode 100644 index 00000000000..720796a2201 --- /dev/null +++ b/examples/gno.land/r/manfred/home/home.gno @@ -0,0 +1,56 @@ +package home + +import "gno.land/r/manfred/config" + +var ( + todos []string + status string + memeImgURL string +) + +func init() { + todos = append(todos, "fill this todo list...") + status = "Online" // Initial status set to "Online" + memeImgURL = "https://i.imgflip.com/7ze8dc.jpg" +} + +func Render(path string) string { + content := "# Manfred's (gn)home Dashboard\n\n" + + content += "## Meme\n" + content += "![](" + memeImgURL + ")\n\n" + + content += "## Status\n" + content += status + "\n\n" + + content += "## Personal ToDo List\n" + for _, todo := range todos { + content += "- [ ] " + todo + "\n" + } + content += "\n" + + // TODO: Implement a feature to list replies on r/boards on my posts + // TODO: Maybe integrate a calendar feature for upcoming events? + + return content +} + +func AddNewTodo(todo string) { + config.AssertIsAdmin() + todos = append(todos, todo) +} + +func DeleteTodo(todoIndex int) { + config.AssertIsAdmin() + if todoIndex >= 0 && todoIndex < len(todos) { + // Remove the todo from the list by merging slices from before and after the todo + todos = append(todos[:todoIndex], todos[todoIndex+1:]...) + } else { + panic("Invalid todo index") + } +} + +func UpdateStatus(newStatus string) { + config.AssertIsAdmin() + status = newStatus +} diff --git a/examples/gno.land/r/manfred/home/z1_filetest.gno b/examples/gno.land/r/manfred/home/z1_filetest.gno new file mode 100644 index 00000000000..801efedb306 --- /dev/null +++ b/examples/gno.land/r/manfred/home/z1_filetest.gno @@ -0,0 +1,19 @@ +package main + +import "gno.land/r/manfred/home" + +func main() { + println(home.Render("")) +} + +// Output: +// # Manfred's (gn)home Dashboard +// +// ## Meme +// ![](https://i.imgflip.com/7ze8dc.jpg) +// +// ## Status +// Online +// +// ## Personal ToDo List +// - [ ] fill this todo list... diff --git a/examples/gno.land/r/manfred/home/z2_filetest.gno b/examples/gno.land/r/manfred/home/z2_filetest.gno new file mode 100644 index 00000000000..316fd400867 --- /dev/null +++ b/examples/gno.land/r/manfred/home/z2_filetest.gno @@ -0,0 +1,35 @@ +package main + +import ( + "std" + + "gno.land/r/manfred/home" +) + +func main() { + std.TestSetOrigCaller("g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq") + home.AddNewTodo("aaa") + home.AddNewTodo("bbb") + home.AddNewTodo("ccc") + home.AddNewTodo("ddd") + home.AddNewTodo("eee") + home.UpdateStatus("Lorem Ipsum") + home.DeleteTodo(3) + println(home.Render("")) +} + +// Output: +// # Manfred's (gn)home Dashboard +// +// ## Meme +// ![](https://i.imgflip.com/7ze8dc.jpg) +// +// ## Status +// Lorem Ipsum +// +// ## Personal ToDo List +// - [ ] fill this todo list... +// - [ ] aaa +// - [ ] bbb +// - [ ] ddd +// - [ ] eee diff --git a/examples/gno.land/r/manfred/present/admin.gno b/examples/gno.land/r/manfred/present/admin.gno new file mode 100644 index 00000000000..ff0cb075656 --- /dev/null +++ b/examples/gno.land/r/manfred/present/admin.gno @@ -0,0 +1,92 @@ +package present + +import ( + "std" + "strings" + + "gno.land/p/demo/avl" +) + +var ( + adminAddr std.Address + moderatorList avl.Tree + inPause bool +) + +func init() { + // adminAddr = std.GetOrigCaller() // FIXME: find a way to use this from the main's genesis. + adminAddr = "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq" +} + +func AdminSetAdminAddr(addr std.Address) { + assertIsAdmin() + adminAddr = addr +} + +func AdminSetInPause(state bool) { + assertIsAdmin() + inPause = state +} + +func AdminAddModerator(addr std.Address) { + assertIsAdmin() + moderatorList.Set(addr.String(), true) +} + +func AdminRemoveModerator(addr std.Address) { + assertIsAdmin() + moderatorList.Set(addr.String(), false) // XXX: delete instead? +} + +func ModAddPost(slug, title, body, tags string) { + assertIsModerator() + + caller := std.GetOrigCaller() + tagList := strings.Split(tags, ",") + err := b.NewPost(caller, slug, title, body, tagList) + checkErr(err) +} + +func ModEditPost(slug, title, body, tags string) { + assertIsModerator() + + tagList := strings.Split(tags, ",") + err := b.GetPost(slug).Update(title, body, tagList) + checkErr(err) +} + +func isAdmin(addr std.Address) bool { + return addr == adminAddr +} + +func isModerator(addr std.Address) bool { + _, found := moderatorList.Get(addr.String()) + return found +} + +func assertIsAdmin() { + caller := std.GetOrigCaller() + if !isAdmin(caller) { + panic("access restricted.") + } +} + +func assertIsModerator() { + caller := std.GetOrigCaller() + if isAdmin(caller) || isModerator(caller) { + return + } + panic("access restricted") +} + +func assertNotInPause() { + if inPause { + panic("access restricted (pause)") + } +} + +func checkErr(err error) { + if err != nil { + panic(err) + } +} diff --git a/examples/gno.land/r/manfred/present/gno.mod b/examples/gno.land/r/manfred/present/gno.mod new file mode 100644 index 00000000000..5d50447e0e0 --- /dev/null +++ b/examples/gno.land/r/manfred/present/gno.mod @@ -0,0 +1,6 @@ +module gno.land/r/manfred/present + +require ( + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/blog v0.0.0-latest +) diff --git a/examples/gno.land/r/manfred/present/present_miami23.gno b/examples/gno.land/r/manfred/present/present_miami23.gno new file mode 100644 index 00000000000..36b1980bb0b --- /dev/null +++ b/examples/gno.land/r/manfred/present/present_miami23.gno @@ -0,0 +1,44 @@ +package present + +func init() { + path := "miami23" + title := "Portal Loop Demo (Miami 2023)" + body := ` +# Portal Loop Demo (Miami 2023) + +Rendered by Gno. + +[Source (WIP)](https://github.com/gnolang/gno/pull/1176) + +## Portal Loop + +- DONE: Dynamic homepage, key pages, aliases, and redirects. +- TODO: Deploy with history, complete worxdao v0. +- Will replace the static gno.land site. +- Enhances local development. + +[GitHub Issue](https://github.com/gnolang/gno/issues/1108) + +## Roadmap + +- Crafting the roadmap this week, open to collaboration. +- Combining onchain (portal loop) and offchain (GitHub). +- Next week: Unveiling the official v0 roadmap. + +## Teams, DAOs, Projects + +- Developing worxDAO contracts for directories of projects and teams. +- GitHub teams and projects align with this structure. +- CODEOWNER file updates coming. +- Initial teams announced next week. + +## Tech Team Retreat Plan + +- Continue Portal Loop. +- Consider dApp development. +- Explore new topics [here](https://github.com/orgs/gnolang/projects/15/). +- Engage in workshops. +- Connect and have fun with colleagues. +` + _ = b.NewPost(adminAddr, path, title, body, []string{"demo", "portal-loop", "miami"}) +} diff --git a/examples/gno.land/r/manfred/present/present_miami23_filetest.gno b/examples/gno.land/r/manfred/present/present_miami23_filetest.gno new file mode 100644 index 00000000000..05c41905060 --- /dev/null +++ b/examples/gno.land/r/manfred/present/present_miami23_filetest.gno @@ -0,0 +1,57 @@ +package main + +import "gno.land/r/manfred/present" + +func main() { + println(present.Render("")) + println("------------------------------------") + println(present.Render("p/miami23")) +} + +// Output: +//
+// +// ## [Portal Loop Demo (Miami 2023)](/r/manfred/present:p/miami23) +// **[Learn More](/r/manfred/present:p/miami23)** +// +//
+// ------------------------------------ +// # Portal Loop Demo (Miami 2023) +// +// Rendered by Gno. +// +// [Source (WIP)](https://github.com/gnolang/gno/pull/1176) +// +// ## Portal Loop +// +// - DONE: Dynamic homepage, key pages, aliases, and redirects. +// - TODO: Deploy with history, complete worxdao v0. +// - Will replace the static gno.land site. +// - Enhances local development. +// +// [GitHub Issue](https://github.com/gnolang/gno/issues/1108) +// +// ## Roadmap +// +// - Crafting the roadmap this week, open to collaboration. +// - Combining onchain (portal loop) and offchain (GitHub). +// - Next week: Unveiling the official v0 roadmap. +// +// ## Teams, DAOs, Projects +// +// - Developing worxDAO contracts for directories of projects and teams. +// - GitHub teams and projects align with this structure. +// - CODEOWNER file updates coming. +// - Initial teams announced next week. +// +// ## Tech Team Retreat Plan +// +// - Continue Portal Loop. +// - Consider dApp development. +// - Explore new topics [here](https://github.com/orgs/gnolang/projects/15/). +// - Engage in workshops. +// - Connect and have fun with colleagues. +// +// [#demo](/r/manfred/present:t/demo) [#portal-loop](/r/manfred/present:t/portal-loop) [#miami](/r/manfred/present:t/miami) +// +// by g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq on 1970-01-01 12:00am UTC diff --git a/examples/gno.land/r/manfred/present/presentations.gno b/examples/gno.land/r/manfred/present/presentations.gno new file mode 100644 index 00000000000..8a99f502e86 --- /dev/null +++ b/examples/gno.land/r/manfred/present/presentations.gno @@ -0,0 +1,17 @@ +package present + +import ( + "gno.land/p/demo/blog" +) + +// TODO: switch from p/blog to p/present + +var b = &blog.Blog{ + Title: "Manfred's Presentations", + Prefix: "/r/manfred/present:", + NoBreadcrumb: true, +} + +func Render(path string) string { + return b.Render(path) +} diff --git a/examples/gno.land/r/system/names/gno.mod b/examples/gno.land/r/system/names/gno.mod index 31c456f90e0..cd4fd0aae4a 100644 --- a/examples/gno.land/r/system/names/gno.mod +++ b/examples/gno.land/r/system/names/gno.mod @@ -1,5 +1,3 @@ module gno.land/r/system/names -require ( - "gno.land/p/demo/avl" v0.0.0-latest -) +require gno.land/p/demo/avl v0.0.0-latest diff --git a/examples/gno.land/r/x/manfred_outfmt/gno.mod b/examples/gno.land/r/x/manfred_outfmt/gno.mod index eef8ec5956e..e6f705c46b9 100644 --- a/examples/gno.land/r/x/manfred_outfmt/gno.mod +++ b/examples/gno.land/r/x/manfred_outfmt/gno.mod @@ -1,6 +1,6 @@ module gno.land/r/x/manfred_outfmt require ( - "gno.land/p/demo/rand" v0.0.0-latest - "gno.land/p/demo/ufmt" v0.0.0-latest + gno.land/p/demo/rand v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest ) diff --git a/examples/gno.land/r/x/nir1218_evaluation_proposal/EVALUATION.md b/examples/gno.land/r/x/nir1218_evaluation_proposal/EVALUATION.md index b1dc2de58df..4926713e8fa 100644 --- a/examples/gno.land/r/x/nir1218_evaluation_proposal/EVALUATION.md +++ b/examples/gno.land/r/x/nir1218_evaluation_proposal/EVALUATION.md @@ -51,7 +51,7 @@ An example of a category is a bounty, a chore, a defect, or a document. A contribution is associated with a pull request. A contribution has an evaluation life cycle. A submission time is set when a contribution is added. -A last evaluation time is set when a contribution is evaluated and approved by a memeber. +A last evaluation time is set when a contribution is evaluated and approved by a member. An approval time is set when a contribution is approved by all members (or when a future threshold is reached) #### Submission diff --git a/examples/gno.land/r/x/nir1218_evaluation_proposal/committee.gno b/examples/gno.land/r/x/nir1218_evaluation_proposal/committee.gno index ed939b56fdb..1ec801bb971 100644 --- a/examples/gno.land/r/x/nir1218_evaluation_proposal/committee.gno +++ b/examples/gno.land/r/x/nir1218_evaluation_proposal/committee.gno @@ -9,7 +9,7 @@ import ( type Committee struct { members []std.Address // TODO - use avl tree or address set? - categories avl.Tree // A catagory is mapped to a list of evaluation criteria + categories avl.Tree // A category is mapped to a list of evaluation criteria evaluation *Evaluation } @@ -84,7 +84,7 @@ func (c *Committee) AddContribution(pr *PullRequest, contributor std.Address) (c if !c.isMember(std.GetOrigCaller()) { return -1, false } - // Check the category of the PR matches a catagory this committee evaluates + // Check the category of the PR matches a category this committee evaluates // TODO check the category is an approved category if c.categories.Has(pr.category) { return c.evaluation.AddContribution(pr, contributor) diff --git a/gno.land/Makefile b/gno.land/Makefile index 117de18fc46..f4f9dd1aae2 100644 --- a/gno.land/Makefile +++ b/gno.land/Makefile @@ -5,23 +5,37 @@ help: rundep=go run -modfile ../misc/devdeps/go.mod +gnoland.start: + @#TODO: remove this makefile directive in a few weeks. + @echo "DEPRECATED: use 'start.gnoland' instead of 'gnoland.start'" + go run ./cmd/gnoland start + +.PHONY: start.gnoland +start.gnoland:; go run ./cmd/gnoland start + +.PHONY: start.gnoweb +start.gnoweb:; go run ./cmd/gnoweb + .PHONY: build -build: build.gnoland build.gnokey build.gnoweb build.gnofaucet build.gnotxsync +build: build.gnoland build.gnokey build.gnoweb build.gnofaucet build.genesis build.gnoland:; go build -o build/gnoland ./cmd/gnoland build.gnoweb:; go build -o build/gnoweb ./cmd/gnoweb build.gnofaucet:; go build -o build/gnofaucet ./cmd/gnofaucet build.gnokey:; go build -o build/gnokey ./cmd/gnokey -build.gnotxsync:; go build -o build/gnotxsync ./cmd/gnotxsync +build.genesis:; go build -o build/genesis ./cmd/genesis + +run.gnoland:; go run ./cmd/gnoland start +run.gnoweb:; go run ./cmd/gnoweb .PHONY: install -install: install.gnoland install.gnoweb install.gnofaucet install.gnokey install.gnotxsync +install: install.gnoland install.gnoweb install.gnofaucet install.gnokey install.genesis install.gnoland:; go install ./cmd/gnoland install.gnoweb:; go install ./cmd/gnoweb install.gnofaucet:; go install ./cmd/gnofaucet install.gnokey:; go install ./cmd/gnokey -install.gnotxsync:; go install ./cmd/gnotxsync +install.genesis:; go install ./cmd/genesis .PHONY: fclean fclean: clean @@ -40,6 +54,11 @@ GOFMT_FLAGS ?= -w fmt: $(rundep) mvdan.cc/gofumpt $(GOFMT_FLAGS) . +.PHONY: imports +GOIMPORTS_FLAGS ?= -w +imports: + $(rundep) golang.org/x/tools/cmd/goimports $(GOIMPORTS_FLAGS) . + ######################################## # Test suite .PHONY: test @@ -47,7 +66,10 @@ test: _test.gnoland _test.gnoweb _test.gnokey _test.pkgs GOTEST_FLAGS ?= -v -p 1 -timeout=30m -_test.gnoland:; go test $(GOTEST_FLAGS) ./cmd/gnoland -_test.gnoweb:; go test $(GOTEST_FLAGS) ./cmd/gnoweb -_test.gnokey:; go test $(GOTEST_FLAGS) ./cmd/gnokey -_test.pkgs:; go test $(GOTEST_FLAGS) ./pkg/... +_test.gnoland:; go test $(GOTEST_FLAGS) ./cmd/gnoland +_test.gnoweb:; go test $(GOTEST_FLAGS) ./cmd/gnoweb +_test.gnokey:; go test $(GOTEST_FLAGS) ./cmd/gnokey +_test.pkgs:; go test $(GOTEST_FLAGS) ./pkg/... +_test.pkgs.sync:; UPDATE_SCRIPTS=true go test $(GOTEST_FLAGS) ./pkg/... + + diff --git a/gno.land/cmd/genesis/balances.go b/gno.land/cmd/genesis/balances.go new file mode 100644 index 00000000000..0e81f280f33 --- /dev/null +++ b/gno.land/cmd/genesis/balances.go @@ -0,0 +1,39 @@ +package main + +import ( + "flag" + + "github.com/gnolang/gno/tm2/pkg/commands" +) + +type balancesCfg struct { + commonCfg +} + +// newBalancesCmd creates the genesis balances subcommand +func newBalancesCmd(io commands.IO) *commands.Command { + cfg := &balancesCfg{} + + cmd := commands.NewCommand( + commands.Metadata{ + Name: "balances", + ShortUsage: "balances [flags]", + LongHelp: "Manipulates the initial genesis.json account balances (pre-mines)", + ShortHelp: "Manages genesis.json account balances", + }, + cfg, + commands.HelpExec, + ) + + cmd.AddSubCommands( + newBalancesAddCmd(cfg, io), + newBalancesRemoveCmd(cfg, io), + newBalancesExportCmd(cfg, io), + ) + + return cmd +} + +func (c *balancesCfg) RegisterFlags(fs *flag.FlagSet) { + c.commonCfg.RegisterFlags(fs) +} diff --git a/gno.land/cmd/genesis/balances_add.go b/gno.land/cmd/genesis/balances_add.go new file mode 100644 index 00000000000..d1e88efcc6b --- /dev/null +++ b/gno.land/cmd/genesis/balances_add.go @@ -0,0 +1,348 @@ +package main + +import ( + "bufio" + "context" + "errors" + "flag" + "fmt" + "io" + "os" + "strings" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/sdk/bank" + "github.com/gnolang/gno/tm2/pkg/std" + + _ "github.com/gnolang/gno/gno.land/pkg/sdk/vm" +) + +var ( + errNoBalanceSource = errors.New("at least one balance source must be set") + errBalanceParsingAborted = errors.New("balance parsing aborted") + errInvalidAddress = errors.New("invalid address encountered") +) + +type balancesAddCfg struct { + rootCfg *balancesCfg + + balanceSheet string + singleEntries commands.StringArr + parseExport string +} + +// newBalancesAddCmd creates the genesis balances add subcommand +func newBalancesAddCmd(rootCfg *balancesCfg, io commands.IO) *commands.Command { + cfg := &balancesAddCfg{ + rootCfg: rootCfg, + } + + return commands.NewCommand( + commands.Metadata{ + Name: "add", + ShortUsage: "balances add [flags]", + LongHelp: "Adds a new validator to the genesis.json", + }, + cfg, + func(ctx context.Context, _ []string) error { + return execBalancesAdd(ctx, cfg, io) + }, + ) +} + +func (c *balancesAddCfg) RegisterFlags(fs *flag.FlagSet) { + fs.StringVar( + &c.balanceSheet, + "balance-sheet", + "", + "the path to the balance file containing addresses in the format
=ugnot", + ) + + fs.Var( + &c.singleEntries, + "single", + "the direct balance addition in the format
=ugnot", + ) + + fs.StringVar( + &c.parseExport, + "parse-export", + "", + "the path to the transaction export containing a list of transactions (JSONL)", + ) +} + +func execBalancesAdd(ctx context.Context, cfg *balancesAddCfg, io commands.IO) error { + // Load the genesis + genesis, loadErr := types.GenesisDocFromFile(cfg.rootCfg.genesisPath) + if loadErr != nil { + return fmt.Errorf("unable to load genesis, %w", loadErr) + } + + // Validate the source is set correctly + var ( + singleEntriesSet = len(cfg.singleEntries) != 0 + balanceSheetSet = cfg.balanceSheet != "" + txFileSet = cfg.parseExport != "" + ) + + if !singleEntriesSet && !balanceSheetSet && !txFileSet { + return errNoBalanceSource + } + + finalBalances := make(accountBalances) + + // Get the balance sheet from the source + if singleEntriesSet { + balances, err := getBalancesFromEntries(cfg.singleEntries) + if err != nil { + return fmt.Errorf("unable to get balances from entries, %w", err) + } + + finalBalances.leftMerge(balances) + } + + if balanceSheetSet { + // Open the balance sheet + file, loadErr := os.Open(cfg.balanceSheet) + if loadErr != nil { + return fmt.Errorf("unable to open balance sheet, %w", loadErr) + } + + balances, err := getBalancesFromSheet(file) + if err != nil { + return fmt.Errorf("unable to get balances from balance sheet, %w", err) + } + + finalBalances.leftMerge(balances) + } + + if txFileSet { + // Open the transactions file + file, loadErr := os.Open(cfg.parseExport) + if loadErr != nil { + return fmt.Errorf("unable to open transactions file, %w", loadErr) + } + + balances, err := getBalancesFromTransactions(ctx, io, file) + if err != nil { + return fmt.Errorf("unable to get balances from tx file, %w", err) + } + + finalBalances.leftMerge(balances) + } + + // Initialize genesis app state if it is not initialized already + if genesis.AppState == nil { + genesis.AppState = gnoland.GnoGenesisState{} + } + + // Construct the initial genesis balance sheet + state := genesis.AppState.(gnoland.GnoGenesisState) + genesisBalances, err := mapGenesisBalancesFromState(state) + if err != nil { + return err + } + + // Merge the two balance sheets, with the input + // having precedence over the genesis balances + finalBalances.leftMerge(genesisBalances) + + // Save the balances + state.Balances = finalBalances.toList() + genesis.AppState = state + + // Save the updated genesis + if err := genesis.SaveAs(cfg.rootCfg.genesisPath); err != nil { + return fmt.Errorf("unable to save genesis.json, %w", err) + } + + io.Printfln( + "%d pre-mines saved", + len(finalBalances), + ) + + io.Println() + + for address, balance := range finalBalances { + io.Printfln("%s:%dugnot", address.String(), balance) + } + + return nil +} + +// getBalancesFromEntries extracts the balance entries +// from the array of balance +func getBalancesFromEntries(entries []string) (accountBalances, error) { + balances := make(accountBalances) + + for _, entry := range entries { + var balance gnoland.Balance + if err := balance.Parse(entry); err != nil { + return nil, fmt.Errorf("unable to parse balance entry: %w", err) + } + balances[balance.Address] = balance + } + + return balances, nil +} + +// getBalancesFromSheet extracts the balance sheet from the passed in +// balance sheet file, that has the format of
=ugnot +func getBalancesFromSheet(sheet io.Reader) (accountBalances, error) { + // Parse the balances + balances := make(accountBalances) + scanner := bufio.NewScanner(sheet) + + for scanner.Scan() { + entry := scanner.Text() + + // Remove comments + entry = strings.Split(entry, "#")[0] + entry = strings.TrimSpace(entry) + + // Skip empty lines + if entry == "" { + continue + } + + var balance gnoland.Balance + if err := balance.Parse(entry); err != nil { + return nil, fmt.Errorf("unable to extract balance data, %w", err) + } + + balances[balance.Address] = balance + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error encountered while scanning, %w", err) + } + + return balances, nil +} + +// getBalancesFromTransactions constructs a balance map based on MsgSend messages. +// This way of determining the final balance sheet is not valid, since it doesn't take into +// account different message types (ex. MsgCall) that can initialize accounts with some balance values. +// The right way to do this sort of initialization is to spin up an in-memory node +// and execute the entire transaction history to determine touched accounts and final balances, +// and construct a balance sheet based off of this information +func getBalancesFromTransactions( + ctx context.Context, + io commands.IO, + reader io.Reader, +) (accountBalances, error) { + balances := make(accountBalances) + + scanner := bufio.NewScanner(reader) + + for scanner.Scan() { + select { + case <-ctx.Done(): + return nil, errBalanceParsingAborted + default: + // Parse the amino JSON + var tx std.Tx + + line := scanner.Bytes() + + if err := amino.UnmarshalJSON(line, &tx); err != nil { + io.ErrPrintfln( + "invalid amino JSON encountered: %q", + string(line), + ) + + continue + } + + feeAmount := std.NewCoins(tx.Fee.GasFee) + if feeAmount.AmountOf("ugnot") <= 0 { + io.ErrPrintfln( + "invalid gas fee amount encountered: %q", + tx.Fee.GasFee.String(), + ) + } + + for _, msg := range tx.Msgs { + if msg.Type() != "send" { + continue + } + + msgSend := msg.(bank.MsgSend) + + sendAmount := msgSend.Amount + if sendAmount.AmountOf("ugnot") <= 0 { + io.ErrPrintfln( + "invalid send amount encountered: %s", + msgSend.Amount.String(), + ) + continue + } + + // This way of determining final account balances is not really valid, + // because we take into account only the ugnot transfer messages (MsgSend) + // and not other message types (like MsgCall), that can also + // initialize accounts with some balances. Because of this, + // we can run into a situation where a message send amount or fee + // causes an accounts balance to go < 0. In these cases, + // we initialize the account (it is present in the balance sheet), but + // with the balance of 0 + + from := balances[msgSend.FromAddress].Amount + to := balances[msgSend.ToAddress].Amount + + to = to.Add(sendAmount) + + if from.IsAllLT(sendAmount) || from.IsAllLT(feeAmount) { + // Account cannot cover send amount / fee + // (see message above) + from = std.NewCoins(std.NewCoin("ugnot", 0)) + } + + if from.IsAllGT(sendAmount) { + from = from.Sub(sendAmount) + } + + if from.IsAllGT(feeAmount) { + from = from.Sub(feeAmount) + } + + // Set new balance + balances[msgSend.FromAddress] = gnoland.Balance{ + Address: msgSend.FromAddress, + Amount: from, + } + balances[msgSend.ToAddress] = gnoland.Balance{ + Address: msgSend.ToAddress, + Amount: to, + } + } + } + } + + // Check for scanning errors + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf( + "error encountered while reading file, %w", + err, + ) + } + + return balances, nil +} + +// mapGenesisBalancesFromState extracts the initial account balances from the +// genesis app state +func mapGenesisBalancesFromState(state gnoland.GnoGenesisState) (accountBalances, error) { + // Construct the initial genesis balance sheet + genesisBalances := make(accountBalances) + + for _, balance := range state.Balances { + genesisBalances[balance.Address] = balance + } + + return genesisBalances, nil +} diff --git a/gno.land/cmd/genesis/balances_add_test.go b/gno.land/cmd/genesis/balances_add_test.go new file mode 100644 index 00000000000..73e2fe148a2 --- /dev/null +++ b/gno.land/cmd/genesis/balances_add_test.go @@ -0,0 +1,704 @@ +package main + +import ( + "bytes" + "context" + "fmt" + "math" + "strconv" + "strings" + "testing" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/sdk/bank" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/gnolang/gno/tm2/pkg/testutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGenesis_Balances_Add(t *testing.T) { + t.Parallel() + + t.Run("invalid genesis", func(t *testing.T) { + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "balances", + "add", + "--genesis-path", + "dummy-path", + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.ErrorContains(t, cmdErr, errUnableToLoadGenesis.Error()) + }) + + t.Run("no sources selected", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "balances", + "add", + "--genesis-path", + tempGenesis.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errNoBalanceSource.Error()) + }) + + t.Run("invalid genesis path", func(t *testing.T) { + t.Parallel() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "balances", + "add", + "--genesis-path", + "dummy-path", + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errUnableToLoadGenesis.Error()) + }) + + t.Run("balances from entries", func(t *testing.T) { + t.Parallel() + + dummyKeys := getDummyKeys(t, 2) + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "balances", + "add", + "--genesis-path", + tempGenesis.Name(), + } + + amount := std.NewCoins(std.NewCoin("ugnot", 10)) + + for _, dummyKey := range dummyKeys { + args = append(args, "--single") + args = append( + args, + fmt.Sprintf( + "%s=%dugnot", + dummyKey.Address().String(), + amount.AmountOf("ugnot"), + ), + ) + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Validate the genesis was updated + genesis, loadErr := types.GenesisDocFromFile(tempGenesis.Name()) + require.NoError(t, loadErr) + + require.NotNil(t, genesis.AppState) + + state, ok := genesis.AppState.(gnoland.GnoGenesisState) + require.True(t, ok) + + require.Equal(t, len(dummyKeys), len(state.Balances)) + + for _, balance := range state.Balances { + // Find the appropriate key + // (the genesis is saved with randomized balance order) + found := false + for _, dummyKey := range dummyKeys { + if dummyKey.Address().String() == balance.Address.String() { + assert.Equal(t, amount, balance.Amount) + + found = true + break + } + } + + if !found { + t.Fatalf("unexpected entry with address %s found", balance.Address.String()) + } + } + }) + + t.Run("balances from sheet", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + dummyKeys := getDummyKeys(t, 10) + amount := std.NewCoins(std.NewCoin("ugnot", 10)) + + balances := make([]string, len(dummyKeys)) + + // Add a random comment to the balances file output + balances = append(balances, "#comment\n") + + for index, key := range dummyKeys { + balances[index] = fmt.Sprintf( + "%s=%dugnot", + key.Address().String(), + amount.AmountOf("ugnot"), + ) + } + + // Write the balance sheet to a file + balanceSheet, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + _, err := balanceSheet.WriteString(strings.Join(balances, "\n")) + require.NoError(t, err) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "balances", + "add", + "--genesis-path", + tempGenesis.Name(), + "--balance-sheet", + balanceSheet.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Validate the genesis was updated + genesis, loadErr := types.GenesisDocFromFile(tempGenesis.Name()) + require.NoError(t, loadErr) + + require.NotNil(t, genesis.AppState) + + state, ok := genesis.AppState.(gnoland.GnoGenesisState) + require.True(t, ok) + + require.Equal(t, len(dummyKeys), len(state.Balances)) + + for _, balance := range state.Balances { + // Find the appropriate key + // (the genesis is saved with randomized balance order) + found := false + for _, dummyKey := range dummyKeys { + if dummyKey.Address().String() == balance.Address.String() { + assert.Equal(t, amount, balance.Amount) + + found = true + break + } + } + + if !found { + t.Fatalf("unexpected entry with address %s found", balance.Address.String()) + } + } + }) + + t.Run("balances from transactions", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + var ( + dummyKeys = getDummyKeys(t, 10) + amount = std.NewCoins(std.NewCoin("ugnot", 10)) + amountCoins = std.NewCoins(std.NewCoin("ugnot", 10)) + gasFee = std.NewCoin("ugnot", 1000000) + txs = make([]std.Tx, 0) + ) + + sender := dummyKeys[0] + for _, dummyKey := range dummyKeys[1:] { + tx := std.Tx{ + Msgs: []std.Msg{ + bank.MsgSend{ + FromAddress: sender.Address(), + ToAddress: dummyKey.Address(), + Amount: amountCoins, + }, + }, + Fee: std.Fee{ + GasWanted: 10, + GasFee: gasFee, + }, + Signatures: make([]std.Signature, 0), + } + + txs = append(txs, tx) + } + + // Marshal the transactions into amino JSON + marshalledTxs := make([]string, 0, len(txs)) + + for _, tx := range txs { + marshalledTx, err := amino.MarshalJSON(tx) + require.NoError(t, err) + + marshalledTxs = append(marshalledTxs, string(marshalledTx)) + } + + // Write the transactions to a file + txsFile, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + _, err := txsFile.WriteString(strings.Join(marshalledTxs, "\n")) + require.NoError(t, err) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "balances", + "add", + "--genesis-path", + tempGenesis.Name(), + "--parse-export", + txsFile.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Validate the genesis was updated + genesis, loadErr := types.GenesisDocFromFile(tempGenesis.Name()) + require.NoError(t, loadErr) + + require.NotNil(t, genesis.AppState) + + state, ok := genesis.AppState.(gnoland.GnoGenesisState) + require.True(t, ok) + + require.Equal(t, len(dummyKeys), len(state.Balances)) + + for _, balance := range state.Balances { + // Find the appropriate key + // (the genesis is saved with randomized balance order) + found := false + for index, dummyKey := range dummyKeys { + checkAmount := amount + if index == 0 { + // the first address should + // have a balance of 0 + checkAmount = std.NewCoins(std.NewCoin("ugnot", 0)) + } + + if dummyKey.Address().String() == balance.Address.String() { + assert.True(t, balance.Amount.IsEqual(checkAmount)) + + found = true + break + } + } + + if !found { + t.Fatalf("unexpected entry with address %s found", balance.Address.String()) + } + } + }) + + t.Run("balances overwrite", func(t *testing.T) { + t.Parallel() + + dummyKeys := getDummyKeys(t, 10) + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + state := gnoland.GnoGenesisState{ + // Set an initial balance value + Balances: []gnoland.Balance{ + { + Address: dummyKeys[0].Address(), + Amount: std.NewCoins(std.NewCoin("ugnot", 100)), + }, + }, + } + genesis.AppState = state + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "balances", + "add", + "--genesis-path", + tempGenesis.Name(), + } + + amount := std.NewCoins(std.NewCoin("ugnot", 10)) + + for _, dummyKey := range dummyKeys { + args = append(args, "--single") + args = append( + args, + fmt.Sprintf( + "%s=%dugnot", + dummyKey.Address().String(), + amount.AmountOf("ugnot"), + ), + ) + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Validate the genesis was updated + genesis, loadErr := types.GenesisDocFromFile(tempGenesis.Name()) + require.NoError(t, loadErr) + + require.NotNil(t, genesis.AppState) + + state, ok := genesis.AppState.(gnoland.GnoGenesisState) + require.True(t, ok) + + require.Equal(t, len(dummyKeys), len(state.Balances)) + + for _, balance := range state.Balances { + // Find the appropriate key + // (the genesis is saved with randomized balance order) + found := false + for _, dummyKey := range dummyKeys { + if dummyKey.Address().String() == balance.Address.String() { + assert.Equal(t, amount, balance.Amount) + + found = true + break + } + } + + if !found { + t.Fatalf("unexpected entry with address %s found", balance.Address.String()) + } + } + }) +} + +func TestBalances_GetBalancesFromEntries(t *testing.T) { + t.Parallel() + + t.Run("valid balances", func(t *testing.T) { + t.Parallel() + + // Generate dummy keys + dummyKeys := getDummyKeys(t, 2) + amount := std.NewCoins(std.NewCoin("ugnot", 10)) + + balances := make([]string, len(dummyKeys)) + + for index, key := range dummyKeys { + balances[index] = fmt.Sprintf( + "%s=%dugnot", + key.Address().String(), + amount.AmountOf("ugnot"), + ) + } + + balanceMap, err := getBalancesFromEntries(balances) + require.NoError(t, err) + + // Validate the balance map + assert.Len(t, balanceMap, len(dummyKeys)) + for _, key := range dummyKeys { + assert.Equal(t, amount, balanceMap[key.Address()].Amount) + } + }) + + t.Run("malformed balance, invalid format", func(t *testing.T) { + t.Parallel() + + balances := []string{ + "malformed balance", + } + + balanceMap, err := getBalancesFromEntries(balances) + + assert.Nil(t, balanceMap) + assert.ErrorContains(t, err, "malformed entry") + }) + + t.Run("malformed balance, invalid address", func(t *testing.T) { + t.Parallel() + + balances := []string{ + "dummyaddress=10ugnot", + } + + balanceMap, err := getBalancesFromEntries(balances) + + assert.Nil(t, balanceMap) + assert.ErrorContains(t, err, "invalid address") + }) + + t.Run("malformed balance, invalid amount", func(t *testing.T) { + t.Parallel() + + dummyKey := getDummyKey(t) + + balances := []string{ + fmt.Sprintf( + "%s=%sugnot", + dummyKey.Address().String(), + strconv.FormatUint(math.MaxUint64, 10), + ), + } + + balanceMap, err := getBalancesFromEntries(balances) + + assert.Nil(t, balanceMap) + assert.ErrorContains(t, err, "invalid amount") + }) +} + +func TestBalances_GetBalancesFromSheet(t *testing.T) { + t.Parallel() + + t.Run("valid balances", func(t *testing.T) { + t.Parallel() + + // Generate dummy keys + dummyKeys := getDummyKeys(t, 2) + amount := std.NewCoins(std.NewCoin("ugnot", 10)) + + balances := make([]string, len(dummyKeys)) + + for index, key := range dummyKeys { + balances[index] = fmt.Sprintf( + "%s=%dugnot", + key.Address().String(), + amount.AmountOf("ugnot"), + ) + } + + reader := strings.NewReader(strings.Join(balances, "\n")) + balanceMap, err := getBalancesFromSheet(reader) + require.NoError(t, err) + + // Validate the balance map + assert.Len(t, balanceMap, len(dummyKeys)) + for _, key := range dummyKeys { + assert.Equal(t, amount, balanceMap[key.Address()].Amount) + } + }) + + t.Run("malformed balance, invalid amount", func(t *testing.T) { + t.Parallel() + + dummyKey := getDummyKey(t) + + balances := []string{ + fmt.Sprintf( + "%s=%sugnot", + dummyKey.Address().String(), + strconv.FormatUint(math.MaxUint64, 10), + ), + } + + reader := strings.NewReader(strings.Join(balances, "\n")) + + balanceMap, err := getBalancesFromSheet(reader) + + assert.Nil(t, balanceMap) + assert.ErrorContains(t, err, "invalid amount") + }) +} + +func TestBalances_GetBalancesFromTransactions(t *testing.T) { + t.Parallel() + + t.Run("valid transactions", func(t *testing.T) { + t.Parallel() + + var ( + dummyKeys = getDummyKeys(t, 10) + amount = std.NewCoins(std.NewCoin("ugnot", 10)) + amountCoins = std.NewCoins(std.NewCoin("ugnot", 10)) + gasFee = std.NewCoin("ugnot", 1000000) + txs = make([]std.Tx, 0) + ) + + sender := dummyKeys[0] + for _, dummyKey := range dummyKeys[1:] { + tx := std.Tx{ + Msgs: []std.Msg{ + bank.MsgSend{ + FromAddress: sender.Address(), + ToAddress: dummyKey.Address(), + Amount: amountCoins, + }, + }, + Fee: std.Fee{ + GasWanted: 10, + GasFee: gasFee, + }, + Signatures: make([]std.Signature, 0), + } + + txs = append(txs, tx) + } + + // Marshal the transactions into amino JSON + marshalledTxs := make([]string, 0, len(txs)) + + for _, tx := range txs { + marshalledTx, err := amino.MarshalJSON(tx) + require.NoError(t, err) + + marshalledTxs = append(marshalledTxs, string(marshalledTx)) + } + + mockErr := bytes.NewBufferString("") + io := commands.NewTestIO() + io.SetErr(commands.WriteNopCloser(mockErr)) + + reader := strings.NewReader(strings.Join(marshalledTxs, "\n")) + balanceMap, err := getBalancesFromTransactions(context.Background(), io, reader) + require.NoError(t, err) + + // Validate the balance map + assert.Len(t, balanceMap, len(dummyKeys)) + for _, key := range dummyKeys[1:] { + assert.Equal(t, amount, balanceMap[key.Address()].Amount) + } + + assert.Equal(t, std.Coins{}, balanceMap[sender.Address()].Amount) + }) + + t.Run("malformed transaction, invalid fee amount", func(t *testing.T) { + t.Parallel() + + var ( + dummyKeys = getDummyKeys(t, 10) + amountCoins = std.NewCoins(std.NewCoin("ugnot", 10)) + gasFee = std.NewCoin("gnos", 1) // invalid fee + txs = make([]std.Tx, 0) + ) + + sender := dummyKeys[0] + for _, dummyKey := range dummyKeys[1:] { + tx := std.Tx{ + Msgs: []std.Msg{ + bank.MsgSend{ + FromAddress: sender.Address(), + ToAddress: dummyKey.Address(), + Amount: amountCoins, + }, + }, + Fee: std.Fee{ + GasWanted: 10, + GasFee: gasFee, + }, + Signatures: make([]std.Signature, 0), + } + + txs = append(txs, tx) + } + + // Marshal the transactions into amino JSON + marshalledTxs := make([]string, 0, len(txs)) + + for _, tx := range txs { + marshalledTx, err := amino.MarshalJSON(tx) + require.NoError(t, err) + + marshalledTxs = append(marshalledTxs, string(marshalledTx)) + } + + mockErr := bytes.NewBufferString("") + io := commands.NewTestIO() + io.SetErr(commands.WriteNopCloser(mockErr)) + + reader := strings.NewReader(strings.Join(marshalledTxs, "\n")) + balanceMap, err := getBalancesFromTransactions(context.Background(), io, reader) + require.NoError(t, err) + + assert.NotNil(t, balanceMap) + assert.Contains(t, mockErr.String(), "invalid gas fee amount") + }) + + t.Run("malformed transaction, invalid send amount", func(t *testing.T) { + t.Parallel() + + var ( + dummyKeys = getDummyKeys(t, 10) + amountCoins = std.NewCoins(std.NewCoin("gnogno", 10)) // invalid send amount + gasFee = std.NewCoin("ugnot", 1) + txs = make([]std.Tx, 0) + ) + + sender := dummyKeys[0] + for _, dummyKey := range dummyKeys[1:] { + tx := std.Tx{ + Msgs: []std.Msg{ + bank.MsgSend{ + FromAddress: sender.Address(), + ToAddress: dummyKey.Address(), + Amount: amountCoins, + }, + }, + Fee: std.Fee{ + GasWanted: 10, + GasFee: gasFee, + }, + Signatures: make([]std.Signature, 0), + } + + txs = append(txs, tx) + } + + // Marshal the transactions into amino JSON + marshalledTxs := make([]string, 0, len(txs)) + + for _, tx := range txs { + marshalledTx, err := amino.MarshalJSON(tx) + require.NoError(t, err) + + marshalledTxs = append(marshalledTxs, string(marshalledTx)) + } + + mockErr := bytes.NewBufferString("") + io := commands.NewTestIO() + io.SetErr(commands.WriteNopCloser(mockErr)) + + reader := strings.NewReader(strings.Join(marshalledTxs, "\n")) + balanceMap, err := getBalancesFromTransactions(context.Background(), io, reader) + require.NoError(t, err) + + assert.NotNil(t, balanceMap) + assert.Contains(t, mockErr.String(), "invalid send amount") + }) +} diff --git a/gno.land/cmd/genesis/balances_export.go b/gno.land/cmd/genesis/balances_export.go new file mode 100644 index 00000000000..c07f250afeb --- /dev/null +++ b/gno.land/cmd/genesis/balances_export.go @@ -0,0 +1,78 @@ +package main + +import ( + "context" + "fmt" + "os" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" +) + +// newBalancesExportCmd creates the genesis balances export subcommand +func newBalancesExportCmd(balancesCfg *balancesCfg, io commands.IO) *commands.Command { + return commands.NewCommand( + commands.Metadata{ + Name: "export", + ShortUsage: "balances export [flags] ", + ShortHelp: "Exports the balances from the genesis.json", + LongHelp: "Exports the balances from the genesis.json to an output file", + }, + commands.NewEmptyConfig(), + func(_ context.Context, args []string) error { + return execBalancesExport(balancesCfg, io, args) + }, + ) +} + +func execBalancesExport(cfg *balancesCfg, io commands.IO, args []string) error { + // Load the genesis + genesis, loadErr := types.GenesisDocFromFile(cfg.genesisPath) + if loadErr != nil { + return fmt.Errorf("unable to load genesis, %w", loadErr) + } + + // Load the genesis state + if genesis.AppState == nil { + return errAppStateNotSet + } + + state := genesis.AppState.(gnoland.GnoGenesisState) + if len(state.Balances) == 0 { + io.Println("No genesis balances to export") + + return nil + } + + // Make sure the output file path is specified + if len(args) == 0 { + return errNoOutputFile + } + + // Open output file + outputFile, err := os.OpenFile( + args[0], + os.O_RDWR|os.O_CREATE|os.O_APPEND, + 0o755, + ) + if err != nil { + return fmt.Errorf("unable to create output file, %w", err) + } + + // Save the balances + for _, balance := range state.Balances { + if _, err = outputFile.WriteString( + fmt.Sprintf("%s\n", balance), + ); err != nil { + return fmt.Errorf("unable to write to output, %w", err) + } + } + + io.Printfln( + "Exported %d balances", + len(state.Balances), + ) + + return nil +} diff --git a/gno.land/cmd/genesis/balances_export_test.go b/gno.land/cmd/genesis/balances_export_test.go new file mode 100644 index 00000000000..d7441fd438f --- /dev/null +++ b/gno.land/cmd/genesis/balances_export_test.go @@ -0,0 +1,158 @@ +package main + +import ( + "bufio" + "context" + "testing" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/gnolang/gno/tm2/pkg/testutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// getDummyBalances generates dummy balance lines +func getDummyBalances(t *testing.T, count int) []gnoland.Balance { + t.Helper() + + dummyKeys := getDummyKeys(t, count) + amount := std.NewCoins(std.NewCoin("ugnot", 10)) + + balances := make([]gnoland.Balance, len(dummyKeys)) + + for index, key := range dummyKeys { + balances[index] = gnoland.Balance{ + Address: key.Address(), + Amount: amount, + } + } + + return balances +} + +func TestGenesis_Balances_Export(t *testing.T) { + t.Parallel() + + t.Run("invalid genesis file", func(t *testing.T) { + t.Parallel() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "balances", + "export", + "--genesis-path", + "dummy-path", + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errUnableToLoadGenesis.Error()) + }) + + t.Run("invalid genesis app state", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + genesis.AppState = nil // no app state + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "balances", + "export", + "--genesis-path", + tempGenesis.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errAppStateNotSet.Error()) + }) + + t.Run("no output file specified", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + genesis.AppState = gnoland.GnoGenesisState{ + Balances: getDummyBalances(t, 1), + } + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "balances", + "export", + "--genesis-path", + tempGenesis.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errNoOutputFile.Error()) + }) + + t.Run("valid balances export", func(t *testing.T) { + t.Parallel() + + // Generate dummy balances + balances := getDummyBalances(t, 10) + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + genesis.AppState = gnoland.GnoGenesisState{ + Balances: balances, + } + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Prepare the output file + outputFile, outputCleanup := testutils.NewTestFile(t) + t.Cleanup(outputCleanup) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "balances", + "export", + "--genesis-path", + tempGenesis.Name(), + outputFile.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Validate the transactions were written down + scanner := bufio.NewScanner(outputFile) + + outputBalances := make([]gnoland.Balance, 0) + for scanner.Scan() { + var balance gnoland.Balance + err := balance.Parse(scanner.Text()) + require.NoError(t, err) + + outputBalances = append(outputBalances, balance) + } + + require.NoError(t, scanner.Err()) + + assert.Len(t, outputBalances, len(balances)) + + for index, balance := range outputBalances { + assert.Equal(t, balances[index], balance) + } + }) +} diff --git a/gno.land/cmd/genesis/balances_remove.go b/gno.land/cmd/genesis/balances_remove.go new file mode 100644 index 00000000000..a752bbda4fd --- /dev/null +++ b/gno.land/cmd/genesis/balances_remove.go @@ -0,0 +1,103 @@ +package main + +import ( + "context" + "errors" + "flag" + "fmt" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto" +) + +var ( + errUnableToLoadGenesis = errors.New("unable to load genesis") + errBalanceNotFound = errors.New("genesis balances entry does not exist") +) + +type balancesRemoveCfg struct { + rootCfg *balancesCfg + + address string +} + +// newBalancesRemoveCmd creates the genesis balances remove subcommand +func newBalancesRemoveCmd(rootCfg *balancesCfg, io commands.IO) *commands.Command { + cfg := &balancesRemoveCfg{ + rootCfg: rootCfg, + } + + return commands.NewCommand( + commands.Metadata{ + Name: "remove", + ShortUsage: "balances remove [flags]", + LongHelp: "Removes the balance information of a specific account", + }, + cfg, + func(_ context.Context, _ []string) error { + return execBalancesRemove(cfg, io) + }, + ) +} + +func (c *balancesRemoveCfg) RegisterFlags(fs *flag.FlagSet) { + fs.StringVar( + &c.address, + "address", + "", + "the address of the account whose balance information should be removed from genesis.json", + ) +} + +func execBalancesRemove(cfg *balancesRemoveCfg, io commands.IO) error { + // Load the genesis + genesis, loadErr := types.GenesisDocFromFile(cfg.rootCfg.genesisPath) + if loadErr != nil { + return fmt.Errorf("%w, %w", errUnableToLoadGenesis, loadErr) + } + + // Validate the address + address, err := crypto.AddressFromString(cfg.address) + if err != nil { + return fmt.Errorf("%w, %w", errInvalidAddress, err) + } + + // Check if the genesis state is set at all + if genesis.AppState == nil { + return errAppStateNotSet + } + + // Construct the initial genesis balance sheet + state := genesis.AppState.(gnoland.GnoGenesisState) + genesisBalances, err := mapGenesisBalancesFromState(state) + if err != nil { + return err + } + + // Check if the genesis balance for the account is present + _, exists := genesisBalances[address] + if !exists { + return errBalanceNotFound + } + + // Drop the account pre-mine + delete(genesisBalances, address) + + // Save the balances + state.Balances = genesisBalances.toList() + genesis.AppState = state + + // Save the updated genesis + if err := genesis.SaveAs(cfg.rootCfg.genesisPath); err != nil { + return fmt.Errorf("unable to save genesis.json, %w", err) + } + + io.Printfln( + "Pre-mine information for address %s removed", + address.String(), + ) + + return nil +} diff --git a/gno.land/cmd/genesis/balances_remove_test.go b/gno.land/cmd/genesis/balances_remove_test.go new file mode 100644 index 00000000000..b9d10d0db08 --- /dev/null +++ b/gno.land/cmd/genesis/balances_remove_test.go @@ -0,0 +1,140 @@ +package main + +import ( + "context" + "testing" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/gnolang/gno/tm2/pkg/testutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGenesis_Balances_Remove(t *testing.T) { + t.Parallel() + + t.Run("invalid genesis", func(t *testing.T) { + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "balances", + "remove", + "--genesis-path", + "dummy-path", + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.ErrorContains(t, cmdErr, errUnableToLoadGenesis.Error()) + }) + + t.Run("genesis app state not set", func(t *testing.T) { + t.Parallel() + + dummyKey := getDummyKey(t) + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + genesis.AppState = nil // not set + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "balances", + "remove", + "--genesis-path", + tempGenesis.Name(), + "--address", + dummyKey.Address().String(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.ErrorContains(t, cmdErr, errAppStateNotSet.Error()) + }) + + t.Run("address is present", func(t *testing.T) { + t.Parallel() + + dummyKey := getDummyKey(t) + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + state := gnoland.GnoGenesisState{ + // Set an initial balance value + Balances: []gnoland.Balance{ + { + Address: dummyKey.Address(), + Amount: std.NewCoins(std.NewCoin("ugnot", 100)), + }, + }, + } + genesis.AppState = state + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "balances", + "remove", + "--genesis-path", + tempGenesis.Name(), + "--address", + dummyKey.Address().String(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Validate the genesis was updated + genesis, loadErr := types.GenesisDocFromFile(tempGenesis.Name()) + require.NoError(t, loadErr) + + require.NotNil(t, genesis.AppState) + + state, ok := genesis.AppState.(gnoland.GnoGenesisState) + require.True(t, ok) + + assert.Len(t, state.Balances, 0) + }) + + t.Run("address not present", func(t *testing.T) { + t.Parallel() + + dummyKey := getDummyKey(t) + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + state := gnoland.GnoGenesisState{ + Balances: []gnoland.Balance{}, // Empty initial balance + } + genesis.AppState = state + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "balances", + "remove", + "--genesis-path", + tempGenesis.Name(), + "--address", + dummyKey.Address().String(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.ErrorContains(t, cmdErr, errBalanceNotFound.Error()) + }) +} diff --git a/gno.land/cmd/genesis/generate.go b/gno.land/cmd/genesis/generate.go new file mode 100644 index 00000000000..684f8441874 --- /dev/null +++ b/gno.land/cmd/genesis/generate.go @@ -0,0 +1,153 @@ +package main + +import ( + "context" + "flag" + "fmt" + "time" + + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" +) + +var defaultChainID = "dev" + +type generateCfg struct { + outputPath string + chainID string + genesisTime int64 + blockMaxTxBytes int64 + blockMaxDataBytes int64 + blockMaxGas int64 + blockTimeIota int64 +} + +// newGenerateCmd creates the genesis generate subcommand +func newGenerateCmd(io commands.IO) *commands.Command { + cfg := &generateCfg{} + + return commands.NewCommand( + commands.Metadata{ + Name: "generate", + ShortUsage: "generate [flags]", + LongHelp: "Generates a node's genesis.json based on specified parameters", + ShortHelp: "Generates a fresh genesis.json", + }, + cfg, + func(_ context.Context, _ []string) error { + return execGenerate(cfg, io) + }, + ) +} + +func (c *generateCfg) RegisterFlags(fs *flag.FlagSet) { + fs.StringVar( + &c.outputPath, + "output-path", + "./genesis.json", + "the output path for the genesis.json", + ) + + fs.Int64Var( + &c.genesisTime, + "genesis-time", + time.Now().Unix(), + "the genesis creation time. Defaults to current time", + ) + + fs.StringVar( + &c.chainID, + "chain-id", + defaultChainID, + "the ID of the chain", + ) + + fs.Int64Var( + &c.blockMaxTxBytes, + "block-max-tx-bytes", + types.MaxBlockTxBytes, + "the max size of the block transaction", + ) + + fs.Int64Var( + &c.blockMaxDataBytes, + "block-max-data-bytes", + types.MaxBlockDataBytes, + "the max size of the block data", + ) + + fs.Int64Var( + &c.blockMaxGas, + "block-max-gas", + types.MaxBlockMaxGas, + "the max gas limit for the block", + ) + + fs.Int64Var( + &c.blockTimeIota, + "block-time-iota", + types.BlockTimeIotaMS, + "the block time iota (in ms)", + ) +} + +func execGenerate(cfg *generateCfg, io commands.IO) error { + // Start with the default configuration + genesis := getDefaultGenesis() + + // Set the genesis time + if cfg.genesisTime > 0 { + genesis.GenesisTime = time.Unix(cfg.genesisTime, 0) + } + + // Set the chain ID + if cfg.chainID != "" { + genesis.ChainID = cfg.chainID + } + + // Set the max tx bytes + if cfg.blockMaxTxBytes > 0 { + genesis.ConsensusParams.Block.MaxTxBytes = cfg.blockMaxTxBytes + } + + // Set the max data bytes + if cfg.blockMaxDataBytes > 0 { + genesis.ConsensusParams.Block.MaxDataBytes = cfg.blockMaxDataBytes + } + + // Set the max block gas + if cfg.blockMaxGas > 0 { + genesis.ConsensusParams.Block.MaxGas = cfg.blockMaxGas + } + + // Set the block time IOTA + if cfg.blockTimeIota > 0 { + genesis.ConsensusParams.Block.TimeIotaMS = cfg.blockTimeIota + } + + // Validate the genesis + if validateErr := genesis.ValidateAndComplete(); validateErr != nil { + return fmt.Errorf("unable to validate genesis, %w", validateErr) + } + + // Save the genesis file to disk + if saveErr := genesis.SaveAs(cfg.outputPath); saveErr != nil { + return fmt.Errorf("unable to save genesis, %w", saveErr) + } + + io.Printfln("Genesis successfully generated at %s\n", cfg.outputPath) + + // Log the empty validator set warning + io.Printfln("WARN: Genesis is generated with an empty validator set") + + return nil +} + +// getDefaultGenesis returns the default genesis config +func getDefaultGenesis() *types.GenesisDoc { + return &types.GenesisDoc{ + GenesisTime: time.Now(), + ChainID: defaultChainID, + ConsensusParams: types.DefaultConsensusParams(), + } +} diff --git a/gno.land/cmd/genesis/generate_test.go b/gno.land/cmd/genesis/generate_test.go new file mode 100644 index 00000000000..ca742e55150 --- /dev/null +++ b/gno.land/cmd/genesis/generate_test.go @@ -0,0 +1,245 @@ +package main + +import ( + "context" + "fmt" + "path/filepath" + "testing" + + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/testutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGenesis_Generate(t *testing.T) { + t.Parallel() + + t.Run("default genesis", func(t *testing.T) { + t.Parallel() + + tempDir, cleanup := testutils.NewTestCaseDir(t) + t.Cleanup(cleanup) + + genesisPath := filepath.Join(tempDir, "genesis.json") + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "generate", + "--output-path", + genesisPath, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Load the genesis + genesis, readErr := types.GenesisDocFromFile(genesisPath) + require.NoError(t, readErr) + + // Make sure the default configuration is set + defaultGenesis := getDefaultGenesis() + defaultGenesis.GenesisTime = genesis.GenesisTime + + assert.Equal(t, defaultGenesis, genesis) + }) + + t.Run("set chain ID", func(t *testing.T) { + t.Parallel() + + chainID := "example-chain-ID" + + tempDir, cleanup := testutils.NewTestCaseDir(t) + t.Cleanup(cleanup) + + genesisPath := filepath.Join(tempDir, "genesis.json") + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "generate", + "--chain-id", + chainID, + "--output-path", + genesisPath, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Load the genesis + genesis, readErr := types.GenesisDocFromFile(genesisPath) + require.NoError(t, readErr) + + assert.Equal(t, genesis.ChainID, chainID) + }) + + t.Run("set block max tx bytes", func(t *testing.T) { + t.Parallel() + + blockMaxTxBytes := int64(100) + + tempDir, cleanup := testutils.NewTestCaseDir(t) + t.Cleanup(cleanup) + + genesisPath := filepath.Join(tempDir, "genesis.json") + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "generate", + "--block-max-tx-bytes", + fmt.Sprintf("%d", blockMaxTxBytes), + "--output-path", + genesisPath, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Load the genesis + genesis, readErr := types.GenesisDocFromFile(genesisPath) + require.NoError(t, readErr) + + assert.Equal( + t, + genesis.ConsensusParams.Block.MaxTxBytes, + blockMaxTxBytes, + ) + }) + + t.Run("set block max data bytes", func(t *testing.T) { + t.Parallel() + + blockMaxDataBytes := int64(100) + + tempDir, cleanup := testutils.NewTestCaseDir(t) + t.Cleanup(cleanup) + + genesisPath := filepath.Join(tempDir, "genesis.json") + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "generate", + "--block-max-data-bytes", + fmt.Sprintf("%d", blockMaxDataBytes), + "--output-path", + genesisPath, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Load the genesis + genesis, readErr := types.GenesisDocFromFile(genesisPath) + require.NoError(t, readErr) + + assert.Equal( + t, + genesis.ConsensusParams.Block.MaxDataBytes, + blockMaxDataBytes, + ) + }) + + t.Run("set block max gas", func(t *testing.T) { + t.Parallel() + + blockMaxGas := int64(100) + + tempDir, cleanup := testutils.NewTestCaseDir(t) + t.Cleanup(cleanup) + + genesisPath := filepath.Join(tempDir, "genesis.json") + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "generate", + "--block-max-gas", + fmt.Sprintf("%d", blockMaxGas), + "--output-path", + genesisPath, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Load the genesis + genesis, readErr := types.GenesisDocFromFile(genesisPath) + require.NoError(t, readErr) + + assert.Equal( + t, + genesis.ConsensusParams.Block.MaxGas, + blockMaxGas, + ) + }) + + t.Run("set block time iota", func(t *testing.T) { + t.Parallel() + + blockTimeIota := int64(10) + + tempDir, cleanup := testutils.NewTestCaseDir(t) + t.Cleanup(cleanup) + + genesisPath := filepath.Join(tempDir, "genesis.json") + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "generate", + "--block-time-iota", + fmt.Sprintf("%d", blockTimeIota), + "--output-path", + genesisPath, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Load the genesis + genesis, readErr := types.GenesisDocFromFile(genesisPath) + require.NoError(t, readErr) + + assert.Equal( + t, + genesis.ConsensusParams.Block.TimeIotaMS, + blockTimeIota, + ) + }) + + t.Run("invalid genesis config (chain ID)", func(t *testing.T) { + t.Parallel() + + invalidChainID := "thischainidisunusuallylongsoitwillcausethetesttofail" + + tempDir, cleanup := testutils.NewTestCaseDir(t) + t.Cleanup(cleanup) + + genesisPath := filepath.Join(tempDir, "genesis.json") + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "generate", + "--chain-id", + invalidChainID, + "--output-path", + genesisPath, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.Error(t, cmdErr) + }) +} diff --git a/gno.land/cmd/genesis/main.go b/gno.land/cmd/genesis/main.go new file mode 100644 index 00000000000..53bb48c3c64 --- /dev/null +++ b/gno.land/cmd/genesis/main.go @@ -0,0 +1,53 @@ +package main + +import ( + "context" + "flag" + "os" + + "github.com/gnolang/gno/tm2/pkg/commands" +) + +func main() { + io := commands.NewDefaultIO() + cmd := newRootCmd(io) + + cmd.Execute(context.Background(), os.Args[1:]) +} + +func newRootCmd(io commands.IO) *commands.Command { + cmd := commands.NewCommand( + commands.Metadata{ + ShortUsage: " [flags] [...]", + LongHelp: "Gno Genesis manipulation suite", + }, + commands.NewEmptyConfig(), + commands.HelpExec, + ) + + cmd.AddSubCommands( + newGenerateCmd(io), + newValidatorCmd(io), + newVerifyCmd(io), + newBalancesCmd(io), + newTxsCmd(io), + ) + + return cmd +} + +// commonCfg is the common +// configuration for genesis commands +// that require a genesis.json +type commonCfg struct { + genesisPath string +} + +func (c *commonCfg) RegisterFlags(fs *flag.FlagSet) { + fs.StringVar( + &c.genesisPath, + "genesis-path", + "./genesis.json", + "the path to the genesis.json", + ) +} diff --git a/gno.land/cmd/genesis/txs.go b/gno.land/cmd/genesis/txs.go new file mode 100644 index 00000000000..a292a9c01de --- /dev/null +++ b/gno.land/cmd/genesis/txs.go @@ -0,0 +1,39 @@ +package main + +import ( + "flag" + + "github.com/gnolang/gno/tm2/pkg/commands" +) + +type txsCfg struct { + commonCfg +} + +// newTxsCmd creates the genesis txs subcommand +func newTxsCmd(io commands.IO) *commands.Command { + cfg := &txsCfg{} + + cmd := commands.NewCommand( + commands.Metadata{ + Name: "txs", + ShortUsage: "txs [flags]", + ShortHelp: "Manages the initial genesis transactions", + LongHelp: "Manages genesis transactions through input files", + }, + cfg, + commands.HelpExec, + ) + + cmd.AddSubCommands( + newTxsAddCmd(cfg, io), + newTxsRemoveCmd(cfg, io), + newTxsExportCmd(cfg, io), + ) + + return cmd +} + +func (c *txsCfg) RegisterFlags(fs *flag.FlagSet) { + c.commonCfg.RegisterFlags(fs) +} diff --git a/gno.land/cmd/genesis/txs_add.go b/gno.land/cmd/genesis/txs_add.go new file mode 100644 index 00000000000..e356badc4aa --- /dev/null +++ b/gno.land/cmd/genesis/txs_add.go @@ -0,0 +1,141 @@ +package main + +import ( + "bufio" + "context" + "errors" + "fmt" + "io" + "os" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/std" +) + +var ( + errInvalidTxsFile = errors.New("unable to open transactions file") + errNoTxsFileSpecified = errors.New("no txs file specified") + errTxsParsingAborted = errors.New("transaction parsing aborted") +) + +// newTxsAddCmd creates the genesis txs add subcommand +func newTxsAddCmd(txsCfg *txsCfg, io commands.IO) *commands.Command { + return commands.NewCommand( + commands.Metadata{ + Name: "add", + ShortUsage: "txs add ", + ShortHelp: "Imports transactions into the genesis.json", + LongHelp: "Imports the transactions from a tx-archive backup to the genesis.json", + }, + commands.NewEmptyConfig(), + func(ctx context.Context, args []string) error { + return execTxsAdd(ctx, txsCfg, io, args) + }, + ) +} + +func execTxsAdd( + ctx context.Context, + cfg *txsCfg, + io commands.IO, + args []string, +) error { + // Load the genesis + genesis, loadErr := types.GenesisDocFromFile(cfg.genesisPath) + if loadErr != nil { + return fmt.Errorf("unable to load genesis, %w", loadErr) + } + + // Open the transactions files + if len(args) == 0 { + return errNoTxsFileSpecified + } + + parsedTxs := make([]std.Tx, 0) + for _, file := range args { + file, loadErr := os.Open(file) + if loadErr != nil { + return fmt.Errorf("%w, %w", errInvalidTxsFile, loadErr) + } + + txs, err := getTransactionsFromFile(ctx, file) + if err != nil { + return fmt.Errorf("unable to read file, %w", err) + } + + parsedTxs = append(parsedTxs, txs...) + } + + // Initialize the app state if it's not present + if genesis.AppState == nil { + genesis.AppState = gnoland.GnoGenesisState{} + } + + state := genesis.AppState.(gnoland.GnoGenesisState) + + // Left merge the transactions + fileTxStore := txStore(parsedTxs) + genesisTxStore := txStore(state.Txs) + + // The genesis transactions have preference with the order + // in the genesis.json + if err := genesisTxStore.leftMerge(fileTxStore); err != nil { + return err + } + + // Save the state + state.Txs = genesisTxStore + genesis.AppState = state + + // Save the updated genesis + if err := genesis.SaveAs(cfg.genesisPath); err != nil { + return fmt.Errorf("unable to save genesis.json, %w", err) + } + + io.Printfln( + "Saved %d transactions to genesis.json", + len(parsedTxs), + ) + + return nil +} + +// getTransactionsFromFile fetches the transactions from the +// specified reader +func getTransactionsFromFile(ctx context.Context, reader io.Reader) ([]std.Tx, error) { + txs := make([]std.Tx, 0) + + scanner := bufio.NewScanner(reader) + + for scanner.Scan() { + select { + case <-ctx.Done(): + return nil, errTxsParsingAborted + default: + // Parse the amino JSON + var tx std.Tx + + if err := amino.UnmarshalJSON(scanner.Bytes(), &tx); err != nil { + return nil, fmt.Errorf( + "unable to unmarshal amino JSON, %w", + err, + ) + } + + txs = append(txs, tx) + } + } + + // Check for scanning errors + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf( + "error encountered while reading file, %w", + err, + ) + } + + return txs, nil +} diff --git a/gno.land/cmd/genesis/txs_add_test.go b/gno.land/cmd/genesis/txs_add_test.go new file mode 100644 index 00000000000..7d194182fb0 --- /dev/null +++ b/gno.land/cmd/genesis/txs_add_test.go @@ -0,0 +1,266 @@ +package main + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/sdk/bank" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/gnolang/gno/tm2/pkg/testutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// generateDummyTxs generates dummy transactions +func generateDummyTxs(t *testing.T, count int) []std.Tx { + t.Helper() + + txs := make([]std.Tx, count) + + for i := 0; i < count; i++ { + txs[i] = std.Tx{ + Msgs: []std.Msg{ + bank.MsgSend{ + FromAddress: crypto.Address{byte(i)}, + ToAddress: crypto.Address{byte((i + 1) % count)}, + Amount: std.NewCoins(std.NewCoin("ugnot", 1)), + }, + }, + Fee: std.Fee{ + GasWanted: 1, + GasFee: std.NewCoin("ugnot", 1000000), + }, + Memo: fmt.Sprintf("tx %d", i), + } + } + + return txs +} + +// encodeDummyTxs encodes the transactions into amino JSON +func encodeDummyTxs(t *testing.T, txs []std.Tx) []string { + t.Helper() + + encodedTxs := make([]string, 0, len(txs)) + + for _, tx := range txs { + encodedTx, err := amino.MarshalJSON(tx) + if err != nil { + t.Fatalf("unable to marshal tx, %v", err) + } + + encodedTxs = append(encodedTxs, string(encodedTx)) + } + + return encodedTxs +} + +func TestGenesis_Txs_Add(t *testing.T) { + t.Parallel() + + t.Run("invalid genesis file", func(t *testing.T) { + t.Parallel() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "txs", + "add", + "--genesis-path", + "dummy-path", + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errUnableToLoadGenesis.Error()) + }) + + t.Run("invalid txs file", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "txs", + "add", + "--genesis-path", + tempGenesis.Name(), + "dummy-tx-file", + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errInvalidTxsFile.Error()) + }) + + t.Run("no txs file", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "txs", + "add", + "--genesis-path", + tempGenesis.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errNoTxsFileSpecified.Error()) + }) + + t.Run("malformed txs file", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "txs", + "add", + "--genesis-path", + tempGenesis.Name(), + tempGenesis.Name(), // invalid txs file + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, "unable to read file") + }) + + t.Run("valid txs file", func(t *testing.T) { + t.Parallel() + + // Generate dummy txs + txs := generateDummyTxs(t, 10) + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Prepare the transactions file + txsFile, txsCleanup := testutils.NewTestFile(t) + t.Cleanup(txsCleanup) + + _, err := txsFile.WriteString( + strings.Join( + encodeDummyTxs(t, txs), + "\n", + ), + ) + require.NoError(t, err) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "txs", + "add", + "--genesis-path", + tempGenesis.Name(), + txsFile.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Validate the transactions were written down + updatedGenesis, err := types.GenesisDocFromFile(tempGenesis.Name()) + require.NoError(t, err) + require.NotNil(t, updatedGenesis.AppState) + + // Fetch the state + state := updatedGenesis.AppState.(gnoland.GnoGenesisState) + + assert.Len(t, state.Txs, len(txs)) + + for index, tx := range state.Txs { + assert.Equal(t, txs[index], tx) + } + }) + + t.Run("existing genesis txs", func(t *testing.T) { + t.Parallel() + + // Generate dummy txs + txs := generateDummyTxs(t, 10) + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + genesisState := gnoland.GnoGenesisState{ + Txs: txs[0 : len(txs)/2], + } + + genesis.AppState = genesisState + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Prepare the transactions file + txsFile, txsCleanup := testutils.NewTestFile(t) + t.Cleanup(txsCleanup) + + _, err := txsFile.WriteString( + strings.Join( + encodeDummyTxs(t, txs), + "\n", + ), + ) + require.NoError(t, err) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "txs", + "add", + "--genesis-path", + tempGenesis.Name(), + txsFile.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Validate the transactions were written down + updatedGenesis, err := types.GenesisDocFromFile(tempGenesis.Name()) + require.NoError(t, err) + require.NotNil(t, updatedGenesis.AppState) + + // Fetch the state + state := updatedGenesis.AppState.(gnoland.GnoGenesisState) + + assert.Len(t, state.Txs, len(txs)) + + for index, tx := range state.Txs { + assert.Equal(t, txs[index], tx) + } + }) +} diff --git a/gno.land/cmd/genesis/txs_export.go b/gno.land/cmd/genesis/txs_export.go new file mode 100644 index 00000000000..b06660d87d4 --- /dev/null +++ b/gno.land/cmd/genesis/txs_export.go @@ -0,0 +1,92 @@ +package main + +import ( + "context" + "errors" + "fmt" + "os" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" +) + +var errNoOutputFile = errors.New("no output file path specified") + +// newTxsExportCmd creates the genesis txs export subcommand +func newTxsExportCmd(txsCfg *txsCfg, io commands.IO) *commands.Command { + return commands.NewCommand( + commands.Metadata{ + Name: "export", + ShortUsage: "txs export [flags] ", + ShortHelp: "Exports the transactions from the genesis.json", + LongHelp: "Exports the transactions from the genesis.json to an output file", + }, + commands.NewEmptyConfig(), + func(_ context.Context, args []string) error { + return execTxsExport(txsCfg, io, args) + }, + ) +} + +func execTxsExport(cfg *txsCfg, io commands.IO, args []string) error { + // Load the genesis + genesis, loadErr := types.GenesisDocFromFile(cfg.genesisPath) + if loadErr != nil { + return fmt.Errorf("unable to load genesis, %w", loadErr) + } + + // Load the genesis state + if genesis.AppState == nil { + return errAppStateNotSet + } + + state := genesis.AppState.(gnoland.GnoGenesisState) + if len(state.Txs) == 0 { + io.Println("No genesis transactions to export") + + return nil + } + + // Make sure the output file path is specified + if len(args) == 0 { + return errNoOutputFile + } + + // Open output file + outputFile, err := os.OpenFile( + args[0], + os.O_RDWR|os.O_CREATE|os.O_APPEND, + 0o755, + ) + if err != nil { + return fmt.Errorf("unable to create output file, %w", err) + } + + // Save the transactions + for _, tx := range state.Txs { + // Marshal tx individual tx into JSON + jsonData, err := amino.MarshalJSON(tx) + if err != nil { + return fmt.Errorf("unable to marshal JSON data, %w", err) + } + + // Write the JSON data as a line to the file + if _, err = outputFile.Write(jsonData); err != nil { + return fmt.Errorf("unable to write to output, %w", err) + } + + // Write a newline character to separate JSON objects + if _, err = outputFile.WriteString("\n"); err != nil { + return fmt.Errorf("unable to write newline output, %w", err) + } + } + + io.Printfln( + "Exported %d transactions", + len(state.Txs), + ) + + return nil +} diff --git a/gno.land/cmd/genesis/txs_export_test.go b/gno.land/cmd/genesis/txs_export_test.go new file mode 100644 index 00000000000..bc84bc45f73 --- /dev/null +++ b/gno.land/cmd/genesis/txs_export_test.go @@ -0,0 +1,140 @@ +package main + +import ( + "bufio" + "context" + "testing" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/gnolang/gno/tm2/pkg/testutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGenesis_Txs_Export(t *testing.T) { + t.Parallel() + + t.Run("invalid genesis file", func(t *testing.T) { + t.Parallel() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "txs", + "export", + "--genesis-path", + "dummy-path", + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errUnableToLoadGenesis.Error()) + }) + + t.Run("invalid genesis app state", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + genesis.AppState = nil // no app state + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "txs", + "export", + "--genesis-path", + tempGenesis.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errAppStateNotSet.Error()) + }) + + t.Run("no output file specified", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + genesis.AppState = gnoland.GnoGenesisState{ + Txs: generateDummyTxs(t, 1), + } + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "txs", + "export", + "--genesis-path", + tempGenesis.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errNoOutputFile.Error()) + }) + + t.Run("valid txs export", func(t *testing.T) { + t.Parallel() + + // Generate dummy txs + txs := generateDummyTxs(t, 10) + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + genesis.AppState = gnoland.GnoGenesisState{ + Txs: txs, + } + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Prepare the output file + outputFile, outputCleanup := testutils.NewTestFile(t) + t.Cleanup(outputCleanup) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "txs", + "export", + "--genesis-path", + tempGenesis.Name(), + outputFile.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Validate the transactions were written down + scanner := bufio.NewScanner(outputFile) + + outputTxs := make([]std.Tx, 0) + for scanner.Scan() { + var tx std.Tx + + require.NoError(t, amino.UnmarshalJSON(scanner.Bytes(), &tx)) + + outputTxs = append(outputTxs, tx) + } + + require.NoError(t, scanner.Err()) + + assert.Len(t, outputTxs, len(txs)) + + for index, tx := range outputTxs { + assert.Equal(t, txs[index], tx) + } + }) +} diff --git a/gno.land/cmd/genesis/txs_remove.go b/gno.land/cmd/genesis/txs_remove.go new file mode 100644 index 00000000000..7893ad50cac --- /dev/null +++ b/gno.land/cmd/genesis/txs_remove.go @@ -0,0 +1,108 @@ +package main + +import ( + "context" + "errors" + "fmt" + "strings" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/std" +) + +var ( + errAppStateNotSet = errors.New("genesis app state not set") + errNoTxHashSpecified = errors.New("no transaction hashes specified") + errTxNotFound = errors.New("transaction not present in genesis.json") +) + +// newTxsRemoveCmd creates the genesis txs remove subcommand +func newTxsRemoveCmd(txsCfg *txsCfg, io commands.IO) *commands.Command { + return commands.NewCommand( + commands.Metadata{ + Name: "remove", + ShortUsage: "txs remove ", + ShortHelp: "Removes the transactions from the genesis.json", + LongHelp: "Removes the transactions using the transaction hash", + }, + commands.NewEmptyConfig(), + func(_ context.Context, args []string) error { + return execTxsRemove(txsCfg, io, args) + }, + ) +} + +func execTxsRemove(cfg *txsCfg, io commands.IO, args []string) error { + // Load the genesis + genesis, loadErr := types.GenesisDocFromFile(cfg.genesisPath) + if loadErr != nil { + return fmt.Errorf("unable to load genesis, %w", loadErr) + } + + // Check if the genesis state is set at all + if genesis.AppState == nil { + return errAppStateNotSet + } + + // Make sure the transaction hashes are set + if len(args) == 0 { + return errNoTxHashSpecified + } + + state := genesis.AppState.(gnoland.GnoGenesisState) + + for _, inputHash := range args { + index := -1 + + for indx, tx := range state.Txs { + // Find the hash of the transaction + hash, err := getTxHash(tx) + if err != nil { + return fmt.Errorf("unable to generate tx hash, %w", err) + } + + // Check if the hashes match + if strings.ToLower(hash) == strings.ToLower(inputHash) { + index = indx + + break + } + } + + if index < 0 { + return errTxNotFound + } + + state.Txs = append(state.Txs[:index], state.Txs[index+1:]...) + + io.Printfln( + "Transaction %s removed from genesis.json", + inputHash, + ) + } + + genesis.AppState = state + + // Save the updated genesis + if err := genesis.SaveAs(cfg.genesisPath); err != nil { + return fmt.Errorf("unable to save genesis.json, %w", err) + } + + return nil +} + +// getTxHash returns the hex hash representation of +// the transaction (Amino encoded) +func getTxHash(tx std.Tx) (string, error) { + encodedTx, err := amino.Marshal(tx) + if err != nil { + return "", fmt.Errorf("unable to marshal transaction, %w", err) + } + + txHash := types.Tx(encodedTx).Hash() + + return fmt.Sprintf("%X", txHash), nil +} diff --git a/gno.land/cmd/genesis/txs_remove_test.go b/gno.land/cmd/genesis/txs_remove_test.go new file mode 100644 index 00000000000..b89f2af761a --- /dev/null +++ b/gno.land/cmd/genesis/txs_remove_test.go @@ -0,0 +1,136 @@ +package main + +import ( + "context" + "testing" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/testutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGenesis_Txs_Remove(t *testing.T) { + t.Parallel() + + t.Run("invalid genesis file", func(t *testing.T) { + t.Parallel() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "txs", + "remove", + "--genesis-path", + "dummy-path", + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errUnableToLoadGenesis.Error()) + }) + + t.Run("invalid genesis app state", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + genesis.AppState = nil // no app state + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "txs", + "remove", + "--genesis-path", + tempGenesis.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errAppStateNotSet.Error()) + }) + t.Run("no transaction hash specified", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + // Generate dummy txs + txs := generateDummyTxs(t, 10) + + genesis := getDefaultGenesis() + genesis.AppState = gnoland.GnoGenesisState{ + Txs: txs, + } + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "txs", + "remove", + "--genesis-path", + tempGenesis.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errNoTxHashSpecified.Error()) + }) + + t.Run("transaction removed", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + // Generate dummy txs + txs := generateDummyTxs(t, 10) + + genesis := getDefaultGenesis() + genesis.AppState = gnoland.GnoGenesisState{ + Txs: txs, + } + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + txHash, err := getTxHash(txs[0]) + require.NoError(t, err) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "txs", + "remove", + "--genesis-path", + tempGenesis.Name(), + txHash, + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + + // Validate the transaction was removed + updatedGenesis, err := types.GenesisDocFromFile(tempGenesis.Name()) + require.NoError(t, err) + require.NotNil(t, updatedGenesis.AppState) + + // Fetch the state + state := updatedGenesis.AppState.(gnoland.GnoGenesisState) + + assert.Len(t, state.Txs, len(txs)-1) + + for _, tx := range state.Txs { + genesisTxHash, err := getTxHash(tx) + require.NoError(t, err) + + assert.NotEqual(t, txHash, genesisTxHash) + } + }) +} diff --git a/gno.land/cmd/genesis/types.go b/gno.land/cmd/genesis/types.go new file mode 100644 index 00000000000..dba39ea8ec1 --- /dev/null +++ b/gno.land/cmd/genesis/types.go @@ -0,0 +1,61 @@ +package main + +import ( + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/std" +) + +// txStore is a wrapper for TM2 transactions +type txStore []std.Tx + +// leftMerge merges the two tx stores, with +// preference to the left +func (i *txStore) leftMerge(b txStore) error { + // Build out the tx hash map + txHashMap := make(map[string]struct{}, len(*i)) + + for _, tx := range *i { + txHash, err := getTxHash(tx) + if err != nil { + return err + } + + txHashMap[txHash] = struct{}{} + } + + for _, tx := range b { + txHash, err := getTxHash(tx) + if err != nil { + return err + } + + if _, exists := txHashMap[txHash]; !exists { + *i = append(*i, tx) + } + } + + return nil +} + +type accountBalances map[types.Address]gnoland.Balance // address -> balance (ugnot) + +// toList linearizes the account balances map +func (a accountBalances) toList() []gnoland.Balance { + balances := make([]gnoland.Balance, 0, len(a)) + + for _, balance := range a { + balances = append(balances, balance) + } + + return balances +} + +// leftMerge left-merges the two maps +func (a accountBalances) leftMerge(b accountBalances) { + for key, bVal := range b { + if _, present := (a)[key]; !present { + (a)[key] = bVal + } + } +} diff --git a/gno.land/cmd/genesis/validator.go b/gno.land/cmd/genesis/validator.go new file mode 100644 index 00000000000..bf858a7732e --- /dev/null +++ b/gno.land/cmd/genesis/validator.go @@ -0,0 +1,49 @@ +package main + +import ( + "flag" + + "github.com/gnolang/gno/tm2/pkg/commands" +) + +type validatorCfg struct { + commonCfg + + address string +} + +// newValidatorCmd creates the genesis validator subcommand +func newValidatorCmd(io commands.IO) *commands.Command { + cfg := &validatorCfg{ + commonCfg: commonCfg{}, + } + + cmd := commands.NewCommand( + commands.Metadata{ + Name: "validator", + ShortUsage: "validator [flags]", + LongHelp: "Manipulates the genesis.json validator set", + ShortHelp: "Validator set management in genesis.json", + }, + cfg, + commands.HelpExec, + ) + + cmd.AddSubCommands( + newValidatorAddCmd(cfg, io), + newValidatorRemoveCmd(cfg, io), + ) + + return cmd +} + +func (c *validatorCfg) RegisterFlags(fs *flag.FlagSet) { + c.commonCfg.RegisterFlags(fs) + + fs.StringVar( + &c.address, + "address", + "", + "the output path for the genesis.json", + ) +} diff --git a/gno.land/cmd/genesis/validator_add.go b/gno.land/cmd/genesis/validator_add.go new file mode 100644 index 00000000000..502b572effb --- /dev/null +++ b/gno.land/cmd/genesis/validator_add.go @@ -0,0 +1,137 @@ +package main + +import ( + "context" + "errors" + "flag" + "fmt" + + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto" + _ "github.com/gnolang/gno/tm2/pkg/crypto/keys" +) + +var ( + errInvalidPower = errors.New("invalid validator power") + errInvalidName = errors.New("invalid validator name") + errPublicKeyMismatch = errors.New("provided public key and address do not match") + errAddressPresent = errors.New("validator with same address already present in genesis.json") +) + +type validatorAddCfg struct { + rootCfg *validatorCfg + + pubKey string + name string + power int64 +} + +// newValidatorAddCmd creates the genesis validator add subcommand +func newValidatorAddCmd(validatorCfg *validatorCfg, io commands.IO) *commands.Command { + cfg := &validatorAddCfg{ + rootCfg: validatorCfg, + } + + return commands.NewCommand( + commands.Metadata{ + Name: "add", + ShortUsage: "validator add [flags]", + LongHelp: "Adds a new validator to the genesis.json", + }, + cfg, + func(_ context.Context, _ []string) error { + return execValidatorAdd(cfg, io) + }, + ) +} + +func (c *validatorAddCfg) RegisterFlags(fs *flag.FlagSet) { + fs.StringVar( + &c.pubKey, + "pub-key", + "", + "the bech32 string representation of the validator's public key", + ) + + fs.StringVar( + &c.name, + "name", + "", + "the name of the validator (must be unique)", + ) + + fs.Int64Var( + &c.power, + "power", + 1, + "the voting power of the validator (must be > 0)", + ) +} + +func execValidatorAdd(cfg *validatorAddCfg, io commands.IO) error { + // Load the genesis + genesis, loadErr := types.GenesisDocFromFile(cfg.rootCfg.genesisPath) + if loadErr != nil { + return fmt.Errorf("unable to load genesis, %w", loadErr) + } + + // Check the validator address + address, err := crypto.AddressFromString(cfg.rootCfg.address) + if err != nil { + return fmt.Errorf("invalid validator address, %w", err) + } + + // Check the voting power + if cfg.power < 1 { + return errInvalidPower + } + + // Check the name + if cfg.name == "" { + return errors.New("invalid validator name") + } + + // Check the public key + pubKey, err := crypto.PubKeyFromBech32(cfg.pubKey) + if err != nil { + return fmt.Errorf("invalid validator public key, %w", err) + } + + // Check the public key matches the address + if pubKey.Address() != address { + return errors.New("provided public key and address do not match") + } + + validator := types.GenesisValidator{ + Address: address, + PubKey: pubKey, + Power: cfg.power, + Name: cfg.name, + } + + // Check if the validator exists + for _, genesisValidator := range genesis.Validators { + // There is no need to check if the public keys match + // since the address is derived from it, and the derivation + // is checked already + if validator.Address == genesisValidator.Address { + return errAddressPresent + } + } + + // Add the validator + genesis.Validators = append(genesis.Validators, validator) + + // Save the updated genesis + if err := genesis.SaveAs(cfg.rootCfg.genesisPath); err != nil { + return fmt.Errorf("unable to save genesis.json, %w", err) + } + + io.Printfln( + "Validator with address %s added to genesis file", + cfg.rootCfg.address, + ) + + return nil +} diff --git a/gno.land/cmd/genesis/validator_add_test.go b/gno.land/cmd/genesis/validator_add_test.go new file mode 100644 index 00000000000..37af4157e7c --- /dev/null +++ b/gno.land/cmd/genesis/validator_add_test.go @@ -0,0 +1,293 @@ +package main + +import ( + "context" + "testing" + + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/crypto/bip39" + "github.com/gnolang/gno/tm2/pkg/crypto/hd" + "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" + "github.com/gnolang/gno/tm2/pkg/crypto/secp256k1" + "github.com/gnolang/gno/tm2/pkg/testutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// getDummyKey generates a random public key, +// and returns the key info +func getDummyKey(t *testing.T) crypto.PubKey { + t.Helper() + + mnemonic, err := client.GenerateMnemonic(256) + require.NoError(t, err) + + seed := bip39.NewSeed(mnemonic, "") + + return generateKeyFromSeed(seed, 0).PubKey() +} + +// generateKeyFromSeed generates a private key from +// the provided seed and index +func generateKeyFromSeed(seed []byte, index uint32) crypto.PrivKey { + pathParams := hd.NewFundraiserParams(0, crypto.CoinType, index) + + masterPriv, ch := hd.ComputeMastersFromSeed(seed) + + //nolint:errcheck // This derivation can never error out, since the path params + // are always going to be valid + derivedPriv, _ := hd.DerivePrivateKeyForPath(masterPriv, ch, pathParams.String()) + + return secp256k1.PrivKeySecp256k1(derivedPriv) +} + +// getDummyKeys generates random keys for testing +func getDummyKeys(t *testing.T, count int) []crypto.PubKey { + t.Helper() + + dummyKeys := make([]crypto.PubKey, count) + + for i := 0; i < count; i++ { + dummyKeys[i] = getDummyKey(t) + } + + return dummyKeys +} + +func TestGenesis_Validator_Add(t *testing.T) { + t.Parallel() + + t.Run("invalid genesis file", func(t *testing.T) { + t.Parallel() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "validator", + "add", + "--genesis-path", + "dummy-path", + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errUnableToLoadGenesis.Error()) + }) + + t.Run("invalid validator address", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "validator", + "add", + "--genesis-path", + tempGenesis.Name(), + "--address", + "dummyaddress", + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, "invalid validator address") + }) + + t.Run("invalid voting power", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + key := getDummyKey(t) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "validator", + "add", + "--genesis-path", + tempGenesis.Name(), + "--address", + key.Address().String(), + "--power", + "-1", // invalid voting power + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorIs(t, cmdErr, errInvalidPower) + }) + + t.Run("invalid validator name", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + key := getDummyKey(t) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "validator", + "add", + "--genesis-path", + tempGenesis.Name(), + "--address", + key.Address().String(), + "--name", + "", // invalid validator name + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errInvalidName.Error()) + }) + + t.Run("invalid public key", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + key := getDummyKey(t) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "validator", + "add", + "--genesis-path", + tempGenesis.Name(), + "--address", + key.Address().String(), + "--name", + "example", + "--pub-key", + "invalidkey", // invalid pub key + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, "invalid validator public key") + }) + + t.Run("public key address mismatch", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + dummyKeys := getDummyKeys(t, 2) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "validator", + "add", + "--genesis-path", + tempGenesis.Name(), + "--address", + dummyKeys[0].Address().String(), + "--name", + "example", + "--pub-key", + crypto.PubKeyToBech32(dummyKeys[1]), // another key + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errPublicKeyMismatch.Error()) + }) + + t.Run("validator with same address exists", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + dummyKeys := getDummyKeys(t, 2) + genesis := getDefaultGenesis() + + // Set an existing validator + genesis.Validators = append(genesis.Validators, types.GenesisValidator{ + Address: dummyKeys[0].Address(), + PubKey: dummyKeys[0], + Power: 1, + Name: "example", + }) + + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "validator", + "add", + "--genesis-path", + tempGenesis.Name(), + "--address", + dummyKeys[0].Address().String(), + "--name", + "example", + "--pub-key", + crypto.PubKeyToBech32(dummyKeys[0]), // another key + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errAddressPresent.Error()) + }) + + t.Run("valid genesis validator", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + key := getDummyKey(t) + genesis := getDefaultGenesis() + + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "validator", + "add", + "--genesis-path", + tempGenesis.Name(), + "--address", + key.Address().String(), + "--name", + "example", + "--pub-key", + crypto.PubKeyToBech32(key), // another key + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + }) +} diff --git a/gno.land/cmd/genesis/validator_remove.go b/gno.land/cmd/genesis/validator_remove.go new file mode 100644 index 00000000000..960d891239b --- /dev/null +++ b/gno.land/cmd/genesis/validator_remove.go @@ -0,0 +1,71 @@ +package main + +import ( + "context" + "errors" + "fmt" + + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto" +) + +var errValidatorNotPresent = errors.New("validator not present in genesis.json") + +// newValidatorRemoveCmd creates the genesis validator remove subcommand +func newValidatorRemoveCmd(rootCfg *validatorCfg, io commands.IO) *commands.Command { + return commands.NewCommand( + commands.Metadata{ + Name: "remove", + ShortUsage: "validator remove [flags]", + LongHelp: "Removes a validator from the genesis.json", + }, + commands.NewEmptyConfig(), + func(_ context.Context, _ []string) error { + return execValidatorRemove(rootCfg, io) + }, + ) +} + +func execValidatorRemove(cfg *validatorCfg, io commands.IO) error { + // Load the genesis + genesis, loadErr := types.GenesisDocFromFile(cfg.genesisPath) + if loadErr != nil { + return fmt.Errorf("unable to load genesis, %w", loadErr) + } + + // Check the validator address + address, err := crypto.AddressFromString(cfg.address) + if err != nil { + return fmt.Errorf("invalid validator address, %w", err) + } + + index := -1 + + for indx, validator := range genesis.Validators { + if validator.Address == address { + index = indx + + break + } + } + + if index < 0 { + return errors.New("validator not present in genesis.json") + } + + // Drop the validator + genesis.Validators = append(genesis.Validators[:index], genesis.Validators[index+1:]...) + + // Save the updated genesis + if err := genesis.SaveAs(cfg.genesisPath); err != nil { + return fmt.Errorf("unable to save genesis.json, %w", err) + } + + io.Printfln( + "Validator with address %s removed from genesis file", + cfg.address, + ) + + return nil +} diff --git a/gno.land/cmd/genesis/validator_remove_test.go b/gno.land/cmd/genesis/validator_remove_test.go new file mode 100644 index 00000000000..953657afe33 --- /dev/null +++ b/gno.land/cmd/genesis/validator_remove_test.go @@ -0,0 +1,129 @@ +package main + +import ( + "context" + "testing" + + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/testutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGenesis_Validator_Remove(t *testing.T) { + t.Parallel() + + t.Run("invalid genesis file", func(t *testing.T) { + t.Parallel() + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "validator", + "remove", + "--genesis-path", + "dummy-path", + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errUnableToLoadGenesis.Error()) + }) + + t.Run("invalid validator address", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + genesis := getDefaultGenesis() + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "validator", + "remove", + "--genesis-path", + tempGenesis.Name(), + "--address", + "dummyaddress", + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, "invalid validator address") + }) + + t.Run("validator not found", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + dummyKeys := getDummyKeys(t, 2) + genesis := getDefaultGenesis() + + // Set an existing validator + genesis.Validators = append(genesis.Validators, types.GenesisValidator{ + Address: dummyKeys[0].Address(), + PubKey: dummyKeys[0], + Power: 1, + Name: "example", + }) + + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "validator", + "remove", + "--genesis-path", + tempGenesis.Name(), + "--address", + dummyKeys[1].Address().String(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.ErrorContains(t, cmdErr, errValidatorNotPresent.Error()) + }) + + t.Run("validator removed", func(t *testing.T) { + t.Parallel() + + tempGenesis, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + dummyKey := getDummyKey(t) + + genesis := getDefaultGenesis() + + // Set an existing validator + genesis.Validators = append(genesis.Validators, types.GenesisValidator{ + Address: dummyKey.Address(), + PubKey: dummyKey, + Power: 1, + Name: "example", + }) + + require.NoError(t, genesis.SaveAs(tempGenesis.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "validator", + "remove", + "--genesis-path", + tempGenesis.Name(), + "--address", + dummyKey.Address().String(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + assert.NoError(t, cmdErr) + }) +} diff --git a/gno.land/cmd/genesis/verify.go b/gno.land/cmd/genesis/verify.go new file mode 100644 index 00000000000..260a9eb734d --- /dev/null +++ b/gno.land/cmd/genesis/verify.go @@ -0,0 +1,79 @@ +package main + +import ( + "context" + "errors" + "flag" + "fmt" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" +) + +var errInvalidGenesisState = errors.New("invalid genesis state type") + +type verifyCfg struct { + commonCfg +} + +// newVerifyCmd creates the genesis verify subcommand +func newVerifyCmd(io commands.IO) *commands.Command { + cfg := &verifyCfg{} + + return commands.NewCommand( + commands.Metadata{ + Name: "verify", + ShortUsage: "verify [flags]", + LongHelp: "Verifies a node's genesis.json", + ShortHelp: "Verifies a genesis.json", + }, + cfg, + func(_ context.Context, _ []string) error { + return execVerify(cfg, io) + }, + ) +} + +func (c *verifyCfg) RegisterFlags(fs *flag.FlagSet) { + c.commonCfg.RegisterFlags(fs) +} + +func execVerify(cfg *verifyCfg, io commands.IO) error { + // Load the genesis + genesis, loadErr := types.GenesisDocFromFile(cfg.genesisPath) + if loadErr != nil { + return fmt.Errorf("unable to load genesis, %w", loadErr) + } + + // Verify it + if validateErr := genesis.Validate(); validateErr != nil { + return fmt.Errorf("unable to verify genesis, %w", validateErr) + } + + // Validate the genesis state + if genesis.AppState != nil { + state, ok := genesis.AppState.(gnoland.GnoGenesisState) + if !ok { + return errInvalidGenesisState + } + + // Validate the initial transactions + for _, tx := range state.Txs { + if validateErr := tx.ValidateBasic(); validateErr != nil { + return fmt.Errorf("invalid transacton, %w", validateErr) + } + } + + // Validate the initial balances + for _, balance := range state.Balances { + if err := balance.Verify(); err != nil { + return fmt.Errorf("invalid balance: %w", err) + } + } + } + + io.Printfln("Genesis at %s is valid", cfg.genesisPath) + + return nil +} diff --git a/gno.land/cmd/genesis/verify_test.go b/gno.land/cmd/genesis/verify_test.go new file mode 100644 index 00000000000..8388949898b --- /dev/null +++ b/gno.land/cmd/genesis/verify_test.go @@ -0,0 +1,169 @@ +package main + +import ( + "context" + "testing" + "time" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto/mock" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/gnolang/gno/tm2/pkg/testutils" + "github.com/stretchr/testify/require" +) + +func TestGenesis_Verify(t *testing.T) { + t.Parallel() + + getValidTestGenesis := func() *types.GenesisDoc { + key := mock.GenPrivKey().PubKey() + + return &types.GenesisDoc{ + GenesisTime: time.Now(), + ChainID: "valid-chain-id", + ConsensusParams: types.DefaultConsensusParams(), + Validators: []types.GenesisValidator{ + { + Address: key.Address(), + PubKey: key, + Power: 1, + Name: "valid validator", + }, + }, + } + } + + t.Run("invalid txs", func(t *testing.T) { + t.Parallel() + + tempFile, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + g := getValidTestGenesis() + + g.AppState = gnoland.GnoGenesisState{ + Balances: []gnoland.Balance{}, + Txs: []std.Tx{ + {}, + }, + } + + require.NoError(t, g.SaveAs(tempFile.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "verify", + "--genesis-path", + tempFile.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.Error(t, cmdErr) + }) + + t.Run("invalid balances", func(t *testing.T) { + t.Parallel() + + tempFile, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + g := getValidTestGenesis() + + g.AppState = gnoland.GnoGenesisState{ + Balances: []gnoland.Balance{ + {}, + }, + Txs: []std.Tx{}, + } + + require.NoError(t, g.SaveAs(tempFile.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "verify", + "--genesis-path", + tempFile.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.Error(t, cmdErr) + }) + + t.Run("valid genesis", func(t *testing.T) { + t.Parallel() + + tempFile, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + g := getValidTestGenesis() + g.AppState = gnoland.GnoGenesisState{ + Balances: []gnoland.Balance{}, + Txs: []std.Tx{}, + } + + require.NoError(t, g.SaveAs(tempFile.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "verify", + "--genesis-path", + tempFile.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + }) + + t.Run("valid genesis, no state", func(t *testing.T) { + t.Parallel() + + tempFile, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + g := getValidTestGenesis() + require.NoError(t, g.SaveAs(tempFile.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "verify", + "--genesis-path", + tempFile.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.NoError(t, cmdErr) + }) + + t.Run("invalid genesis state", func(t *testing.T) { + t.Parallel() + + tempFile, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + g := getValidTestGenesis() + g.AppState = "Totally invalid state" + require.NoError(t, g.SaveAs(tempFile.Name())) + + // Create the command + cmd := newRootCmd(commands.NewTestIO()) + args := []string{ + "verify", + "--genesis-path", + tempFile.Name(), + } + + // Run the command + cmdErr := cmd.ParseAndRun(context.Background(), args) + require.Error(t, cmdErr) + }) +} diff --git a/gno.land/cmd/gnofaucet/main.go b/gno.land/cmd/gnofaucet/main.go index dc6c16bac78..af64672d0f5 100644 --- a/gno.land/cmd/gnofaucet/main.go +++ b/gno.land/cmd/gnofaucet/main.go @@ -2,7 +2,6 @@ package main import ( "context" - "fmt" "os" "github.com/gnolang/gno/tm2/pkg/commands" @@ -22,9 +21,5 @@ func main() { newServeCmd(), ) - if err := cmd.ParseAndRun(context.Background(), os.Args[1:]); err != nil { - _, _ = fmt.Fprintf(os.Stderr, "%+v\n", err) - - os.Exit(1) - } + cmd.Execute(context.Background(), os.Args[1:]) } diff --git a/gno.land/cmd/gnofaucet/serve.go b/gno.land/cmd/gnofaucet/serve.go index 3fe0966592c..b810b8e96a0 100644 --- a/gno.land/cmd/gnofaucet/serve.go +++ b/gno.land/cmd/gnofaucet/serve.go @@ -152,7 +152,7 @@ func (c *config) RegisterFlags(fs *flag.FlagSet) { ) } -func execServe(cfg *config, args []string, io *commands.IO) error { +func execServe(cfg *config, args []string, io commands.IO) error { if len(args) != 1 { return flag.ErrHelp } @@ -182,31 +182,6 @@ func execServe(cfg *config, args []string, io *commands.IO) error { if err != nil { return err } - info, err := kb.GetByName(name) - if err != nil { - return err - } - fromAddr := info.GetAddress() - - // query for initial number and sequence. - path := fmt.Sprintf("auth/accounts/%s", fromAddr.String()) - data := []byte(nil) - opts2 := rpcclient.ABCIQueryOptions{} - qres, err := cli.ABCIQueryWithOptions( - path, data, opts2) - if err != nil { - return errors.Wrap(err, "querying") - } - if qres.Response.Error != nil { - fmt.Printf("Log: %s\n", - qres.Response.Log) - return qres.Response.Error - } - resdata := qres.Response.Data - var acc gnoland.GnoAccount - amino.MustUnmarshalJSON(resdata, &acc) - accountNumber := acc.BaseAccount.AccountNumber - sequence := acc.BaseAccount.Sequence // Get password for supply account. // Test by signing a dummy message; @@ -239,7 +214,7 @@ func execServe(cfg *config, args []string, io *commands.IO) error { if err != nil { return err } - err = sendAmountTo(cfg, cli, io, name, pass, testToAddr, accountNumber, sequence, send) + err = sendAmountTo(cfg, cli, io, name, pass, testToAddr, send) return err } @@ -318,13 +293,12 @@ func execServe(cfg *config, args []string, io *commands.IO) error { w.Write([]byte("invalid address format")) return } - err = sendAmountTo(cfg, cli, io, name, pass, toAddr, accountNumber, sequence, send) + err = sendAmountTo(cfg, cli, io, name, pass, toAddr, send) if err != nil { fmt.Println(ip, "faucet failed", err) w.Write([]byte("faucet failed")) return } else { - sequence += 1 fmt.Println(ip, "faucet success") w.Write([]byte("faucet success")) } @@ -337,7 +311,9 @@ func execServe(cfg *config, args []string, io *commands.IO) error { Addr: ":5050", ReadHeaderTimeout: 60 * time.Second, } - server.ListenAndServe() + if err := server.ListenAndServe(); err != nil { + return fmt.Errorf("http server stopped. %w", err) + } return nil } @@ -345,12 +321,10 @@ func execServe(cfg *config, args []string, io *commands.IO) error { func sendAmountTo( cfg *config, cli rpcclient.Client, - io *commands.IO, + io commands.IO, name, pass string, toAddr crypto.Address, - accountNumber, - sequence uint64, send std.Coins, ) error { // Read supply account pubkey. @@ -400,6 +374,26 @@ func sendAmountTo( } // fmt.Println("will sign:", string(amino.MustMarshalJSON(tx))) + // query for the account number and sequence each time in case the node is reset + path := fmt.Sprintf("auth/accounts/%s", fromAddr.String()) + data := []byte(nil) + opts2 := rpcclient.ABCIQueryOptions{} + qres, err := cli.ABCIQueryWithOptions( + path, data, opts2) + if err != nil { + return errors.Wrap(err, "querying") + } + if qres.Response.Error != nil { + fmt.Printf("Log: %s\n", + qres.Response.Log) + return qres.Response.Error + } + resdata := qres.Response.Data + var acc gnoland.GnoAccount + amino.MustUnmarshalJSON(resdata, &acc) + accountNumber := acc.BaseAccount.AccountNumber + sequence := acc.BaseAccount.Sequence + // get sign-bytes and make signature. chainID := cfg.ChainID signbz := tx.GetSignBytes(chainID, accountNumber, sequence) diff --git a/gno.land/cmd/gnokey/main.go b/gno.land/cmd/gnokey/main.go index 1f3414dc893..9f473971bf4 100644 --- a/gno.land/cmd/gnokey/main.go +++ b/gno.land/cmd/gnokey/main.go @@ -2,18 +2,18 @@ package main import ( "context" - "fmt" "os" + "github.com/gnolang/gno/gno.land/pkg/keyscli" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" ) func main() { - cmd := client.NewRootCmd() + baseCfg := client.DefaultBaseOptions + baseCfg.Home = gnoenv.HomeDir() - if err := cmd.ParseAndRun(context.Background(), os.Args[1:]); err != nil { - _, _ = fmt.Fprintf(os.Stderr, "%+v\n", err) - - os.Exit(1) - } + cmd := keyscli.NewRootCmd(commands.NewDefaultIO(), baseCfg) + cmd.Execute(context.Background(), os.Args[1:]) } diff --git a/gno.land/cmd/gnoland/integration_test.go b/gno.land/cmd/gnoland/integration_test.go new file mode 100644 index 00000000000..37451df9704 --- /dev/null +++ b/gno.land/cmd/gnoland/integration_test.go @@ -0,0 +1,11 @@ +package main + +import ( + "testing" + + "github.com/gnolang/gno/gno.land/pkg/integration" +) + +func TestTestdata(t *testing.T) { + integration.RunGnolandTestscripts(t, "testdata") +} diff --git a/gno.land/cmd/gnoland/root.go b/gno.land/cmd/gnoland/root.go index cf2a6252478..5b87b9452c7 100644 --- a/gno.land/cmd/gnoland/root.go +++ b/gno.land/cmd/gnoland/root.go @@ -2,7 +2,6 @@ package main import ( "context" - "fmt" "os" "github.com/gnolang/gno/tm2/pkg/commands" @@ -11,16 +10,12 @@ import ( ) func main() { - io := commands.NewDefaultIO() - cmd := newRootCmd(io) + cmd := newRootCmd(commands.NewDefaultIO()) - if err := cmd.ParseAndRun(context.Background(), os.Args[1:]); err != nil { - _, _ = fmt.Fprintf(os.Stderr, "%+v\n", err) - os.Exit(1) - } + cmd.Execute(context.Background(), os.Args[1:]) } -func newRootCmd(io *commands.IO) *commands.Command { +func newRootCmd(io commands.IO) *commands.Command { cmd := commands.NewCommand( commands.Metadata{ ShortUsage: " [flags] [...]", diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index b2134d86ea9..d82647b5b2e 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -2,6 +2,7 @@ package main import ( "context" + "errors" "flag" "fmt" "path/filepath" @@ -9,35 +10,44 @@ import ( "time" "github.com/gnolang/gno/gno.land/pkg/gnoland" - vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm" - gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/gnovm/pkg/gnomod" - "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/gno.land/pkg/log" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/node" "github.com/gnolang/gno/tm2/pkg/bft/privval" + "github.com/gnolang/gno/tm2/pkg/bft/state/eventstore/file" + "github.com/gnolang/gno/tm2/pkg/bft/state/eventstore/null" + eventstorecfg "github.com/gnolang/gno/tm2/pkg/bft/state/eventstore/types" bft "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/crypto" - "github.com/gnolang/gno/tm2/pkg/log" osm "github.com/gnolang/gno/tm2/pkg/os" "github.com/gnolang/gno/tm2/pkg/std" + "go.uber.org/zap/zapcore" ) type startCfg struct { + gnoRootDir string skipFailingGenesisTxs bool skipStart bool genesisBalancesFile string genesisTxsFile string chainID string genesisRemote string - rootDir string + dataDir string genesisMaxVMCycles int64 config string + + txEventStoreType string + txEventStorePath string + nodeConfigPath string + + logLevel string + logFormat string } -func newStartCmd(io *commands.IO) *commands.Command { +func newStartCmd(io commands.IO) *commands.Command { cfg := &startCfg{} return commands.NewCommand( @@ -47,13 +57,17 @@ func newStartCmd(io *commands.IO) *commands.Command { ShortHelp: "Run the full node", }, cfg, - func(_ context.Context, args []string) error { - return execStart(cfg, args, io) + func(_ context.Context, _ []string) error { + return execStart(cfg, io) }, ) } func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { + gnoroot := gnoenv.RootDir() + defaultGenesisBalancesFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_balances.txt") + defaultGenesisTxsFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_txs.txt") + fs.BoolVar( &c.skipFailingGenesisTxs, "skip-failing-genesis-txs", @@ -71,14 +85,14 @@ func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( &c.genesisBalancesFile, "genesis-balances-file", - "./genesis/genesis_balances.txt", + defaultGenesisBalancesFile, "initial distribution file", ) fs.StringVar( &c.genesisTxsFile, "genesis-txs-file", - "./genesis/genesis_txs.txt", + defaultGenesisTxsFile, "initial txs to replay", ) @@ -90,8 +104,16 @@ func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { ) fs.StringVar( - &c.rootDir, - "root-dir", + &c.gnoRootDir, + "gnoroot-dir", + gnoroot, + "the root directory of the gno repository", + ) + + // XXX: Use home directory for this + fs.StringVar( + &c.dataDir, + "data-dir", "testdir", "directory for config and data", ) @@ -114,43 +136,128 @@ func (c *startCfg) RegisterFlags(fs *flag.FlagSet) { &c.config, "config", "", - "config file (optional)", + "the flag config file (optional)", + ) + + fs.StringVar( + &c.nodeConfigPath, + "tm2-node-config", + "", + "the node TOML config file path (optional)", + ) + + fs.StringVar( + &c.txEventStoreType, + "tx-event-store-type", + null.EventStoreType, + fmt.Sprintf( + "type of transaction event store [%s]", + strings.Join( + []string{ + null.EventStoreType, + file.EventStoreType, + }, + ", ", + ), + ), + ) + + fs.StringVar( + &c.txEventStorePath, + "tx-event-store-path", + "", + fmt.Sprintf("path for the file tx event store (required if event store is '%s')", file.EventStoreType), + ) + + fs.StringVar( + &c.logLevel, + "log-level", + zapcore.DebugLevel.String(), + "log level for the gnoland node,", + ) + + fs.StringVar( + &c.logFormat, + "log-format", + log.ConsoleFormat.String(), + "log format for the gnoland node", + ) + + // XXX(deprecated): use data-dir instead + fs.StringVar( + &c.dataDir, + "root-dir", + "testdir", + "deprecated: use data-dir instead - directory for config and data", ) } -func execStart(c *startCfg, args []string, io *commands.IO) error { - logger := log.NewTMLogger(log.NewSyncWriter(io.Out)) - rootDir := c.rootDir +func execStart(c *startCfg, io commands.IO) error { + dataDir := c.dataDir - cfg := config.LoadOrMakeConfigWithOptions(rootDir, func(cfg *config.Config) { - cfg.Consensus.CreateEmptyBlocks = true - cfg.Consensus.CreateEmptyBlocksInterval = 0 * time.Second - }) + var ( + cfg *config.Config + loadCfgErr error + ) - // create priv validator first. - // need it to generate genesis.json - newPrivValKey := cfg.PrivValidatorKeyFile() - newPrivValState := cfg.PrivValidatorStateFile() - priv := privval.LoadOrGenFilePV(newPrivValKey, newPrivValState) + // Set the node configuration + if c.nodeConfigPath != "" { + // Load the node configuration + // from the specified path + cfg, loadCfgErr = config.LoadConfigFile(c.nodeConfigPath) + } else { + // Load the default node configuration + cfg, loadCfgErr = config.LoadOrMakeConfigWithOptions(dataDir) + } + + if loadCfgErr != nil { + return fmt.Errorf("unable to load node configuration, %w", loadCfgErr) + } + + // Initialize the log level + logLevel, err := zapcore.ParseLevel(c.logLevel) + if err != nil { + return fmt.Errorf("unable to parse log level, %w", err) + } + + // Initialize the log format + logFormat := log.Format(strings.ToLower(c.logFormat)) + + // Initialize the zap logger + zapLogger := log.GetZapLoggerFn(logFormat)(io.Out(), logLevel) + + // Wrap the zap logger + logger := log.ZapLoggerToSlog(zapLogger) + + // Write genesis file if missing. + genesisFilePath := filepath.Join(dataDir, cfg.Genesis) - // write genesis file if missing. - genesisFilePath := filepath.Join(rootDir, cfg.Genesis) if !osm.FileExists(genesisFilePath) { - genDoc := makeGenesisDoc( - priv.GetPubKey(), - c.chainID, - c.genesisBalancesFile, - loadGenesisTxs(c.genesisTxsFile, c.chainID, c.genesisRemote), - ) - writeGenesisFile(genDoc, genesisFilePath) + // Create priv validator first. + // Need it to generate genesis.json + newPrivValKey := cfg.PrivValidatorKeyFile() + newPrivValState := cfg.PrivValidatorStateFile() + priv := privval.LoadOrGenFilePV(newPrivValKey, newPrivValState) + pk := priv.GetPubKey() + + // Generate genesis.json file + if err := generateGenesisFile(genesisFilePath, pk, c); err != nil { + return fmt.Errorf("unable to generate genesis file: %w", err) + } } - // create application and node. - gnoApp, err := gnoland.NewApp(rootDir, c.skipFailingGenesisTxs, logger, c.genesisMaxVMCycles) + // Initialize the indexer config + txEventStoreCfg, err := getTxEventStoreConfig(c) if err != nil { - return fmt.Errorf("error in creating new app: %w", err) + return fmt.Errorf("unable to parse indexer config, %w", err) } + cfg.TxEventStore = txEventStoreCfg + // Create application and node. + gnoApp, err := gnoland.NewApp(dataDir, c.skipFailingGenesisTxs, logger, c.genesisMaxVMCycles) + if err != nil { + return fmt.Errorf("error in creating new app: %w", err) + } cfg.LocalApp = gnoApp gnoNode, err := node.DefaultNewNode(cfg, logger) @@ -158,11 +265,10 @@ func execStart(c *startCfg, args []string, io *commands.IO) error { return fmt.Errorf("error in creating node: %w", err) } - fmt.Fprintln(io.Err, "Node created.") + fmt.Fprintln(io.Err(), "Node created.") if c.skipStart { - fmt.Fprintln(io.Err, "'--skip-start' is set. Exiting.") - + io.ErrPrintln("'--skip-start' is set. Exiting.") return nil } @@ -170,151 +276,99 @@ func execStart(c *startCfg, args []string, io *commands.IO) error { return fmt.Errorf("error in start node: %w", err) } - // run forever osm.TrapSignal(func() { if gnoNode.IsRunning() { _ = gnoNode.Stop() } + + // Sync the logger before exiting + _ = zapLogger.Sync() }) - select {} // run forever + // Run forever + select {} } -// Makes a local test genesis doc with local privValidator. -func makeGenesisDoc( - pvPub crypto.PubKey, - chainID string, - genesisBalancesFile string, - genesisTxs []std.Tx, -) *bft.GenesisDoc { +func generateGenesisFile(genesisFile string, pk crypto.PubKey, c *startCfg) error { gen := &bft.GenesisDoc{} - gen.GenesisTime = time.Now() - gen.ChainID = chainID + gen.ChainID = c.chainID gen.ConsensusParams = abci.ConsensusParams{ Block: &abci.BlockParams{ // TODO: update limits. - MaxTxBytes: 1000000, // 1MB, - MaxDataBytes: 2000000, // 2MB, - MaxGas: 10000000, // 10M gas - TimeIotaMS: 100, // 100ms + MaxTxBytes: 1_000_000, // 1MB, + MaxDataBytes: 2_000_000, // 2MB, + MaxGas: 10_0000_00, // 10M gas + TimeIotaMS: 100, // 100ms }, } + gen.Validators = []bft.GenesisValidator{ { - Address: pvPub.Address(), - PubKey: pvPub, + Address: pk.Address(), + PubKey: pk, Power: 10, Name: "testvalidator", }, } - // Load distribution. - balances := loadGenesisBalances(genesisBalancesFile) - // debug: for _, balance := range balances { fmt.Println(balance) } - - // Load initial packages from examples. - test1 := crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") - txs := []std.Tx{} - - // List initial packages to load from examples. - pkgs, err := gnomod.ListPkgs(filepath.Join("..", "examples")) + // Load balances files + balances, err := gnoland.LoadGenesisBalancesFile(c.genesisBalancesFile) if err != nil { - panic(fmt.Errorf("listing gno packages: %w", err)) + return fmt.Errorf("unable to load genesis balances file %q: %w", c.genesisBalancesFile, err) } - // Sort packages by dependencies. - sortedPkgs, err := pkgs.Sort() + // Load examples folder + examplesDir := filepath.Join(c.gnoRootDir, "examples") + test1 := crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + defaultFee := std.NewFee(50000, std.MustParseCoin("1000000ugnot")) + pkgsTxs, err := gnoland.LoadPackagesFromDir(examplesDir, test1, defaultFee, nil) if err != nil { - panic(fmt.Errorf("sorting packages: %w", err)) + return fmt.Errorf("unable to load examples folder: %w", err) } - // Filter out draft packages. - nonDraftPkgs := sortedPkgs.GetNonDraftPkgs() - - for _, pkg := range nonDraftPkgs { - // open files in directory as MemPackage. - memPkg := gno.ReadMemPackage(pkg.Dir, pkg.Name) - - var tx std.Tx - tx.Msgs = []std.Msg{ - vmm.MsgAddPackage{ - Creator: test1, - Package: memPkg, - Deposit: nil, - }, - } - tx.Fee = std.NewFee(50000, std.MustParseCoin("1000000ugnot")) - tx.Signatures = make([]std.Signature, len(tx.GetSigners())) - txs = append(txs, tx) + // Load Genesis TXs + genesisTxs, err := gnoland.LoadGenesisTxsFile(c.genesisTxsFile, c.chainID, c.genesisRemote) + if err != nil { + return fmt.Errorf("unable to load genesis txs file: %w", err) } - // load genesis txs from file. - txs = append(txs, genesisTxs...) + genesisTxs = append(pkgsTxs, genesisTxs...) - // construct genesis AppState. + // Construct genesis AppState. gen.AppState = gnoland.GnoGenesisState{ Balances: balances, - Txs: txs, + Txs: genesisTxs, } - return gen -} -func writeGenesisFile(gen *bft.GenesisDoc, filePath string) { - err := gen.SaveAs(filePath) - if err != nil { - panic(err) + // Write genesis state + if err := gen.SaveAs(genesisFile); err != nil { + return fmt.Errorf("unable to write genesis file %q: %w", genesisFile, err) } -} -func loadGenesisTxs( - path string, - chainID string, - genesisRemote string, -) []std.Tx { - txs := []std.Tx{} - txsBz := osm.MustReadFile(path) - txsLines := strings.Split(string(txsBz), "\n") - for _, txLine := range txsLines { - if txLine == "" { - continue // skip empty line - } - - // patch the TX - txLine = strings.ReplaceAll(txLine, "%%CHAINID%%", chainID) - txLine = strings.ReplaceAll(txLine, "%%REMOTE%%", genesisRemote) - - var tx std.Tx - amino.MustUnmarshalJSON([]byte(txLine), &tx) - txs = append(txs, tx) - } - - return txs + return nil } -func loadGenesisBalances(path string) []string { - // each balance is in the form: g1xxxxxxxxxxxxxxxx=100000ugnot - balances := []string{} - content := osm.MustReadFile(path) - lines := strings.Split(string(content), "\n") - for _, line := range lines { - line = strings.TrimSpace(line) - - // remove comments. - line = strings.Split(line, "#")[0] - line = strings.TrimSpace(line) - - // skip empty lines. - if line == "" { - continue - } +// getTxEventStoreConfig constructs an event store config from provided user options +func getTxEventStoreConfig(c *startCfg) (*eventstorecfg.Config, error) { + var cfg *eventstorecfg.Config - parts := strings.Split(line, "=") - if len(parts) != 2 { - panic("invalid genesis_balance line: " + line) + switch c.txEventStoreType { + case file.EventStoreType: + if c.txEventStorePath == "" { + return nil, errors.New("unspecified file transaction indexer path") } - balances = append(balances, line) + // Fill out the configuration + cfg = &eventstorecfg.Config{ + EventStoreType: file.EventStoreType, + Params: map[string]any{ + file.Path: c.txEventStorePath, + }, + } + default: + cfg = eventstorecfg.DefaultEventStoreConfig() } - return balances + + return cfg, nil } diff --git a/gno.land/cmd/gnoland/start_test.go b/gno.land/cmd/gnoland/start_test.go index 27ef2f572ea..6606d88ba2e 100644 --- a/gno.land/cmd/gnoland/start_test.go +++ b/gno.land/cmd/gnoland/start_test.go @@ -13,6 +13,8 @@ import ( ) func TestStartInitialize(t *testing.T) { + t.Parallel() + cases := []struct { args []string }{ @@ -23,8 +25,11 @@ func TestStartInitialize(t *testing.T) { os.Chdir(filepath.Join("..", "..")) // go to repo's root dir for _, tc := range cases { + tc := tc name := strings.Join(tc.args, " ") t.Run(name, func(t *testing.T) { + t.Parallel() + mockOut := bytes.NewBufferString("") mockErr := bytes.NewBufferString("") io := commands.NewTestIO() diff --git a/gno.land/cmd/gnoland/testdata/addpkg.txtar b/gno.land/cmd/gnoland/testdata/addpkg.txtar new file mode 100644 index 00000000000..7130fe54dab --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/addpkg.txtar @@ -0,0 +1,23 @@ +# test for add package + +## start a new node +gnoland start + +## add bar.gno package located in $WORK directory as gno.land/r/foobar/bar +gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/foobar/bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 + +## execute Render +gnokey maketx call -pkgpath gno.land/r/foobar/bar -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 + +## compare render +stdout '("hello from foo" string)' +stdout 'OK!' +stdout 'GAS WANTED: 2000000' +stdout 'GAS USED: [0-9]+' + +-- bar.gno -- +package bar + +func Render(path string) string { + return "hello from foo" +} \ No newline at end of file diff --git a/gno.land/cmd/gnoland/testdata/append.txtar b/gno.land/cmd/gnoland/testdata/append.txtar new file mode 100644 index 00000000000..792d71882e5 --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/append.txtar @@ -0,0 +1,129 @@ +# start a new node +gnoland start + +gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/append -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout OK! + +# Call Append 1 +gnokey maketx call -pkgpath gno.land/r/append -func Append -gas-fee 1000000ugnot -gas-wanted 2000000 -args '1' -broadcast -chainid=tendermint_test test1 +stdout OK! + +gnokey maketx call -pkgpath gno.land/r/append -func AppendNil -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout OK! + +# Call Append 2 +gnokey maketx call -pkgpath gno.land/r/append -func Append -gas-fee 1000000ugnot -gas-wanted 2000000 -args '2' -broadcast -chainid=tendermint_test test1 +stdout OK! + +# Call Append 3 +gnokey maketx call -pkgpath gno.land/r/append -func Append -gas-fee 1000000ugnot -gas-wanted 2000000 -args '3' -broadcast -chainid=tendermint_test test1 +stdout OK! + +# Call render +gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout '("1-2-3-" string)' +stdout OK! + +# Call Pop +gnokey maketx call -pkgpath gno.land/r/append -func Pop -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout OK! + +# Call render +gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout '("2-3-" string)' +stdout OK! + +# Call Append 42 +gnokey maketx call -pkgpath gno.land/r/append -func Append -gas-fee 1000000ugnot -gas-wanted 2000000 -args '42' -broadcast -chainid=tendermint_test test1 +stdout OK! + +# Call render +gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout '("2-3-42-" string)' +stdout OK! + +gnokey maketx call -pkgpath gno.land/r/append -func CopyAppend -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout OK! + +gnokey maketx call -pkgpath gno.land/r/append -func PopB -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout OK! + +# Call render +gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout '("2-3-42-" string)' +stdout OK! + +gnokey maketx call -pkgpath gno.land/r/append -func AppendMoreAndC -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout OK! + +gnokey maketx call -pkgpath gno.land/r/append -func ReassignC -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout OK! + +gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout '("2-3-42-70-100-" string)' +stdout OK! + +gnokey maketx call -pkgpath gno.land/r/append -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args 'd' -broadcast -chainid=tendermint_test test1 +stdout '("1-" string)' +stdout OK! + +-- append.gno -- +package append + +import ( + "gno.land/p/demo/ufmt" +) + +type T struct{ i int } + +var a, b, d []T +var c = []T{{i: 100}} + + +func init() { + a = make([]T, 0, 1) +} + +func Pop() { + a = append(a[:0], a[1:]...) +} + +func Append(i int) { + a = append(a, T{i: i}) +} + +func CopyAppend() { + b = append(a, T{i: 50}, T{i: 60}) +} + +func PopB() { + b = append(b[:0], b[1:]...) +} + +func AppendMoreAndC() { + // Fill to capacity + a = append(a, T{i: 70}) + // Above capacity; make new array + a = append(a, c...) +} + +func ReassignC() { + c[0] = T{i: 200} +} + +func AppendNil() { + d = append(d, a...) +} + +func Render(path string) string { + source := a + if path == "d" { + source = d + } + + var s string + for i:=0;i *Game + +var counter byte + +func New(s string) string { + // Bug shows if Moves has a cap > 0 when initialised. + el := &Game{Position: Position{Moves: make([]Move, 0, 2)}} + games.Set(s, el) + return values(el.Position) +} + +func Delta(s string) string { + v, _ := games.Get(s) + g, ok := v.(*Game) + if !ok { + panic("invalid game") + } + n := g.Position.update() + g.Position = n + ret := values(n) + return ret +} + +func Render(s string) string { + v, _ := games.Get(s) + g, ok := v.(*Game) + if !ok { + panic("invalid game") + } + return values(g.Position) +} + +func values(x Position) string { + s := "" + for _, val := range x.Moves { + s += strconv.Itoa(int(val.N1)) + "," + strconv.Itoa(int(val.N2)) + "," + strconv.Itoa(int(val.N3)) + ";" + } + return s +} diff --git a/gno.land/cmd/gnoland/testdata/issue-gnochess-97.txtar b/gno.land/cmd/gnoland/testdata/issue-gnochess-97.txtar new file mode 100644 index 00000000000..89406d328d4 --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/issue-gnochess-97.txtar @@ -0,0 +1,41 @@ +# test for https://github.com/gnolang/gnochess/issues/97 + +gnoland start + +gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/demo/bug97 -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 + +gnokey maketx call -pkgpath 'gno.land/r/demo/bug97' -func 'RealmCall1' -gas-fee 1000000ugnot -gas-wanted 2000000 -send '' -broadcast -chainid='tendermint_test' test1 +stdout 'OK!' + +gnokey maketx call -pkgpath 'gno.land/r/demo/bug97' -func 'RealmCall2' -gas-fee 1000000ugnot -gas-wanted 2000000 -send '' -broadcast -chainid='tendermint_test' test1 +stdout 'OK!' + +gnokey maketx call -pkgpath 'gno.land/r/demo/bug97' -func 'RealmCall1' -gas-fee 1000000ugnot -gas-wanted 2000000 -send '' -broadcast -chainid='tendermint_test' test1 +stdout 'OK!' + +-- bug97.gno -- +package bug97 + +var x = [3]int{1, 2, 3} + +func newX() [3]int { return x} + +type S struct { + Arr [3]int +} + +func NewS() S { + return S{Arr: x} +} + +var s S + +func RealmCall1() { + s = NewS() +} + +func RealmCall2() { + arr2 := s.Arr + arr2[0] = 8 + s = S{Arr: arr2} +} diff --git a/gno.land/cmd/gnoland/testdata/run.txtar b/gno.land/cmd/gnoland/testdata/run.txtar new file mode 100644 index 00000000000..94b32de041e --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/run.txtar @@ -0,0 +1,28 @@ +## start a new node +gnoland start + +## add bar.gno package located in $WORK directory as gno.land/r/foobar/bar +gnokey maketx addpkg -pkgdir $WORK/bar -pkgpath gno.land/r/foobar/bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 + +## execute Render +gnokey maketx run -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 $WORK/script/script.gno + +## compare render +stdout 'main: --- hello from foo ---' +stdout 'OK!' +stdout 'GAS WANTED: 200000' +stdout 'GAS USED: ' + +-- bar/bar.gno -- +package bar + +func Render(path string) string { + return "hello from foo" +} + +-- script/script.gno -- +package main +import "gno.land/r/foobar/bar" +func main() { + println("main: ---", bar.Render(""), "---") +} diff --git a/gno.land/cmd/gnoland/testdata/wugnot.txtar b/gno.land/cmd/gnoland/testdata/wugnot.txtar new file mode 100644 index 00000000000..5c2d7d3cb90 --- /dev/null +++ b/gno.land/cmd/gnoland/testdata/wugnot.txtar @@ -0,0 +1,43 @@ +gnoland start + +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout '# wrapped GNOT \(\$wugnot\)' +stdout 'Decimals..: 0' +stdout 'Total supply..: 0' +stdout 'Known accounts..: 0' +stdout 'OK!' + +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Deposit -send 12345678ugnot -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout 'OK!' + +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout 'Total supply..: 12345678' +stdout 'Known accounts..: 1' +stdout 'OK!' + +# XXX: use test2 instead (depends on https://github.com/gnolang/gno/issues/1269#issuecomment-1806386069) +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Deposit -send 12345678ugnot -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout 'OK!' + +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout 'Total supply..: 24691356' +stdout 'Known accounts..: 1' # should be 2 once we can use test2 +stdout 'OK!' + +# XXX: replace hardcoded address with test3 +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Transfer -gas-fee 1000000ugnot -gas-wanted 2000000 -args 'g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq' -args '10000000' -broadcast -chainid=tendermint_test test1 +stdout 'OK!' + +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout 'Total supply..: 24691356' +stdout 'Known accounts..: 2' # should be 3 once we can use test2 +stdout 'OK!' + +# XXX: use test3 instead (depends on https://github.com/gnolang/gno/issues/1269#issuecomment-1806386069) +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Withdraw -args 10000000 -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1 +stdout 'OK!' + +gnokey maketx call -pkgpath gno.land/r/demo/wugnot -func Render -gas-fee 1000000ugnot -gas-wanted 2000000 -args '' -broadcast -chainid=tendermint_test test1 +stdout 'Total supply..: 14691356' +stdout 'Known accounts..: 2' # should be 3 once we can use test2 +stdout 'OK!' diff --git a/gno.land/cmd/gnotxsync/README.md b/gno.land/cmd/gnotxsync/README.md deleted file mode 100644 index 3bd032a5f23..00000000000 --- a/gno.land/cmd/gnotxsync/README.md +++ /dev/null @@ -1,3 +0,0 @@ -This is a simple tool to fetch (valid) transactions using the HTTP rpc. -It is pretty slow, especially from a remote machine. -TODO: make it faster by running on local database instance. diff --git a/gno.land/cmd/gnotxsync/export.go b/gno.land/cmd/gnotxsync/export.go deleted file mode 100644 index f22a4cd22f4..00000000000 --- a/gno.land/cmd/gnotxsync/export.go +++ /dev/null @@ -1,143 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "io" - "log" - "os" - "strings" - "time" - - "github.com/gnolang/gno/tm2/pkg/amino" - "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" - "github.com/gnolang/gno/tm2/pkg/commands" - "github.com/gnolang/gno/tm2/pkg/std" - - _ "github.com/gnolang/gno/gno.land/pkg/sdk/vm" - _ "github.com/gnolang/gno/tm2/pkg/sdk/auth" // XXX better way? - _ "github.com/gnolang/gno/tm2/pkg/sdk/bank" -) - -type exportCfg struct { - rootCfg *config - - startHeight int64 - tailHeight int64 - endHeight int64 - outFile string - quiet bool - follow bool -} - -func newExportCommand(rootCfg *config) *commands.Command { - cfg := &exportCfg{ - rootCfg: rootCfg, - } - - return commands.NewCommand( - commands.Metadata{ - Name: "export", - ShortUsage: "export [flags] ", - ShortHelp: "Export transactions to file", - }, - cfg, - func(_ context.Context, _ []string) error { - return execExport(cfg) - }, - ) -} - -func (c *exportCfg) RegisterFlags(fs *flag.FlagSet) { - fs.Int64Var(&c.startHeight, "start", 1, "start height") - fs.Int64Var(&c.tailHeight, "tail", 0, "start at LAST - N") - fs.Int64Var(&c.endHeight, "end", 0, "end height (optional)") - fs.StringVar(&c.outFile, "out", defaultFilePath, "output file path") - fs.BoolVar(&c.quiet, "quiet", false, "omit console output during execution") - fs.BoolVar(&c.follow, "follow", false, "keep attached and follow new events") -} - -func execExport(c *exportCfg) error { - node := client.NewHTTP(c.rootCfg.remote, "/websocket") - - status, err := node.Status() - if err != nil { - return fmt.Errorf("unable to fetch node status, %w", err) - } - - var ( - start = c.startHeight - end = c.endHeight - tail = c.tailHeight - ) - - if end == 0 { // take last block height - end = status.SyncInfo.LatestBlockHeight - } - if tail > 0 { - start = end - tail - } - - var out io.Writer - switch c.outFile { - case "-", "STDOUT": - out = os.Stdout - default: - out, err = os.OpenFile(c.outFile, os.O_RDWR|os.O_CREATE, 0o755) - if err != nil { - return err - } - } - - for height := start; ; height++ { - if !c.follow && height >= end { - break - } - - getBlock: - block, err := node.Block(&height) - if err != nil { - if c.follow && strings.Contains(err.Error(), "") { - time.Sleep(time.Second) - - goto getBlock - } - - return fmt.Errorf("encountered error while fetching block, %w", err) - } - - txs := block.Block.Data.Txs - if len(txs) == 0 { - continue - } - - _, err = node.BlockResults(&height) - if err != nil { - if c.follow && strings.Contains(err.Error(), "") { - time.Sleep(time.Second) - - goto getBlock - } - - return fmt.Errorf("encountered error while fetching block results, %w", err) - } - - for i := 0; i < len(txs); i++ { - tx := txs[i] - stdtx := std.Tx{} - - amino.MustUnmarshal(tx, &stdtx) - - bz := amino.MustMarshalJSON(stdtx) - - _, _ = fmt.Fprintln(out, string(bz)) - } - - if !c.quiet { - log.Printf("h=%d/%d (txs=%d)", height, end, len(txs)) - } - } - - return nil -} diff --git a/gno.land/cmd/gnotxsync/import.go b/gno.land/cmd/gnotxsync/import.go deleted file mode 100644 index 3369b5378b5..00000000000 --- a/gno.land/cmd/gnotxsync/import.go +++ /dev/null @@ -1,118 +0,0 @@ -package main - -import ( - "bufio" - "context" - "flag" - "fmt" - "os" - "time" - - "github.com/gnolang/gno/tm2/pkg/amino" - "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" - "github.com/gnolang/gno/tm2/pkg/commands" - "github.com/gnolang/gno/tm2/pkg/errors" - "github.com/gnolang/gno/tm2/pkg/std" - - _ "github.com/gnolang/gno/gno.land/pkg/sdk/vm" - _ "github.com/gnolang/gno/tm2/pkg/sdk/auth" // XXX better way? - _ "github.com/gnolang/gno/tm2/pkg/sdk/bank" -) - -type importCfg struct { - rootCfg *config - - inFile string -} - -func newImportCommand(rootCfg *config) *commands.Command { - cfg := &importCfg{ - rootCfg: rootCfg, - } - - return commands.NewCommand( - commands.Metadata{ - Name: "import", - ShortUsage: "import [flags] ", - ShortHelp: "Import transactions from file", - }, - cfg, - func(ctx context.Context, _ []string) error { - return execImport(ctx, cfg) - }, - ) -} - -func (c *importCfg) RegisterFlags(fs *flag.FlagSet) { - fs.StringVar(&c.inFile, "in", defaultFilePath, "input file path") -} - -func execImport(ctx context.Context, c *importCfg) error { - // Initial validation - if len(c.inFile) == 0 { - return errors.New("input file path not specified") - } - - // Read the input file - file, err := os.Open(c.inFile) - if err != nil { - return fmt.Errorf("unable to open input file, %w", err) - } - - defer file.Close() - - // Start the WS connection to the node - node := client.NewHTTP(c.rootCfg.remote, "/websocket") - - index := 0 - scanner := bufio.NewScanner(file) - for scanner.Scan() { - select { - case <-ctx.Done(): - // Stop signal received while parsing - // the import file - return nil - default: - print(".") - - line := scanner.Text() - if len(line) == 0 { - return fmt.Errorf("empty line encountered at %d", index) - } - - var tx std.Tx - amino.MustUnmarshalJSON([]byte(line), &tx) - txbz := amino.MustMarshal(tx) - - res, err := node.BroadcastTxSync(txbz) - - if err != nil || res.Error != nil { - print("!") - // wait for next block and try again. - // TODO: actually wait 1 block instead of fudging it. - time.Sleep(20 * time.Second) - - res, err := node.BroadcastTxSync(txbz) - if err != nil || res.Error != nil { - if err != nil { - fmt.Println("SECOND ERROR", err) - } else { - fmt.Println("SECOND ERROR!", res.Error) - } - - fmt.Println(line) - - return errors.Wrap(err, "broadcasting tx %d", index) - } - } - - index++ - } - } - - if err := scanner.Err(); err != nil { - return fmt.Errorf("error encountered while reading file, %w", err) - } - - return nil -} diff --git a/gno.land/cmd/gnotxsync/main.go b/gno.land/cmd/gnotxsync/main.go deleted file mode 100644 index cdd65f61e8a..00000000000 --- a/gno.land/cmd/gnotxsync/main.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "os" - - "github.com/gnolang/gno/tm2/pkg/commands" -) - -// config is the shared config for gnotxport, and its subcommands -type config struct { - remote string `default:"localhost:26657"` -} - -const ( - defaultFilePath = "txexport.log" -) - -func main() { - cfg := &config{} - - cmd := commands.NewCommand( - commands.Metadata{ - ShortUsage: " [flags] [...]", - LongHelp: "Exports or imports transactions from the node", - }, - cfg, - commands.HelpExec, - ) - - cmd.AddSubCommands( - newImportCommand(cfg), - newExportCommand(cfg), - ) - - if err := cmd.ParseAndRun(context.Background(), os.Args[1:]); err != nil { - _, _ = fmt.Fprintf(os.Stderr, "%+v\n", err) - - os.Exit(1) - } -} - -func (c *config) RegisterFlags(fs *flag.FlagSet) { - fs.StringVar( - &c.remote, - "remote", - "localhost:26657", - "remote RPC address ", - ) -} diff --git a/gno.land/cmd/gnoweb/main.go b/gno.land/cmd/gnoweb/main.go index e8a2feac0d7..547134548ff 100644 --- a/gno.land/cmd/gnoweb/main.go +++ b/gno.land/cmd/gnoweb/main.go @@ -1,450 +1,60 @@ -// main.go - package main import ( - "encoding/json" - "errors" "flag" "fmt" - "io" "net/http" "os" - "path/filepath" - "runtime" - "strings" "time" - "github.com/gnolang/gno/tm2/pkg/amino" - abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" - "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" - osm "github.com/gnolang/gno/tm2/pkg/os" - "github.com/gnolang/gno/tm2/pkg/std" - "github.com/gorilla/mux" - "github.com/gotuna/gotuna" - - "github.com/gnolang/gno/gno.land/cmd/gnoweb/static" // for static files - "github.com/gnolang/gno/gno.land/pkg/sdk/vm" // for error types + // for static files + "github.com/gnolang/gno/gno.land/pkg/gnoweb" + "github.com/gnolang/gno/gno.land/pkg/log" + "go.uber.org/zap/zapcore" + // for error types // "github.com/gnolang/gno/tm2/pkg/sdk" // for baseapp (info, status) ) -const ( - qFileStr = "vm/qfile" -) - -var flags struct { - bindAddr string - remoteAddr string - captchaSite string - faucetURL string - viewsDir string - pagesDir string - helpChainID string - helpRemote string -} - -var startedAt time.Time - -func init() { - flag.StringVar(&flags.remoteAddr, "remote", "127.0.0.1:26657", "remote gnoland node address") - flag.StringVar(&flags.bindAddr, "bind", "127.0.0.1:8888", "server listening address") - flag.StringVar(&flags.captchaSite, "captcha-site", "", "recaptcha site key (if empty, captcha are disabled)") - flag.StringVar(&flags.faucetURL, "faucet-url", "http://localhost:5050", "faucet server URL") - flag.StringVar(&flags.viewsDir, "views-dir", "./cmd/gnoweb/views", "views directory location") - flag.StringVar(&flags.pagesDir, "pages-dir", "./cmd/gnoweb/pages", "pages directory location") - flag.StringVar(&flags.helpChainID, "help-chainid", "dev", "help page's chainid") - flag.StringVar(&flags.helpRemote, "help-remote", "127.0.0.1:26657", "help page's remote addr") - startedAt = time.Now() -} - -func makeApp() gotuna.App { - app := gotuna.App{ - ViewFiles: os.DirFS(flags.viewsDir), - Router: gotuna.NewMuxRouter(), - Static: static.EmbeddedStatic, - // StaticPrefix: "static/", - } - app.Router.Handle("/", handlerHome(app)) - app.Router.Handle("/about", handlerAbout(app)) - app.Router.Handle("/game-of-realms", handlerGor(app)) - app.Router.Handle("/faucet", handlerFaucet(app)) - app.Router.Handle("/r/demo/boards:gnolang/6", handlerRedirect(app)) - // NOTE: see rePathPart. - app.Router.Handle("/r/{rlmname:[a-z][a-z0-9_]*(?:/[a-z][a-z0-9_]*)+}/{filename:(?:.*\\.(?:gno|md|txt)$)?}", handlerRealmFile(app)) - app.Router.Handle("/r/{rlmname:[a-z][a-z0-9_]*(?:/[a-z][a-z0-9_]*)+}", handlerRealmMain(app)) - app.Router.Handle("/r/{rlmname:[a-z][a-z0-9_]*(?:/[a-z][a-z0-9_]*)+}:{querystr:.*}", handlerRealmRender(app)) - app.Router.Handle("/p/{filepath:.*}", handlerPackageFile(app)) - app.Router.Handle("/static/{path:.+}", handlerStaticFile(app)) - app.Router.Handle("/favicon.ico", handlerFavicon(app)) - app.Router.Handle("/status.json", handlerStatusJSON(app)) - return app -} - func main() { - flag.Parse() - fmt.Printf("Running on http://%s\n", flags.bindAddr) - server := &http.Server{ - Addr: flags.bindAddr, - ReadHeaderTimeout: 60 * time.Second, - Handler: makeApp().Router, - } - - if err := server.ListenAndServe(); err != nil { - fmt.Fprintf(os.Stderr, "HTTP server stopped with error: %+v\n", err) + err := runMain(os.Args[1:]) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "%+v\n", err) + os.Exit(1) } } -func handlerHome(app gotuna.App) http.Handler { - md := filepath.Join(flags.pagesDir, "HOME.md") - homeContent := osm.MustReadFile(md) - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - app.NewTemplatingEngine(). - Set("Title", "Gno.land Smart Contract Platform Using Gnolang (Gno)"). - Set("Description", "Gno.land is the only smart contract platform using the Gnolang (Gno) programming language, an interpretation of the widely-used Golang (Go)."). - Set("HomeContent", string(homeContent)). - Render(w, r, "home.html", "funcs.html") - }) -} - -func handlerAbout(app gotuna.App) http.Handler { - md := filepath.Join(flags.pagesDir, "ABOUT.md") - mainContent := osm.MustReadFile(md) - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - app.NewTemplatingEngine(). - Set("Title", "Gno.land Is A Platform To Write Smart Contracts In Gnolang (Gno)"). - Set("Description", "On Gno.land, developers write smart contracts and other blockchain apps using Gnolang (Gno) without learning a language that’s exclusive to a single ecosystem."). - Set("MainContent", string(mainContent)). - Render(w, r, "generic.html", "funcs.html") - }) -} - -func handlerGor(app gotuna.App) http.Handler { - md := filepath.Join(flags.pagesDir, "GOR.md") - mainContent := osm.MustReadFile(md) - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - app.NewTemplatingEngine(). - Set("MainContent", string(mainContent)). - Set("Title", "Game of Realms Content For The Best Contributors "). - Set("Description", "Game of Realms is the first high-stakes competition held in two phases to find the best contributors to the Gno.land platform with a 133,700 ATOM prize pool."). - Render(w, r, "generic.html", "funcs.html") - }) -} - -func handlerFaucet(app gotuna.App) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - app.NewTemplatingEngine(). - Set("captchaSite", flags.captchaSite). - Set("faucetURL", flags.faucetURL). - Render(w, r, "faucet.html", "funcs.html") - }) -} - -func handlerStatusJSON(app gotuna.App) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var ret struct { - Gnoland struct { - Connected bool `json:"connected"` - Error *string `json:"error,omitempty"` - Height *int64 `json:"height,omitempty"` - // processed txs - // active connections - - Version *string `json:"version,omitempty"` - // Uptime *float64 `json:"uptime-seconds,omitempty"` - // Goarch *string `json:"goarch,omitempty"` - // Goos *string `json:"goos,omitempty"` - // GoVersion *string `json:"go-version,omitempty"` - // NumCPU *int `json:"num_cpu,omitempty"` - } `json:"gnoland"` - Website struct { - // Version string `json:"version"` - Uptime float64 `json:"uptime-seconds"` - Goarch string `json:"goarch"` - Goos string `json:"goos"` - GoVersion string `json:"go-version"` - NumCPU int `json:"num_cpu"` - } `json:"website"` - } - ret.Website.Uptime = time.Since(startedAt).Seconds() - ret.Website.Goarch = runtime.GOARCH - ret.Website.Goos = runtime.GOOS - ret.Website.NumCPU = runtime.NumCPU() - ret.Website.GoVersion = runtime.Version() - - ret.Gnoland.Connected = true - res, err := makeRequest(".app/version", []byte{}) - if err != nil { - ret.Gnoland.Connected = false - errmsg := err.Error() - ret.Gnoland.Error = &errmsg - } else { - version := string(res.Value) - ret.Gnoland.Version = &version - ret.Gnoland.Height = &res.Height - } - - out, _ := json.MarshalIndent(ret, "", " ") - w.Header().Set("Content-Type", "application/json") - w.Write(out) - }) -} - -// XXX temporary. -func handlerRedirect(app gotuna.App) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, "/r/boards:gnolang/3", http.StatusFound) - app.NewTemplatingEngine(). - Render(w, r, "home.html", "funcs.html") - }) -} +func runMain(args []string) error { + var ( + fs = flag.NewFlagSet("gnoweb", flag.ContinueOnError) + cfg = gnoweb.NewDefaultConfig() + bindAddress string + ) + fs.StringVar(&cfg.RemoteAddr, "remote", cfg.RemoteAddr, "remote gnoland node address") + fs.StringVar(&cfg.CaptchaSite, "captcha-site", cfg.CaptchaSite, "recaptcha site key (if empty, captcha are disabled)") + fs.StringVar(&cfg.FaucetURL, "faucet-url", cfg.FaucetURL, "faucet server URL") + fs.StringVar(&cfg.ViewsDir, "views-dir", cfg.ViewsDir, "views directory location") // XXX: replace with goembed + fs.StringVar(&cfg.HelpChainID, "help-chainid", cfg.HelpChainID, "help page's chainid") + fs.StringVar(&cfg.HelpRemote, "help-remote", cfg.HelpRemote, "help page's remote addr") + fs.BoolVar(&cfg.WithAnalytics, "with-analytics", cfg.WithAnalytics, "enable privacy-first analytics") + fs.StringVar(&bindAddress, "bind", "127.0.0.1:8888", "server listening address") -func handlerRealmMain(app gotuna.App) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - rlmname := vars["rlmname"] - rlmpath := "gno.land/r/" + rlmname - query := r.URL.Query() - if query.Has("help") { - // Render function helper. - funcName := query.Get("__func") - qpath := "vm/qfuncs" - data := []byte(rlmpath) - res, err := makeRequest(qpath, data) - if err != nil { - writeError(w, err) - return - } - var fsigs vm.FunctionSignatures - amino.MustUnmarshalJSON(res.Data, &fsigs) - // Fill fsigs with query parameters. - for i := range fsigs { - fsig := &(fsigs[i]) - for j := range fsig.Params { - param := &(fsig.Params[j]) - value := query.Get(param.Name) - param.Value = value - } - } - // Render template. - tmpl := app.NewTemplatingEngine() - tmpl.Set("FuncName", funcName) - tmpl.Set("RealmPath", rlmpath) - tmpl.Set("Remote", flags.helpRemote) - tmpl.Set("ChainID", flags.helpChainID) - tmpl.Set("DirPath", pathOf(rlmpath)) - tmpl.Set("FunctionSignatures", fsigs) - tmpl.Render(w, r, "realm_help.html", "funcs.html") - } else { - // Ensure realm exists. TODO optimize. - qpath := qFileStr - data := []byte(rlmpath) - _, err := makeRequest(qpath, data) - if err != nil { - writeError(w, errors.New("error querying realm package")) - return - } - // Render blank query path, /r/REALM:. - handleRealmRender(app, w, r) - } - }) -} - -type pathLink struct { - URL string - Text string -} - -func handlerRealmRender(app gotuna.App) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - handleRealmRender(app, w, r) - }) -} - -func handleRealmRender(app gotuna.App, w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - rlmname := vars["rlmname"] - rlmpath := "gno.land/r/" + rlmname - querystr := vars["querystr"] - if r.URL.Path == "/r/"+rlmname+":" { - // Redirect to /r/REALM if querypath is empty. - http.Redirect(w, r, "/r/"+rlmname, http.StatusFound) - return - } - qpath := "vm/qrender" - data := []byte(fmt.Sprintf("%s\n%s", rlmpath, querystr)) - res, err := makeRequest(qpath, data) - if err != nil { - // XXX hack - if strings.Contains(err.Error(), "Render not declared") { - res = &abci.ResponseQuery{} - res.Data = []byte("realm package has no Render() function") - } else { - writeError(w, err) - return - } - } - // linkify querystr. - queryParts := strings.Split(querystr, "/") - pathLinks := []pathLink{} - for i, part := range queryParts { - pathLinks = append(pathLinks, pathLink{ - URL: "/r/" + rlmname + ":" + strings.Join(queryParts[:i+1], "/"), - Text: part, - }) + if err := fs.Parse(args); err != nil { + return err } - // Render template. - tmpl := app.NewTemplatingEngine() - tmpl.Set("RealmName", rlmname) - tmpl.Set("RealmPath", rlmpath) - tmpl.Set("Query", querystr) - tmpl.Set("PathLinks", pathLinks) - tmpl.Set("Contents", string(res.Data)) - tmpl.Render(w, r, "realm_render.html", "funcs.html") -} - -func handlerRealmFile(app gotuna.App) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - diruri := "gno.land/r/" + vars["rlmname"] - filename := vars["filename"] - renderPackageFile(app, w, r, diruri, filename) - }) -} - -func handlerPackageFile(app gotuna.App) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - pkgpath := "gno.land/p/" + vars["filepath"] - diruri, filename := std.SplitFilepath(pkgpath) - if filename == "" && diruri == pkgpath { - // redirect to diruri + "/" - http.Redirect(w, r, "/p/"+vars["filepath"]+"/", http.StatusFound) - return - } - renderPackageFile(app, w, r, diruri, filename) - }) -} + zapLogger := log.NewZapConsoleLogger(os.Stdout, zapcore.DebugLevel) + logger := log.ZapLoggerToSlog(zapLogger) -func renderPackageFile(app gotuna.App, w http.ResponseWriter, r *http.Request, diruri string, filename string) { - if filename == "" { - // Request is for a folder. - qpath := qFileStr - data := []byte(diruri) - res, err := makeRequest(qpath, data) - if err != nil { - writeError(w, err) - return - } - files := strings.Split(string(res.Data), "\n") - // Render template. - tmpl := app.NewTemplatingEngine() - tmpl.Set("DirURI", diruri) - tmpl.Set("DirPath", pathOf(diruri)) - tmpl.Set("Files", files) - tmpl.Render(w, r, "package_dir.html", "funcs.html") - } else { - // Request is for a file. - filepath := diruri + "/" + filename - qpath := qFileStr - data := []byte(filepath) - res, err := makeRequest(qpath, data) - if err != nil { - writeError(w, err) - return - } - // Render template. - tmpl := app.NewTemplatingEngine() - tmpl.Set("DirURI", diruri) - tmpl.Set("DirPath", pathOf(diruri)) - tmpl.Set("FileName", filename) - tmpl.Set("FileContents", string(res.Data)) - tmpl.Render(w, r, "package_file.html", "funcs.html") + logger.Info("Running", "listener", "http://"+bindAddress) + server := &http.Server{ + Addr: bindAddress, + ReadHeaderTimeout: 60 * time.Second, + Handler: gnoweb.MakeApp(logger, cfg).Router, } -} -func makeRequest(qpath string, data []byte) (res *abci.ResponseQuery, err error) { - opts2 := client.ABCIQueryOptions{ - // Height: height, XXX - // Prove: false, XXX - } - remote := flags.remoteAddr - cli := client.NewHTTP(remote, "/websocket") - qres, err := cli.ABCIQueryWithOptions( - qpath, data, opts2) - if err != nil { - return nil, err - } - if qres.Response.Error != nil { - fmt.Printf("Log: %s\n", - qres.Response.Log) - return nil, qres.Response.Error + if err := server.ListenAndServe(); err != nil { + logger.Error("HTTP server stopped", " error:", err) } - return &qres.Response, nil -} - -func handlerStaticFile(app gotuna.App) http.Handler { - fs := http.FS(app.Static) - fileapp := http.StripPrefix("/static", http.FileServer(fs)) - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - fpath := filepath.Clean(vars["path"]) - f, err := fs.Open(fpath) - if os.IsNotExist(err) { - handleNotFound(app, fpath, w, r) - return - } - stat, err := f.Stat() - if err != nil || stat.IsDir() { - handleNotFound(app, fpath, w, r) - return - } - - // TODO: ModTime doesn't work for embed? - // w.Header().Set("ETag", fmt.Sprintf("%x", stat.ModTime().UnixNano())) - // w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%s", "31536000")) - fileapp.ServeHTTP(w, r) - }) -} -func handlerFavicon(app gotuna.App) http.Handler { - fs := http.FS(app.Static) - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fpath := "img/favicon.ico" - f, err := fs.Open(fpath) - if os.IsNotExist(err) { - handleNotFound(app, fpath, w, r) - return - } - w.Header().Set("Content-Type", "image/x-icon") - w.Header().Set("Cache-Control", "public, max-age=604800") // 7d - io.Copy(w, f) - }) -} - -func handleNotFound(app gotuna.App, path string, w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotFound) - app.NewTemplatingEngine(). - Set("title", "Not found"). - Set("path", path). - Render(w, r, "404.html", "funcs.html") -} - -func writeError(w http.ResponseWriter, err error) { - w.WriteHeader(500) - w.Write([]byte(err.Error())) -} - -func pathOf(diruri string) string { - parts := strings.Split(diruri, "/") - if parts[0] == "gno.land" { - return "/" + strings.Join(parts[1:], "/") - } else { - panic(fmt.Sprintf("invalid dir-URI %q", diruri)) - } + return zapLogger.Sync() } diff --git a/gno.land/cmd/gnoweb/main_test.go b/gno.land/cmd/gnoweb/main_test.go index 579e1bcd06b..640c4763140 100644 --- a/gno.land/cmd/gnoweb/main_test.go +++ b/gno.land/cmd/gnoweb/main_test.go @@ -1,55 +1,14 @@ package main import ( - "fmt" - "net/http" - "net/http/httptest" - "os" - "strings" + "errors" + "flag" "testing" - - "github.com/gotuna/gotuna/test/assert" ) -func TestRoutes(t *testing.T) { - ok := http.StatusOK - routes := []struct { - route string - status int - substring string - }{ - {"/", ok, "Welcome"}, // assert / gives 200 (OK). assert / contains "Welcome". - {"/about", ok, "blockchain"}, - {"/r/gnoland/blog", ok, ""}, // whatever content - {"/r/gnoland/blog?help", ok, "exposed"}, - {"/r/gnoland/blog/", ok, "admin.gno"}, - {"/r/gnoland/blog/admin.gno", ok, "func "}, - {"/r/demo/users:administrator", ok, "address"}, - {"/r/demo/users", ok, "manfred"}, - {"/r/demo/users/types.gno", ok, "type "}, - {"/r/demo/deep/very/deep", ok, "it works!"}, - {"/r/demo/deep/very/deep:bob", ok, "hi bob"}, - {"/r/demo/deep/very/deep?help", ok, "exposed"}, - {"/r/demo/deep/very/deep/", ok, "render.gno"}, - {"/r/demo/deep/very/deep/render.gno", ok, "func Render("}, - } - if wd, err := os.Getwd(); err == nil { - if strings.HasSuffix(wd, "cmd/gnoweb") { - os.Chdir("../..") - } - } else { - panic("os.Getwd() -> err: " + err.Error()) - } - app := makeApp() - - for _, r := range routes { - t.Run(fmt.Sprintf("test route %s", r.route), func(t *testing.T) { - request := httptest.NewRequest(http.MethodGet, r.route, nil) - response := httptest.NewRecorder() - app.Router.ServeHTTP(response, request) - assert.Equal(t, r.status, response.Code) - assert.Equal(t, strings.Contains(response.Body.String(), r.substring), true) - println(response.Body.String()) - }) +func TestFlagHelp(t *testing.T) { + err := runMain([]string{"-h"}) + if !errors.Is(err, flag.ErrHelp) { + t.Errorf("should display usage") } } diff --git a/gno.land/cmd/gnoweb/pages/GOR.md b/gno.land/cmd/gnoweb/pages/GOR.md deleted file mode 100644 index 95a155319fb..00000000000 --- a/gno.land/cmd/gnoweb/pages/GOR.md +++ /dev/null @@ -1,30 +0,0 @@ -# Game of Realms - -The first high-stakes contest will see participants compete for the tiered membership to co-own the Gno.land blockchain. -A series of complex technical and non-technical tasks will challenge contributors to create innovative patterns that push the chain to new limits. -Start building the foundation for tomorrow through key smart contracts and other contributions that change our understanding of the world. - -You can start participating in the co-creation of the Game of Realms now by adding your contributions on the dedicated [GitHub page](https://github.com/gnolang/gno/issues/357) to help us formulate the challenges. We want to release the final challenges from your contributions. - -## Register Now - - -
-
- -
-
- - -
- -
- - -
- - -
-
-
- diff --git a/gno.land/cmd/gnoweb/pages/HOME.md b/gno.land/cmd/gnoweb/pages/HOME.md deleted file mode 100644 index 0f3bfaec685..00000000000 --- a/gno.land/cmd/gnoweb/pages/HOME.md +++ /dev/null @@ -1,50 +0,0 @@ -# Welcome to **Gno.land** - -- [About Gno.land](/about) -- [Blogs](/r/gnoland/blog) -- [Install `gnokey`](https://github.com/gnolang/gno/tree/master/gno.land/cmd/gnokey) -- [Acquire testnet tokens](/faucet) -- [Game of Realms](/game-of-realms) - An open worldwide competition for developers to build the best Gnolang smart-contracts. - -# Explore new packages. - -- r/gnoland - - [/r/gnoland/blog](/r/gnoland/blog) - - [/r/gnoland/faucet](/r/gnoland/faucet) -- r/system - - [/r/system/names](/r/system/names) - - [/r/system/rewards](/r/system/rewards) - - [/r/system/validators](/r/system/validators) -- r/demo - - [/r/demo/banktest](/r/demo/banktest) - - [/r/demo/boards](/r/demo/boards) - - [/r/demo/foo20](/r/demo/foo20) - - [/r/demo/nft](/r/demo/nft) - - [/r/demo/types](/r/demo/types) - - [/r/demo/users](/r/demo/users) - - [/r/demo/groups](/r/demo/groups) -- p/demo - - [/p/demo/avl](/p/demo/avl) - - [/p/demo/blog](/p/demo/blog) - - [/p/demo/flow](/p/demo/flow) - - [/p/demo/gnode](/p/demo/gnode) - - [/p/demo/grc/exts](/p/demo/grc/exts) - - [/p/demo/grc/grc20](/p/demo/grc/grc20) - - [/p/demo/grc/grc721](/p/demo/grc/grc721) - -# Other Testnets - -- **[staging.gno.land](https://staging.gno.land) (wiped every commit to master)** -- _[test3.gno.land](https://test3.gno.land) (latest)_ -- _[test2.gno.land](https://test2.gno.land) (archive)_ -- _[test1.gno.land](https://test1.gno.land) (archive)_ - -**This is a testnet.** -Package names are not guaranteed to be available for production. - -# Social - -Check out our [community projects](https://github.com/gnolang/awesome-gno). - -Official channel: [Discord](https://discord.gg/S8nKUqwkPn)
-Other channels: [Telegram](https://t.me/gnoland) [Twitter](https://twitter.com/_gnoland) [Youtube](https://www.youtube.com/@_gnoland) diff --git a/gno.land/cmd/gnoweb/views/home.html b/gno.land/cmd/gnoweb/views/home.html deleted file mode 100644 index a2bf78adb96..00000000000 --- a/gno.land/cmd/gnoweb/views/home.html +++ /dev/null @@ -1,21 +0,0 @@ -{{- define "app" -}} - - - - Gno.land - {{ .Data.Title }} - - {{ template "html_head" }} - - -
- -
-
{{ .Data.HomeContent }}
-
-
{{ template "subscribe" }}
- {{ template "footer" }} -
- {{ template "js" }} - - -{{- end -}} diff --git a/gno.land/pkg/gnoclient/README.md b/gno.land/pkg/gnoclient/README.md new file mode 100644 index 00000000000..390559fb142 --- /dev/null +++ b/gno.land/pkg/gnoclient/README.md @@ -0,0 +1,22 @@ +# Gno.land Go Client + +The Gno.land Go client is a dedicated library for interacting seamlessly with the Gno.land RPC API. +This library simplifies the process of querying or sending transactions to the Gno.land RPC API and interpreting the responses. + +## Installation + +Integrate this library into your Go project with the following command: + + go get github.com/gnolang/gno/gno.land/pkg/gnoclient + +## Development Plan + +The roadmap for the Gno.land Go client includes: + +- **Initial Development:** Kickstart the development specifically for Gno.land. Subsequently, transition the generic functionalities to other modules like `tm2`, `gnovm`, `gnosdk`. +- **Integration:** Begin incorporating this library within various components such as `gno.land/cmd/*` and other external clients, including `gnoblog-client`, the Discord community faucet bot, and [GnoMobile](https://github.com/gnolang/gnomobile). +- **Enhancements:** Once the generic client establishes a robust foundation, we aim to utilize code generation for contracts. This will streamline the creation of type-safe, contract-specific clients. + +## Usage + +TODO: Documentation for usage is currently in development and will be available soon. diff --git a/gno.land/pkg/gnoclient/client.go b/gno.land/pkg/gnoclient/client.go new file mode 100644 index 00000000000..0a6918999a6 --- /dev/null +++ b/gno.land/pkg/gnoclient/client.go @@ -0,0 +1,27 @@ +package gnoclient + +import ( + rpcclient "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" +) + +// Client provides an interface for interacting with the blockchain. +type Client struct { + Signer Signer // Signer for transaction authentication + RPCClient rpcclient.Client // RPC client for blockchain communication +} + +// validateSigner checks that the signer is correctly configured. +func (c Client) validateSigner() error { + if c.Signer == nil { + return ErrMissingSigner + } + return nil +} + +// validateRPCClient checks that the RPCClient is correctly configured. +func (c Client) validateRPCClient() error { + if c.RPCClient == nil { + return ErrMissingRPCClient + } + return nil +} diff --git a/gno.land/pkg/gnoclient/client_queries.go b/gno.land/pkg/gnoclient/client_queries.go new file mode 100644 index 00000000000..ba63c0d543e --- /dev/null +++ b/gno.land/pkg/gnoclient/client_queries.go @@ -0,0 +1,124 @@ +package gnoclient + +import ( + "fmt" + + "github.com/gnolang/gno/tm2/pkg/amino" + rpcclient "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" + ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/errors" + "github.com/gnolang/gno/tm2/pkg/std" +) + +// QueryCfg contains configuration options for performing queries. +type QueryCfg struct { + Path string // Query path + Data []byte // Query data + rpcclient.ABCIQueryOptions // ABCI query options +} + +// Query performs a generic query on the blockchain. +func (c Client) Query(cfg QueryCfg) (*ctypes.ResultABCIQuery, error) { + if err := c.validateRPCClient(); err != nil { + return nil, err + } + qres, err := c.RPCClient.ABCIQueryWithOptions(cfg.Path, cfg.Data, cfg.ABCIQueryOptions) + if err != nil { + return nil, errors.Wrap(err, "query error") + } + + if qres.Response.Error != nil { + return qres, errors.Wrap(qres.Response.Error, "deliver transaction failed: log:%s", qres.Response.Log) + } + + return qres, nil +} + +// QueryAccount retrieves account information for a given address. +func (c Client) QueryAccount(addr crypto.Address) (*std.BaseAccount, *ctypes.ResultABCIQuery, error) { + if err := c.validateRPCClient(); err != nil { + return nil, nil, err + } + + path := fmt.Sprintf("auth/accounts/%s", crypto.AddressToBech32(addr)) + data := []byte{} + + qres, err := c.RPCClient.ABCIQuery(path, data) + if err != nil { + return nil, nil, errors.Wrap(err, "query account") + } + if qres.Response.Data == nil || len(qres.Response.Data) == 0 || string(qres.Response.Data) == "null" { + return nil, nil, std.ErrUnknownAddress("unknown address: " + crypto.AddressToBech32(addr)) + } + + var qret struct{ BaseAccount std.BaseAccount } + err = amino.UnmarshalJSON(qres.Response.Data, &qret) + if err != nil { + return nil, nil, err + } + + return &qret.BaseAccount, qres, nil +} + +func (c Client) QueryAppVersion() (string, *ctypes.ResultABCIQuery, error) { + if err := c.validateRPCClient(); err != nil { + return "", nil, err + } + + path := ".app/version" + data := []byte{} + + qres, err := c.RPCClient.ABCIQuery(path, data) + if err != nil { + return "", nil, errors.Wrap(err, "query app version") + } + + version := string(qres.Response.Value) + return version, qres, nil +} + +// Render calls the Render function for pkgPath with optional args. The pkgPath should +// include the prefix like "gno.land/". This is similar to using a browser URL +// /: where doesn't have the prefix like "gno.land/". +func (c Client) Render(pkgPath string, args string) (string, *ctypes.ResultABCIQuery, error) { + if err := c.validateRPCClient(); err != nil { + return "", nil, err + } + + path := "vm/qrender" + data := []byte(fmt.Sprintf("%s\n%s", pkgPath, args)) + + qres, err := c.RPCClient.ABCIQuery(path, data) + if err != nil { + return "", nil, errors.Wrap(err, "query render") + } + if qres.Response.Error != nil { + return "", nil, errors.Wrap(qres.Response.Error, "Render failed: log:%s", qres.Response.Log) + } + + return string(qres.Response.Data), qres, nil +} + +// QEval evaluates the given expression with the realm code at pkgPath. The pkgPath should +// include the prefix like "gno.land/". The expression is usually a function call like +// "GetBoardIDFromName(\"testboard\")". The return value is a typed expression like +// "(1 gno.land/r/demo/boards.BoardID)\n(true bool)". +func (c Client) QEval(pkgPath string, expression string) (string, *ctypes.ResultABCIQuery, error) { + if err := c.validateRPCClient(); err != nil { + return "", nil, err + } + + path := "vm/qeval" + data := []byte(fmt.Sprintf("%s\n%s", pkgPath, expression)) + + qres, err := c.RPCClient.ABCIQuery(path, data) + if err != nil { + return "", nil, errors.Wrap(err, "query qeval") + } + if qres.Response.Error != nil { + return "", nil, errors.Wrap(qres.Response.Error, "QEval failed: log:%s", qres.Response.Log) + } + + return string(qres.Response.Data), qres, nil +} diff --git a/gno.land/pkg/gnoclient/client_test.go b/gno.land/pkg/gnoclient/client_test.go new file mode 100644 index 00000000000..8bcdd903831 --- /dev/null +++ b/gno.land/pkg/gnoclient/client_test.go @@ -0,0 +1,551 @@ +package gnoclient + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/std" +) + +func TestClient_Render(t *testing.T) { + t.Parallel() + testRealmPath := "gno.land/r/demo/deep/very/deep" + expectedRender := []byte("it works!") + + client := Client{ + Signer: &mockSigner{ + sign: func(cfg SignCfg) (*std.Tx, error) { + return &std.Tx{}, nil + }, + info: func() keys.Info { + return &mockKeysInfo{ + getAddress: func() crypto.Address { + adr, _ := crypto.AddressFromBech32("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + return adr + }, + } + }, + }, + RPCClient: &mockRPCClient{ + abciQuery: func(path string, data []byte) (*ctypes.ResultABCIQuery, error) { + res := &ctypes.ResultABCIQuery{ + Response: abci.ResponseQuery{ + ResponseBase: abci.ResponseBase{ + Data: expectedRender, + }, + }, + } + return res, nil + }, + }, + } + + res, data, err := client.Render(testRealmPath, "") + assert.NoError(t, err) + assert.NotEmpty(t, data.Response.Data) + assert.NotEmpty(t, res) + assert.Equal(t, data.Response.Data, expectedRender) +} + +func TestClient_CallSingle(t *testing.T) { + t.Parallel() + + client := Client{ + Signer: &mockSigner{ + sign: func(cfg SignCfg) (*std.Tx, error) { + return &std.Tx{}, nil + }, + info: func() keys.Info { + return &mockKeysInfo{ + getAddress: func() crypto.Address { + adr, _ := crypto.AddressFromBech32("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + return adr + }, + } + }, + }, + RPCClient: &mockRPCClient{ + broadcastTxCommit: func(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { + res := &ctypes.ResultBroadcastTxCommit{ + DeliverTx: abci.ResponseDeliverTx{ + ResponseBase: abci.ResponseBase{ + Data: []byte("it works!"), + }, + }, + } + return res, nil + }, + }, + } + + cfg := BaseTxCfg{ + GasWanted: 100000, + GasFee: "10000ugnot", + AccountNumber: 1, + SequenceNumber: 1, + Memo: "Test memo", + } + + msg := []MsgCall{ + { + PkgPath: "gno.land/r/demo/deep/very/deep", + FuncName: "Render", + Args: []string{""}, + Send: "100ugnot", + }, + } + + res, err := client.Call(cfg, msg...) + assert.NoError(t, err) + require.NotNil(t, res) + assert.Equal(t, string(res.DeliverTx.Data), "it works!") +} + +func TestClient_CallMultiple(t *testing.T) { + t.Parallel() + + client := Client{ + Signer: &mockSigner{ + sign: func(cfg SignCfg) (*std.Tx, error) { + return &std.Tx{}, nil + }, + info: func() keys.Info { + return &mockKeysInfo{ + getAddress: func() crypto.Address { + adr, _ := crypto.AddressFromBech32("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + return adr + }, + } + }, + }, + RPCClient: &mockRPCClient{ + broadcastTxCommit: func(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { + res := &ctypes.ResultBroadcastTxCommit{ + CheckTx: abci.ResponseCheckTx{ + ResponseBase: abci.ResponseBase{ + Error: nil, + Data: nil, + Events: nil, + Log: "", + Info: "", + }, + }, + } + + return res, nil + }, + }, + } + + cfg := BaseTxCfg{ + GasWanted: 100000, + GasFee: "10000ugnot", + AccountNumber: 1, + SequenceNumber: 1, + Memo: "Test memo", + } + + msg := []MsgCall{ + { + PkgPath: "gno.land/r/demo/deep/very/deep", + FuncName: "Render", + Args: []string{""}, + Send: "100ugnot", + }, + { + PkgPath: "gno.land/r/demo/wugnot", + FuncName: "Deposit", + Args: []string{""}, + Send: "1000ugnot", + }, + { + PkgPath: "gno.land/r/demo/tamagotchi", + FuncName: "Feed", + Args: []string{""}, + Send: "", + }, + } + + res, err := client.Call(cfg, msg...) + assert.NoError(t, err) + assert.NotNil(t, res) +} + +func TestClient_Call_Errors(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + client Client + cfg BaseTxCfg + msgs []MsgCall + expectedError error + }{ + { + name: "Invalid Signer", + client: Client{ + Signer: nil, + RPCClient: &mockRPCClient{}, + }, + cfg: BaseTxCfg{ + GasWanted: 100000, + GasFee: "10000ugnot", + AccountNumber: 1, + SequenceNumber: 1, + Memo: "Test memo", + }, + msgs: []MsgCall{ + { + PkgPath: "random/path", + FuncName: "RandomName", + Send: "", + Args: []string{}, + }, + }, + expectedError: ErrMissingSigner, + }, + { + name: "Invalid RPCClient", + client: Client{ + &mockSigner{}, + nil, + }, + cfg: BaseTxCfg{ + GasWanted: 100000, + GasFee: "10000ugnot", + AccountNumber: 1, + SequenceNumber: 1, + Memo: "Test memo", + }, + msgs: []MsgCall{ + { + PkgPath: "random/path", + FuncName: "RandomName", + Send: "", + Args: []string{}, + }, + }, + expectedError: ErrMissingRPCClient, + }, + { + name: "Invalid Gas Fee", + client: Client{ + Signer: &mockSigner{}, + RPCClient: &mockRPCClient{}, + }, + cfg: BaseTxCfg{ + GasWanted: 100000, + GasFee: "", + AccountNumber: 1, + SequenceNumber: 1, + Memo: "Test memo", + }, + msgs: []MsgCall{ + { + PkgPath: "random/path", + FuncName: "RandomName", + }, + }, + expectedError: ErrInvalidGasFee, + }, + { + name: "Negative Gas Wanted", + client: Client{ + Signer: &mockSigner{}, + RPCClient: &mockRPCClient{}, + }, + cfg: BaseTxCfg{ + GasWanted: -1, + GasFee: "10000ugnot", + AccountNumber: 1, + SequenceNumber: 1, + Memo: "Test memo", + }, + msgs: []MsgCall{ + { + PkgPath: "random/path", + FuncName: "RandomName", + Send: "", + Args: []string{}, + }, + }, + expectedError: ErrInvalidGasWanted, + }, + { + name: "0 Gas Wanted", + client: Client{ + Signer: &mockSigner{}, + RPCClient: &mockRPCClient{}, + }, + cfg: BaseTxCfg{ + GasWanted: 0, + GasFee: "10000ugnot", + AccountNumber: 1, + SequenceNumber: 1, + Memo: "Test memo", + }, + msgs: []MsgCall{ + { + PkgPath: "random/path", + FuncName: "RandomName", + Send: "", + Args: []string{}, + }, + }, + expectedError: ErrInvalidGasWanted, + }, + { + name: "Invalid PkgPath", + client: Client{ + Signer: &mockSigner{}, + RPCClient: &mockRPCClient{}, + }, + cfg: BaseTxCfg{ + GasWanted: 100000, + GasFee: "10000ugnot", + AccountNumber: 1, + SequenceNumber: 1, + Memo: "Test memo", + }, + msgs: []MsgCall{ + { + PkgPath: "", + FuncName: "RandomName", + Send: "", + Args: []string{}, + }, + }, + expectedError: ErrEmptyPkgPath, + }, + { + name: "Invalid FuncName", + client: Client{ + Signer: &mockSigner{}, + RPCClient: &mockRPCClient{}, + }, + cfg: BaseTxCfg{ + GasWanted: 100000, + GasFee: "10000ugnot", + AccountNumber: 1, + SequenceNumber: 1, + Memo: "Test memo", + }, + msgs: []MsgCall{ + { + PkgPath: "random/path", + FuncName: "", + Send: "", + Args: []string{}, + }, + }, + expectedError: ErrEmptyFuncName, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + res, err := tc.client.Call(tc.cfg, tc.msgs...) + assert.Nil(t, res) + assert.ErrorIs(t, err, tc.expectedError) + }) + } +} + +func TestClient_Send_Errors(t *testing.T) { + t.Parallel() + + toAddress, _ := crypto.AddressFromBech32("g14a0y9a64dugh3l7hneshdxr4w0rfkkww9ls35p") + testCases := []struct { + name string + client Client + cfg BaseTxCfg + msgs []MsgSend + expectedError error + }{ + { + name: "Invalid Signer", + client: Client{ + Signer: nil, + RPCClient: &mockRPCClient{}, + }, + cfg: BaseTxCfg{ + GasWanted: 100000, + GasFee: "10000ugnot", + AccountNumber: 1, + SequenceNumber: 1, + Memo: "Test memo", + }, + msgs: []MsgSend{ + { + ToAddress: toAddress, + Send: "1ugnot", + }, + }, + expectedError: ErrMissingSigner, + }, + { + name: "Invalid RPCClient", + client: Client{ + &mockSigner{}, + nil, + }, + cfg: BaseTxCfg{ + GasWanted: 100000, + GasFee: "10000ugnot", + AccountNumber: 1, + SequenceNumber: 1, + Memo: "Test memo", + }, + msgs: []MsgSend{ + { + ToAddress: toAddress, + Send: "1ugnot", + }, + }, + expectedError: ErrMissingRPCClient, + }, + { + name: "Invalid Gas Fee", + client: Client{ + Signer: &mockSigner{}, + RPCClient: &mockRPCClient{}, + }, + cfg: BaseTxCfg{ + GasWanted: 100000, + GasFee: "", + AccountNumber: 1, + SequenceNumber: 1, + Memo: "Test memo", + }, + msgs: []MsgSend{ + { + ToAddress: toAddress, + Send: "1ugnot", + }, + }, + expectedError: ErrInvalidGasFee, + }, + { + name: "Negative Gas Wanted", + client: Client{ + Signer: &mockSigner{}, + RPCClient: &mockRPCClient{}, + }, + cfg: BaseTxCfg{ + GasWanted: -1, + GasFee: "10000ugnot", + AccountNumber: 1, + SequenceNumber: 1, + Memo: "Test memo", + }, + msgs: []MsgSend{ + { + ToAddress: toAddress, + Send: "1ugnot", + }, + }, + expectedError: ErrInvalidGasWanted, + }, + { + name: "0 Gas Wanted", + client: Client{ + Signer: &mockSigner{}, + RPCClient: &mockRPCClient{}, + }, + cfg: BaseTxCfg{ + GasWanted: 0, + GasFee: "10000ugnot", + AccountNumber: 1, + SequenceNumber: 1, + Memo: "Test memo", + }, + msgs: []MsgSend{ + { + ToAddress: toAddress, + Send: "1ugnot", + }, + }, + expectedError: ErrInvalidGasWanted, + }, + { + name: "Invalid To Address", + client: Client{ + Signer: &mockSigner{ + info: func() keys.Info { + return &mockKeysInfo{ + getAddress: func() crypto.Address { + adr, _ := crypto.AddressFromBech32("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + return adr + }, + } + }, + }, + RPCClient: &mockRPCClient{}, + }, + cfg: BaseTxCfg{ + GasWanted: 100000, + GasFee: "10000ugnot", + AccountNumber: 1, + SequenceNumber: 1, + Memo: "Test memo", + }, + msgs: []MsgSend{ + { + ToAddress: crypto.Address{}, + Send: "1ugnot", + }, + }, + expectedError: ErrInvalidToAddress, + }, + { + name: "Invalid Send Coins", + client: Client{ + Signer: &mockSigner{ + info: func() keys.Info { + return &mockKeysInfo{ + getAddress: func() crypto.Address { + adr, _ := crypto.AddressFromBech32("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + return adr + }, + } + }, + }, + RPCClient: &mockRPCClient{}, + }, + cfg: BaseTxCfg{ + GasWanted: 100000, + GasFee: "10000ugnot", + AccountNumber: 1, + SequenceNumber: 1, + Memo: "Test memo", + }, + msgs: []MsgSend{ + { + ToAddress: toAddress, + Send: "-1ugnot", + }, + }, + expectedError: ErrInvalidSendAmount, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + res, err := tc.client.Send(tc.cfg, tc.msgs...) + assert.Nil(t, res) + assert.ErrorIs(t, err, tc.expectedError) + }) + } +} diff --git a/gno.land/pkg/gnoclient/client_txs.go b/gno.land/pkg/gnoclient/client_txs.go new file mode 100644 index 00000000000..39088bc30d5 --- /dev/null +++ b/gno.land/pkg/gnoclient/client_txs.go @@ -0,0 +1,277 @@ +package gnoclient + +import ( + "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/tm2/pkg/amino" + ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/errors" + "github.com/gnolang/gno/tm2/pkg/sdk/bank" + "github.com/gnolang/gno/tm2/pkg/std" +) + +var ( + ErrEmptyPkgPath = errors.New("empty pkg path") + ErrEmptyFuncName = errors.New("empty function name") + ErrInvalidGasWanted = errors.New("invalid gas wanted") + ErrInvalidGasFee = errors.New("invalid gas fee") + ErrMissingSigner = errors.New("missing Signer") + ErrMissingRPCClient = errors.New("missing RPCClient") + ErrInvalidToAddress = errors.New("invalid send to address") + ErrInvalidSendAmount = errors.New("invalid send amount") +) + +type BaseTxCfg struct { + GasFee string // Gas fee + GasWanted int64 // Gas wanted + AccountNumber uint64 // Account number + SequenceNumber uint64 // Sequence number + Memo string // Memo +} + +// MsgCall - syntax sugar for vm.MsgCall +type MsgCall struct { + PkgPath string // Package path + FuncName string // Function name + Args []string // Function arguments + Send string // Send amount +} + +// MsgSend - syntax sugar for bank.MsgSend minus fields in BaseTxCfg +type MsgSend struct { + ToAddress crypto.Address // Send to address + Send string // Send amount +} + +// RunCfg contains configuration options for running a temporary package on the blockchain. +type RunCfg struct { + Package *std.MemPackage + GasFee string // Gas fee + GasWanted int64 // Gas wanted + AccountNumber uint64 // Account number + SequenceNumber uint64 // Sequence number + Memo string // Memo +} + +// Call executes a contract call on the blockchain. +func (c *Client) Call(cfg BaseTxCfg, msgs ...MsgCall) (*ctypes.ResultBroadcastTxCommit, error) { + // Validate required client fields. + if err := c.validateSigner(); err != nil { + return nil, err + } + if err := c.validateRPCClient(); err != nil { + return nil, err + } + + // Validate base transaction config + if err := cfg.validateBaseTxConfig(); err != nil { + return nil, err + } + + // Parse MsgCall slice + vmMsgs := make([]vm.MsgCall, 0, len(msgs)) + for _, msg := range msgs { + // Validate MsgCall fields + if err := msg.validateMsgCall(); err != nil { + return nil, err + } + + // Parse send coins + send, err := std.ParseCoins(msg.Send) + if err != nil { + return nil, err + } + + // Unwrap syntax sugar to vm.MsgCall slice + vmMsgs = append(vmMsgs, vm.MsgCall{ + Caller: c.Signer.Info().GetAddress(), + PkgPath: msg.PkgPath, + Func: msg.FuncName, + Args: msg.Args, + Send: send, + }) + } + + // Cast vm.MsgCall back into std.Msg + stdMsgs := make([]std.Msg, len(vmMsgs)) + for i, msg := range vmMsgs { + stdMsgs[i] = msg + } + + // Parse gas fee + gasFeeCoins, err := std.ParseCoin(cfg.GasFee) + if err != nil { + return nil, err + } + + // Pack transaction + tx := std.Tx{ + Msgs: stdMsgs, + Fee: std.NewFee(cfg.GasWanted, gasFeeCoins), + Signatures: nil, + Memo: cfg.Memo, + } + + return c.signAndBroadcastTxCommit(tx, cfg.AccountNumber, cfg.SequenceNumber) +} + +// Send currency to an account on the blockchain. +func (c *Client) Send(cfg BaseTxCfg, msgs ...MsgSend) (*ctypes.ResultBroadcastTxCommit, error) { + // Validate required client fields. + if err := c.validateSigner(); err != nil { + return nil, err + } + if err := c.validateRPCClient(); err != nil { + return nil, err + } + + // Validate base transaction config + if err := cfg.validateBaseTxConfig(); err != nil { + return nil, err + } + + // Parse MsgSend slice + vmMsgs := make([]bank.MsgSend, 0, len(msgs)) + for _, msg := range msgs { + // Validate MsgSend fields + if err := msg.validateMsgSend(); err != nil { + return nil, err + } + + // Parse send coins + send, err := std.ParseCoins(msg.Send) + if err != nil { + return nil, err + } + + // Unwrap syntax sugar to vm.MsgSend slice + vmMsgs = append(vmMsgs, bank.MsgSend{ + FromAddress: c.Signer.Info().GetAddress(), + ToAddress: msg.ToAddress, + Amount: send, + }) + } + + // Cast vm.MsgSend back into std.Msg + stdMsgs := make([]std.Msg, len(vmMsgs)) + for i, msg := range vmMsgs { + stdMsgs[i] = msg + } + + // Parse gas fee + gasFeeCoins, err := std.ParseCoin(cfg.GasFee) + if err != nil { + return nil, err + } + + // Pack transaction + tx := std.Tx{ + Msgs: stdMsgs, + Fee: std.NewFee(cfg.GasWanted, gasFeeCoins), + Signatures: nil, + Memo: cfg.Memo, + } + + return c.signAndBroadcastTxCommit(tx, cfg.AccountNumber, cfg.SequenceNumber) +} + +// Temporarily load cfg.Package on the blockchain and run main() which can +// call realm functions and use println() to output to the "console". +// This returns bres where string(bres.DeliverTx.Data) is the "console" output. +func (c *Client) Run(cfg RunCfg) (*ctypes.ResultBroadcastTxCommit, error) { + // Validate required client fields. + if err := c.validateSigner(); err != nil { + return nil, errors.Wrap(err, "validate signer") + } + if err := c.validateRPCClient(); err != nil { + return nil, errors.Wrap(err, "validate RPC client") + } + + memPkg := cfg.Package + gasWanted := cfg.GasWanted + gasFee := cfg.GasFee + sequenceNumber := cfg.SequenceNumber + accountNumber := cfg.AccountNumber + memo := cfg.Memo + + // Validate config. + if memPkg.IsEmpty() { + return nil, errors.New("found an empty package " + memPkg.Path) + } + + // Parse gas wanted & fee. + gasFeeCoins, err := std.ParseCoin(gasFee) + if err != nil { + return nil, errors.Wrap(err, "parsing gas fee coin") + } + + caller := c.Signer.Info().GetAddress() + + // precompile and validate syntax + err = gno.PrecompileAndCheckMempkg(memPkg) + if err != nil { + return nil, errors.Wrap(err, "precompile and check") + } + memPkg.Name = "main" + memPkg.Path = "" + + // Construct message & transaction and marshal. + msg := vm.MsgRun{ + Caller: caller, + Package: memPkg, + } + tx := std.Tx{ + Msgs: []std.Msg{msg}, + Fee: std.NewFee(gasWanted, gasFeeCoins), + Signatures: nil, + Memo: memo, + } + + return c.signAndBroadcastTxCommit(tx, accountNumber, sequenceNumber) +} + +// signAndBroadcastTxCommit signs a transaction and broadcasts it, returning the result. +func (c Client) signAndBroadcastTxCommit(tx std.Tx, accountNumber, sequenceNumber uint64) (*ctypes.ResultBroadcastTxCommit, error) { + caller := c.Signer.Info().GetAddress() + + if sequenceNumber == 0 || accountNumber == 0 { + account, _, err := c.QueryAccount(caller) + if err != nil { + return nil, errors.Wrap(err, "query account") + } + accountNumber = account.AccountNumber + sequenceNumber = account.Sequence + } + + signCfg := SignCfg{ + UnsignedTX: tx, + SequenceNumber: sequenceNumber, + AccountNumber: accountNumber, + } + signedTx, err := c.Signer.Sign(signCfg) + if err != nil { + return nil, errors.Wrap(err, "sign") + } + + bz, err := amino.Marshal(signedTx) + if err != nil { + return nil, errors.Wrap(err, "marshaling tx binary bytes") + } + + bres, err := c.RPCClient.BroadcastTxCommit(bz) + if err != nil { + return nil, errors.Wrap(err, "broadcasting bytes") + } + + if bres.CheckTx.IsErr() { + return bres, errors.Wrap(bres.CheckTx.Error, "check transaction failed: log:%s", bres.CheckTx.Log) + } + if bres.DeliverTx.IsErr() { + return bres, errors.Wrap(bres.DeliverTx.Error, "deliver transaction failed: log:%s", bres.DeliverTx.Log) + } + + return bres, nil +} + +// TODO: Add more functionality, examples, and unit tests. diff --git a/gno.land/pkg/gnoclient/example_test.go b/gno.land/pkg/gnoclient/example_test.go new file mode 100644 index 00000000000..0a5589bc776 --- /dev/null +++ b/gno.land/pkg/gnoclient/example_test.go @@ -0,0 +1,64 @@ +package gnoclient_test + +import ( + "fmt" + + "github.com/gnolang/gno/gno.land/pkg/gnoclient" + rpcclient "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" +) + +// Example_withDisk demonstrates how to initialize a gnoclient with a keybase sourced from a directory. +func Example_withDisk() { + kb, _ := keys.NewKeyBaseFromDir("/path/to/dir") + signer := gnoclient.SignerFromKeybase{ + Keybase: kb, + Account: "mykey", + Password: "secure", + } + + remote := "127.0.0.1:26657" + rpcClient := rpcclient.NewHTTP(remote, "/websocket") + + client := gnoclient.Client{ + Signer: signer, + RPCClient: rpcClient, + } + _ = client +} + +// Example_withInMemCrypto demonstrates how to initialize a gnoclient with an in-memory keybase using BIP39 mnemonics. +func Example_withInMemCrypto() { + mnemo := "index brass unknown lecture autumn provide royal shrimp elegant wink now zebra discover swarm act ill you bullet entire outdoor tilt usage gap multiply" + bip39Passphrase := "" + account := uint32(0) + index := uint32(0) + chainID := "dev" + signer, _ := gnoclient.SignerFromBip39(mnemo, chainID, bip39Passphrase, account, index) + + remote := "127.0.0.1:26657" + rpcClient := rpcclient.NewHTTP(remote, "/websocket") + + client := gnoclient.Client{ + Signer: signer, + RPCClient: rpcClient, + } + _ = client + fmt.Println("Hello") + // Output: + // Hello +} + +// Example_readOnly demonstrates how to initialize a read-only gnoclient, which can only query. +func Example_readOnly() { + remote := "127.0.0.1:26657" + rpcClient := rpcclient.NewHTTP(remote, "/websocket") + + client := gnoclient.Client{ + RPCClient: rpcClient, + } + _ = client + fmt.Println("Hello") + // Output: + // Hello +} diff --git a/gno.land/pkg/gnoclient/integration_test.go b/gno.land/pkg/gnoclient/integration_test.go new file mode 100644 index 00000000000..4002e25077b --- /dev/null +++ b/gno.land/pkg/gnoclient/integration_test.go @@ -0,0 +1,232 @@ +package gnoclient + +import ( + "testing" + + "github.com/gnolang/gno/gno.land/pkg/integration" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + rpcclient "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/log" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestClient_Call_Single_Integration(t *testing.T) { + // Set up in-memory node + config, _ := integration.TestingNodeConfig(t, gnoenv.RootDir()) + node, remoteAddr := integration.TestingInMemoryNode(t, log.NewNoopLogger(), config) + defer node.Stop() + + // Init Signer & RPCClient + signer := newInMemorySigner(t, "tendermint_test") + rpcClient := rpcclient.NewHTTP(remoteAddr, "/websocket") + + // Setup Client + client := Client{ + Signer: signer, + RPCClient: rpcClient, + } + + // Make Tx config + baseCfg := BaseTxCfg{ + GasFee: "10000ugnot", + GasWanted: 8000000, + AccountNumber: 0, + SequenceNumber: 0, + Memo: "", + } + + // Make Msg config + msg := MsgCall{ + PkgPath: "gno.land/r/demo/deep/very/deep", + FuncName: "Render", + Args: []string{"test argument"}, + Send: "", + } + + // Execute call + res, err := client.Call(baseCfg, msg) + + expected := "(\"hi test argument\" string)" + got := string(res.DeliverTx.Data) + + assert.Nil(t, err) + assert.Equal(t, expected, got) +} + +func TestClient_Call_Multiple_Integration(t *testing.T) { + // Set up in-memory node + config, _ := integration.TestingNodeConfig(t, gnoenv.RootDir()) + node, remoteAddr := integration.TestingInMemoryNode(t, log.NewNoopLogger(), config) + defer node.Stop() + + // Init Signer & RPCClient + signer := newInMemorySigner(t, "tendermint_test") + rpcClient := rpcclient.NewHTTP(remoteAddr, "/websocket") + + // Setup Client + client := Client{ + Signer: signer, + RPCClient: rpcClient, + } + + // Make Tx config + baseCfg := BaseTxCfg{ + GasFee: "10000ugnot", + GasWanted: 8000000, + AccountNumber: 0, + SequenceNumber: 0, + Memo: "", + } + + // Make Msg configs + msg1 := MsgCall{ + PkgPath: "gno.land/r/demo/deep/very/deep", + FuncName: "Render", + Args: []string{""}, + Send: "", + } + + // Same call, different argument + msg2 := MsgCall{ + PkgPath: "gno.land/r/demo/deep/very/deep", + FuncName: "Render", + Args: []string{"test argument"}, + Send: "", + } + + expected := "(\"it works!\" string)(\"hi test argument\" string)" + + // Execute call + res, err := client.Call(baseCfg, msg1, msg2) + + got := string(res.DeliverTx.Data) + assert.Nil(t, err) + assert.Equal(t, expected, got) +} + +func TestClient_Send_Single_Integration(t *testing.T) { + // Set up in-memory node + config, _ := integration.TestingNodeConfig(t, gnoenv.RootDir()) + node, remoteAddr := integration.TestingInMemoryNode(t, log.NewNoopLogger(), config) + defer node.Stop() + + // Init Signer & RPCClient + signer := newInMemorySigner(t, "tendermint_test") + rpcClient := rpcclient.NewHTTP(remoteAddr, "/websocket") + + // Setup Client + client := Client{ + Signer: signer, + RPCClient: rpcClient, + } + + // Make Tx config + baseCfg := BaseTxCfg{ + GasFee: "10000ugnot", + GasWanted: 8000000, + AccountNumber: 0, + SequenceNumber: 0, + Memo: "", + } + + // Make Send config for a new address on the blockchain + toAddress, _ := crypto.AddressFromBech32("g14a0y9a64dugh3l7hneshdxr4w0rfkkww9ls35p") + amount := 10 + msg := MsgSend{ + ToAddress: toAddress, + Send: std.Coin{"ugnot", int64(amount)}.String(), + } + + // Execute send + res, err := client.Send(baseCfg, msg) + assert.Nil(t, err) + assert.Equal(t, "", string(res.DeliverTx.Data)) + + // Get the new account balance + account, _, err := client.QueryAccount(toAddress) + assert.Nil(t, err) + + expected := std.Coins{{"ugnot", int64(amount)}} + got := account.GetCoins() + + assert.Equal(t, expected, got) +} + +func TestClient_Send_Multiple_Integration(t *testing.T) { + // Set up in-memory node + config, _ := integration.TestingNodeConfig(t, gnoenv.RootDir()) + node, remoteAddr := integration.TestingInMemoryNode(t, log.NewNoopLogger(), config) + defer node.Stop() + + // Init Signer & RPCClient + signer := newInMemorySigner(t, "tendermint_test") + rpcClient := rpcclient.NewHTTP(remoteAddr, "/websocket") + + // Setup Client + client := Client{ + Signer: signer, + RPCClient: rpcClient, + } + + // Make Tx config + baseCfg := BaseTxCfg{ + GasFee: "10000ugnot", + GasWanted: 8000000, + AccountNumber: 0, + SequenceNumber: 0, + Memo: "", + } + + // Make Msg configs + toAddress, _ := crypto.AddressFromBech32("g14a0y9a64dugh3l7hneshdxr4w0rfkkww9ls35p") + amount1 := 10 + msg1 := MsgSend{ + ToAddress: toAddress, + Send: std.Coin{"ugnot", int64(amount1)}.String(), + } + + // Same send, different argument + amount2 := 20 + msg2 := MsgSend{ + ToAddress: toAddress, + Send: std.Coin{"ugnot", int64(amount2)}.String(), + } + + // Execute send + res, err := client.Send(baseCfg, msg1, msg2) + assert.Nil(t, err) + assert.Equal(t, "", string(res.DeliverTx.Data)) + + // Get the new account balance + account, _, err := client.QueryAccount(toAddress) + assert.Nil(t, err) + + expected := std.Coins{{"ugnot", int64(amount1 + amount2)}} + got := account.GetCoins() + + assert.Equal(t, expected, got) +} + +// todo add more integration tests. + +func newInMemorySigner(t *testing.T, chainid string) *SignerFromKeybase { + t.Helper() + + mnemonic := integration.DefaultAccount_Seed + name := integration.DefaultAccount_Name + + kb := keys.NewInMemory() + _, err := kb.CreateAccount(name, mnemonic, "", "", uint32(0), uint32(0)) + require.NoError(t, err) + + return &SignerFromKeybase{ + Keybase: kb, // Stores keys in memory or on disk + Account: name, // Account name or bech32 format + Password: "", // Password for encryption + ChainID: chainid, // Chain ID for transaction signing + } +} diff --git a/gno.land/pkg/gnoclient/mock_test.go b/gno.land/pkg/gnoclient/mock_test.go new file mode 100644 index 00000000000..4a12dfd2d88 --- /dev/null +++ b/gno.land/pkg/gnoclient/mock_test.go @@ -0,0 +1,284 @@ +package gnoclient + +import ( + "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" + ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/crypto/hd" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/std" +) + +// Signer mock +type ( + mockSign func(cfg SignCfg) (*std.Tx, error) + mockInfo func() keys.Info + mockValidate func() error +) + +type mockSigner struct { + sign mockSign + info mockInfo + validate mockValidate +} + +func (m *mockSigner) Sign(cfg SignCfg) (*std.Tx, error) { + if m.sign != nil { + return m.sign(cfg) + } + return nil, nil +} + +func (m *mockSigner) Info() keys.Info { + if m.info != nil { + return m.info() + } + return nil +} + +func (m *mockSigner) Validate() error { + if m.validate != nil { + return m.validate() + } + return nil +} + +// Keys Info mock +type ( + mockGetAddress func() crypto.Address + mockGetType func() keys.KeyType + mockGetName func() string + mockGetPubKey func() crypto.PubKey + mockGetPath func() (*hd.BIP44Params, error) +) + +type mockKeysInfo struct { + getAddress mockGetAddress + getType mockGetType + getName mockGetName + getPubKey mockGetPubKey + getPath mockGetPath +} + +func (m *mockKeysInfo) GetAddress() crypto.Address { + if m.getAddress != nil { + return m.getAddress() + } + return crypto.Address{} +} + +func (m *mockKeysInfo) GetType() keys.KeyType { + if m.getType != nil { + return m.getType() + } + return 0 +} + +func (m *mockKeysInfo) GetName() string { + if m.getName != nil { + return m.getName() + } + return "" +} + +func (m *mockKeysInfo) GetPubKey() crypto.PubKey { + if m.getPubKey != nil { + return m.getPubKey() + } + return nil +} + +func (m *mockKeysInfo) GetPath() (*hd.BIP44Params, error) { + if m.getPath != nil { + return m.getPath() + } + return nil, nil +} + +// RPC Client mock +type ( + mockBroadcastTxCommit func(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) + mockABCIQuery func(path string, data []byte) (*ctypes.ResultABCIQuery, error) + mockABCIInfo func() (*ctypes.ResultABCIInfo, error) + mockABCIQueryWithOptions func(path string, data []byte, opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) + mockBroadcastTxAsync func(tx types.Tx) (*ctypes.ResultBroadcastTx, error) + mockBroadcastTxSync func(tx types.Tx) (*ctypes.ResultBroadcastTx, error) + mockGenesis func() (*ctypes.ResultGenesis, error) + mockBlockchainInfo func(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) + mockNetInfo func() (*ctypes.ResultNetInfo, error) + mockDumpConsensusState func() (*ctypes.ResultDumpConsensusState, error) + mockConsensusState func() (*ctypes.ResultConsensusState, error) + mockConsensusParams func(height *int64) (*ctypes.ResultConsensusParams, error) + mockHealth func() (*ctypes.ResultHealth, error) + mockBlock func(height *int64) (*ctypes.ResultBlock, error) + mockBlockResults func(height *int64) (*ctypes.ResultBlockResults, error) + mockCommit func(height *int64) (*ctypes.ResultCommit, error) + mockValidators func(height *int64) (*ctypes.ResultValidators, error) + mockStatus func() (*ctypes.ResultStatus, error) + mockUnconfirmedTxs func(limit int) (*ctypes.ResultUnconfirmedTxs, error) + mockNumUnconfirmedTxs func() (*ctypes.ResultUnconfirmedTxs, error) +) + +type mockRPCClient struct { + broadcastTxCommit mockBroadcastTxCommit + abciQuery mockABCIQuery + abciInfo mockABCIInfo + abciQueryWithOptions mockABCIQueryWithOptions + broadcastTxAsync mockBroadcastTxAsync + broadcastTxSync mockBroadcastTxSync + genesis mockGenesis + blockchainInfo mockBlockchainInfo + netInfo mockNetInfo + dumpConsensusState mockDumpConsensusState + consensusState mockConsensusState + consensusParams mockConsensusParams + health mockHealth + block mockBlock + blockResults mockBlockResults + commit mockCommit + validators mockValidators + status mockStatus + unconfirmedTxs mockUnconfirmedTxs + numUnconfirmedTxs mockNumUnconfirmedTxs +} + +func (m *mockRPCClient) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { + if m.broadcastTxCommit != nil { + return m.broadcastTxCommit(tx) + } + return nil, nil +} + +func (m *mockRPCClient) ABCIQuery(path string, data []byte) (*ctypes.ResultABCIQuery, error) { + if m.abciQuery != nil { + return m.abciQuery(path, data) + } + return nil, nil +} + +func (m *mockRPCClient) ABCIInfo() (*ctypes.ResultABCIInfo, error) { + if m.abciInfo != nil { + return m.ABCIInfo() + } + return nil, nil +} + +func (m *mockRPCClient) ABCIQueryWithOptions(path string, data []byte, opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { + if m.abciQueryWithOptions != nil { + return m.abciQueryWithOptions(path, data, opts) + } + return nil, nil +} + +func (m *mockRPCClient) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { + if m.broadcastTxAsync != nil { + return m.broadcastTxAsync(tx) + } + return nil, nil +} + +func (m *mockRPCClient) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { + if m.broadcastTxSync != nil { + return m.broadcastTxSync(tx) + } + return nil, nil +} + +func (m *mockRPCClient) Genesis() (*ctypes.ResultGenesis, error) { + if m.genesis != nil { + return m.genesis() + } + return nil, nil +} + +func (m *mockRPCClient) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { + if m.blockchainInfo != nil { + return m.blockchainInfo(minHeight, maxHeight) + } + return nil, nil +} + +func (m *mockRPCClient) NetInfo() (*ctypes.ResultNetInfo, error) { + if m.netInfo != nil { + return m.netInfo() + } + return nil, nil +} + +func (m *mockRPCClient) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { + if m.dumpConsensusState != nil { + return m.dumpConsensusState() + } + return nil, nil +} + +func (m *mockRPCClient) ConsensusState() (*ctypes.ResultConsensusState, error) { + if m.consensusState != nil { + return m.consensusState() + } + return nil, nil +} + +func (m *mockRPCClient) ConsensusParams(height *int64) (*ctypes.ResultConsensusParams, error) { + if m.consensusParams != nil { + return m.consensusParams(height) + } + return nil, nil +} + +func (m *mockRPCClient) Health() (*ctypes.ResultHealth, error) { + if m.health != nil { + return m.health() + } + return nil, nil +} + +func (m *mockRPCClient) Block(height *int64) (*ctypes.ResultBlock, error) { + if m.block != nil { + return m.block(height) + } + return nil, nil +} + +func (m *mockRPCClient) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) { + if m.blockResults != nil { + return m.blockResults(height) + } + return nil, nil +} + +func (m *mockRPCClient) Commit(height *int64) (*ctypes.ResultCommit, error) { + if m.commit != nil { + return m.commit(height) + } + return nil, nil +} + +func (m *mockRPCClient) Validators(height *int64) (*ctypes.ResultValidators, error) { + if m.validators != nil { + return m.validators(height) + } + return nil, nil +} + +func (m *mockRPCClient) Status() (*ctypes.ResultStatus, error) { + if m.status != nil { + return m.status() + } + return nil, nil +} + +func (m *mockRPCClient) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) { + if m.unconfirmedTxs != nil { + return m.unconfirmedTxs(limit) + } + return nil, nil +} + +func (m *mockRPCClient) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) { + if m.numUnconfirmedTxs != nil { + return m.numUnconfirmedTxs() + } + return nil, nil +} diff --git a/gno.land/pkg/gnoclient/signer.go b/gno.land/pkg/gnoclient/signer.go new file mode 100644 index 00000000000..ba2aea4d287 --- /dev/null +++ b/gno.land/pkg/gnoclient/signer.go @@ -0,0 +1,145 @@ +package gnoclient + +import ( + "fmt" + + "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/errors" + "github.com/gnolang/gno/tm2/pkg/std" +) + +// Signer provides an interface for signing transactions. +type Signer interface { + Sign(SignCfg) (*std.Tx, error) // Signs a transaction and returns a signed tx ready for broadcasting. + Info() keys.Info // Returns key information, including the address. + Validate() error // Checks whether the signer is properly configured. +} + +// SignerFromKeybase represents a signer created from a Keybase. +type SignerFromKeybase struct { + Keybase keys.Keybase // Stores keys in memory or on disk + Account string // Account name or bech32 format + Password string // Password for encryption + ChainID string // Chain ID for transaction signing +} + +func (s SignerFromKeybase) Validate() error { + if s.ChainID == "" { + return errors.New("missing ChainID") + } + + _, err := s.Keybase.GetByNameOrAddress(s.Account) + if err != nil { + return err + } + + // To verify if the password unlocks the account, sign a blank transaction. + msg := vm.MsgCall{ + Caller: s.Info().GetAddress(), + } + signCfg := SignCfg{ + UnsignedTX: std.Tx{ + Msgs: []std.Msg{msg}, + Fee: std.NewFee(0, std.NewCoin("ugnot", 1000000)), + }, + } + if _, err = s.Sign(signCfg); err != nil { + return err + } + + return nil +} + +func (s SignerFromKeybase) Info() keys.Info { + info, err := s.Keybase.GetByNameOrAddress(s.Account) + if err != nil { + panic("should not happen") + } + return info +} + +// Sign implements the Signer interface for SignerFromKeybase. +type SignCfg struct { + UnsignedTX std.Tx + SequenceNumber uint64 + AccountNumber uint64 +} + +func (s SignerFromKeybase) Sign(cfg SignCfg) (*std.Tx, error) { + tx := cfg.UnsignedTX + chainID := s.ChainID + accountNumber := cfg.AccountNumber + sequenceNumber := cfg.SequenceNumber + account := s.Account + password := s.Password + + // Initialize tx signatures. + signers := tx.GetSigners() + if tx.Signatures == nil { + for range signers { + tx.Signatures = append(tx.Signatures, std.Signature{ + PubKey: nil, // Zero signature + Signature: nil, // Zero signature + }) + } + } + + // Validate the transaction to sign. + err := tx.ValidateBasic() + if err != nil { + return nil, err + } + + // Derive sign doc bytes. + signbz := tx.GetSignBytes(chainID, accountNumber, sequenceNumber) + + sig, pub, err := s.Keybase.Sign(account, password, signbz) + if err != nil { + return nil, err + } + addr := pub.Address() + found := false + for i := range tx.Signatures { + if signers[i] == addr { + found = true + tx.Signatures[i] = std.Signature{ + PubKey: pub, + Signature: sig, + } + } + } + + if !found { + return nil, fmt.Errorf("address %v (%s) not in signer set", addr, account) + } + + return &tx, nil +} + +// Ensure SignerFromKeybase implements the Signer interface. +var _ Signer = (*SignerFromKeybase)(nil) + +// SignerFromBip39 creates an in-memory keybase with a single default account. +// This can be useful in scenarios where storing private keys in the filesystem isn't feasible. +// +// Warning: Using keys.NewKeyBaseFromDir is recommended where possible, as it is more secure. +func SignerFromBip39(mnemonic string, chainID string, passphrase string, account uint32, index uint32) (Signer, error) { + kb := keys.NewInMemory() + name := "default" + password := "" // Password isn't needed for in-memory storage + + _, err := kb.CreateAccount(name, mnemonic, passphrase, password, account, index) + if err != nil { + return nil, err + } + + signer := SignerFromKeybase{ + Keybase: kb, + Account: name, + Password: password, + ChainID: chainID, + } + + return &signer, nil +} diff --git a/gno.land/pkg/gnoclient/signer_test.go b/gno.land/pkg/gnoclient/signer_test.go new file mode 100644 index 00000000000..3b4cbb757ad --- /dev/null +++ b/gno.land/pkg/gnoclient/signer_test.go @@ -0,0 +1 @@ +package gnoclient diff --git a/gno.land/pkg/gnoclient/util.go b/gno.land/pkg/gnoclient/util.go new file mode 100644 index 00000000000..398bbca08fc --- /dev/null +++ b/gno.land/pkg/gnoclient/util.go @@ -0,0 +1,35 @@ +package gnoclient + +import "github.com/gnolang/gno/tm2/pkg/std" + +func (cfg BaseTxCfg) validateBaseTxConfig() error { + if cfg.GasWanted <= 0 { + return ErrInvalidGasWanted + } + if cfg.GasFee == "" { + return ErrInvalidGasFee + } + + return nil +} + +func (msg MsgCall) validateMsgCall() error { + if msg.PkgPath == "" { + return ErrEmptyPkgPath + } + if msg.FuncName == "" { + return ErrEmptyFuncName + } + return nil +} + +func (msg MsgSend) validateMsgSend() error { + if msg.ToAddress.IsZero() { + return ErrInvalidToAddress + } + _, err := std.ParseCoins(msg.Send) + if err != nil { + return ErrInvalidSendAmount + } + return nil +} diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index b10f251b115..0b0488fc98e 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -3,12 +3,13 @@ package gnoland import ( "fmt" "path/filepath" - "strings" + + "golang.org/x/exp/slog" "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" "github.com/gnolang/gno/tm2/pkg/amino" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" - "github.com/gnolang/gno/tm2/pkg/crypto" dbm "github.com/gnolang/gno/tm2/pkg/db" "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/sdk" @@ -20,12 +21,40 @@ import ( "github.com/gnolang/gno/tm2/pkg/store/iavl" ) +type AppOptions struct { + DB dbm.DB + // `gnoRootDir` should point to the local location of the gno repository. + // It serves as the gno equivalent of GOROOT. + GnoRootDir string + SkipFailingGenesisTxs bool + Logger *slog.Logger + MaxCycles int64 +} + +func NewAppOptions() *AppOptions { + return &AppOptions{ + Logger: log.NewNoopLogger(), + DB: dbm.NewMemDB(), + GnoRootDir: gnoenv.RootDir(), + } +} + +func (c *AppOptions) validate() error { + if c.Logger == nil { + return fmt.Errorf("no logger provided") + } + + if c.DB == nil { + return fmt.Errorf("no db provided") + } + + return nil +} + // NewApp creates the GnoLand application. -func NewApp(rootDir string, skipFailingGenesisTxs bool, logger log.Logger, maxCycles int64) (abci.Application, error) { - // Get main DB. - db, err := dbm.NewDB("gnolang", dbm.GoLevelDBBackend, filepath.Join(rootDir, "data")) - if err != nil { - return nil, fmt.Errorf("error initializing database %q using path %q: %w", dbm.GoLevelDBBackend, rootDir, err) +func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { + if err := cfg.validate(); err != nil { + return nil, err } // Capabilities keys. @@ -33,21 +62,23 @@ func NewApp(rootDir string, skipFailingGenesisTxs bool, logger log.Logger, maxCy baseKey := store.NewStoreKey("base") // Create BaseApp. - baseApp := sdk.NewBaseApp("gnoland", logger, db, baseKey, mainKey) + baseApp := sdk.NewBaseApp("gnoland", cfg.Logger, cfg.DB, baseKey, mainKey) baseApp.SetAppVersion("dev") // Set mounts for BaseApp's MultiStore. - baseApp.MountStoreWithDB(mainKey, iavl.StoreConstructor, db) - baseApp.MountStoreWithDB(baseKey, dbadapter.StoreConstructor, db) + baseApp.MountStoreWithDB(mainKey, iavl.StoreConstructor, cfg.DB) + baseApp.MountStoreWithDB(baseKey, dbadapter.StoreConstructor, cfg.DB) // Construct keepers. acctKpr := auth.NewAccountKeeper(mainKey, ProtoGnoAccount) bankKpr := bank.NewBankKeeper(acctKpr) - stdlibsDir := filepath.Join("..", "gnovm", "stdlibs") - vmKpr := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, maxCycles) + + // XXX: Embed this ? + stdlibsDir := filepath.Join(cfg.GnoRootDir, "gnovm", "stdlibs") + vmKpr := vm.NewVMKeeper(baseKey, mainKey, acctKpr, bankKpr, stdlibsDir, cfg.MaxCycles) // Set InitChainer - baseApp.SetInitChainer(InitChainer(baseApp, acctKpr, bankKpr, skipFailingGenesisTxs)) + baseApp.SetInitChainer(InitChainer(baseApp, acctKpr, bankKpr, cfg.SkipFailingGenesisTxs)) // Set AnteHandler authOptions := auth.AnteOptions{ @@ -88,6 +119,24 @@ func NewApp(rootDir string, skipFailingGenesisTxs bool, logger log.Logger, maxCy return baseApp, nil } +// NewApp creates the GnoLand application. +func NewApp(dataRootDir string, skipFailingGenesisTxs bool, logger *slog.Logger, maxCycles int64) (abci.Application, error) { + var err error + + cfg := NewAppOptions() + cfg.SkipFailingGenesisTxs = skipFailingGenesisTxs + + // Get main DB. + cfg.DB, err = dbm.NewDB("gnolang", dbm.GoLevelDBBackend, filepath.Join(dataRootDir, "data")) + if err != nil { + return nil, fmt.Errorf("error initializing database %q using path %q: %w", dbm.GoLevelDBBackend, dataRootDir, err) + } + + cfg.Logger = logger + + return NewAppWithOptions(cfg) +} + // InitChainer returns a function that can initialize the chain with genesis. func InitChainer(baseApp *sdk.BaseApp, acctKpr auth.AccountKeeperI, bankKpr bank.BankKeeperI, skipFailingGenesisTxs bool) func(sdk.Context, abci.RequestInitChain) abci.ResponseInitChain { return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { @@ -95,10 +144,9 @@ func InitChainer(baseApp *sdk.BaseApp, acctKpr auth.AccountKeeperI, bankKpr bank genState := req.AppState.(GnoGenesisState) // Parse and set genesis state balances. for _, bal := range genState.Balances { - addr, coins := parseBalance(bal) - acc := acctKpr.NewAccountWithAddress(ctx, addr) + acc := acctKpr.NewAccountWithAddress(ctx, bal.Address) acctKpr.SetAccount(ctx, acc) - err := bankKpr.SetCoins(ctx, addr, coins) + err := bankKpr.SetCoins(ctx, bal.Address, bal.Amount) if err != nil { panic(err) } @@ -107,14 +155,15 @@ func InitChainer(baseApp *sdk.BaseApp, acctKpr auth.AccountKeeperI, bankKpr bank for i, tx := range genState.Txs { res := baseApp.Deliver(tx) if res.IsErr() { - fmt.Println("ERROR LOG:", res.Log) - fmt.Println("#", i, string(amino.MustMarshalJSON(tx))) + ctx.Logger().Error("LOG", "log", res.Log) + ctx.Logger().Error(fmt.Sprintf("#%d", i), "value", string(amino.MustMarshalJSON(tx))) + // NOTE: comment out to ignore. if !skipFailingGenesisTxs { - panic(res.Error) + panic(res.Log) } } else { - fmt.Println("SUCCESS:", string(amino.MustMarshalJSON(tx))) + ctx.Logger().Info("SUCCESS:", "value", string(amino.MustMarshalJSON(tx))) } } // Done! @@ -124,22 +173,6 @@ func InitChainer(baseApp *sdk.BaseApp, acctKpr auth.AccountKeeperI, bankKpr bank } } -func parseBalance(bal string) (crypto.Address, std.Coins) { - parts := strings.Split(bal, "=") - if len(parts) != 2 { - panic(fmt.Sprintf("invalid balance string %s", bal)) - } - addr, err := crypto.AddressFromBech32(parts[0]) - if err != nil { - panic(fmt.Sprintf("invalid balance addr %s (%v)", bal, err)) - } - coins, err := std.ParseCoins(parts[1]) - if err != nil { - panic(fmt.Sprintf("invalid balance coins %s (%v)", bal, err)) - } - return addr, coins -} - // XXX not used yet. func EndBlocker(vmk vm.VMKeeperI) func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { return func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { diff --git a/gno.land/pkg/gnoland/genesis.go b/gno.land/pkg/gnoland/genesis.go new file mode 100644 index 00000000000..8f2fa0debf6 --- /dev/null +++ b/gno.land/pkg/gnoland/genesis.go @@ -0,0 +1,129 @@ +package gnoland + +import ( + "errors" + "fmt" + "strings" + + vmm "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/pkg/gnomod" + "github.com/gnolang/gno/tm2/pkg/amino" + bft "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto" + osm "github.com/gnolang/gno/tm2/pkg/os" + "github.com/gnolang/gno/tm2/pkg/std" +) + +// LoadGenesisBalancesFile loads genesis balances from the provided file path. +func LoadGenesisBalancesFile(path string) ([]Balance, error) { + // each balance is in the form: g1xxxxxxxxxxxxxxxx=100000ugnot + content := osm.MustReadFile(path) + lines := strings.Split(string(content), "\n") + + balances := make([]Balance, 0, len(lines)) + for _, line := range lines { + line = strings.TrimSpace(line) + + // remove comments. + line = strings.Split(line, "#")[0] + line = strings.TrimSpace(line) + + // skip empty lines. + if line == "" { + continue + } + + parts := strings.Split(line, "=") //
= + if len(parts) != 2 { + return nil, errors.New("invalid genesis_balance line: " + line) + } + + addr, err := crypto.AddressFromBech32(parts[0]) + if err != nil { + return nil, fmt.Errorf("invalid balance addr %s: %w", parts[0], err) + } + + coins, err := std.ParseCoins(parts[1]) + if err != nil { + return nil, fmt.Errorf("invalid balance coins %s: %w", parts[1], err) + } + + balances = append(balances, Balance{ + Address: addr, + Amount: coins, + }) + } + + return balances, nil +} + +// LoadGenesisTxsFile loads genesis transactions from the provided file path. +// XXX: Improve the way we generate and load this file +func LoadGenesisTxsFile(path string, chainID string, genesisRemote string) ([]std.Tx, error) { + txs := []std.Tx{} + txsBz := osm.MustReadFile(path) + txsLines := strings.Split(string(txsBz), "\n") + for _, txLine := range txsLines { + if txLine == "" { + continue // Skip empty line. + } + + // Patch the TX. + txLine = strings.ReplaceAll(txLine, "%%CHAINID%%", chainID) + txLine = strings.ReplaceAll(txLine, "%%REMOTE%%", genesisRemote) + + var tx std.Tx + if err := amino.UnmarshalJSON([]byte(txLine), &tx); err != nil { + return nil, fmt.Errorf("unable to Unmarshall txs file: %w", err) + } + + txs = append(txs, tx) + } + + return txs, nil +} + +// LoadPackagesFromDir loads gno packages from a directory. +// It creates and returns a list of transactions based on these packages. +func LoadPackagesFromDir(dir string, creator bft.Address, fee std.Fee, deposit std.Coins) ([]std.Tx, error) { + // list all packages from target path + pkgs, err := gnomod.ListPkgs(dir) + if err != nil { + return nil, fmt.Errorf("listing gno packages: %w", err) + } + + // Sort packages by dependencies. + sortedPkgs, err := pkgs.Sort() + if err != nil { + return nil, fmt.Errorf("sorting packages: %w", err) + } + + // Filter out draft packages. + nonDraftPkgs := sortedPkgs.GetNonDraftPkgs() + txs := []std.Tx{} + for _, pkg := range nonDraftPkgs { + // Open files in directory as MemPackage. + memPkg := gno.ReadMemPackage(pkg.Dir, pkg.Name) + if err := memPkg.Validate(); err != nil { + return nil, fmt.Errorf("invalid package: %w", err) + } + + // Create transaction + tx := std.Tx{ + Fee: fee, + Msgs: []std.Msg{ + vmm.MsgAddPackage{ + Creator: creator, + Package: memPkg, + Deposit: deposit, + }, + }, + } + + tx.Signatures = make([]std.Signature, len(tx.GetSigners())) + txs = append(txs, tx) + } + + return txs, nil +} diff --git a/gno.land/pkg/gnoland/node_inmemory.go b/gno.land/pkg/gnoland/node_inmemory.go new file mode 100644 index 00000000000..2db8544a909 --- /dev/null +++ b/gno.land/pkg/gnoland/node_inmemory.go @@ -0,0 +1,178 @@ +package gnoland + +import ( + "fmt" + "sync" + "time" + + "golang.org/x/exp/slog" + + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + tmcfg "github.com/gnolang/gno/tm2/pkg/bft/config" + "github.com/gnolang/gno/tm2/pkg/bft/node" + "github.com/gnolang/gno/tm2/pkg/bft/proxy" + bft "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" + "github.com/gnolang/gno/tm2/pkg/db" + "github.com/gnolang/gno/tm2/pkg/events" + "github.com/gnolang/gno/tm2/pkg/p2p" + "github.com/gnolang/gno/tm2/pkg/std" +) + +type InMemoryNodeConfig struct { + PrivValidator bft.PrivValidator // identity of the validator + Genesis *bft.GenesisDoc + TMConfig *tmcfg.Config + SkipFailingGenesisTxs bool + GenesisMaxVMCycles int64 +} + +// NewMockedPrivValidator generate a new key +func NewMockedPrivValidator() bft.PrivValidator { + return bft.NewMockPVWithParams(ed25519.GenPrivKey(), false, false) +} + +// NewInMemoryNodeConfig creates a default configuration for an in-memory node. +func NewDefaultGenesisConfig(pk crypto.PubKey, chainid string) *bft.GenesisDoc { + return &bft.GenesisDoc{ + GenesisTime: time.Now(), + ChainID: chainid, + ConsensusParams: abci.ConsensusParams{ + Block: &abci.BlockParams{ + MaxTxBytes: 1_000_000, // 1MB, + MaxDataBytes: 2_000_000, // 2MB, + MaxGas: 10_0000_000, // 10M gas + TimeIotaMS: 100, // 100ms + }, + }, + AppState: &GnoGenesisState{ + Balances: []Balance{}, + Txs: []std.Tx{}, + }, + } +} + +func NewDefaultTMConfig(rootdir string) *tmcfg.Config { + // We use `TestConfig` here otherwise ChainID will be empty, and + // there is no other way to update it than using a config file + return tmcfg.TestConfig().SetRootDir(rootdir) +} + +// NewInMemoryNodeConfig creates a default configuration for an in-memory node. +func NewDefaultInMemoryNodeConfig(rootdir string) *InMemoryNodeConfig { + tm := NewDefaultTMConfig(rootdir) + + // Create Mocked Identity + pv := NewMockedPrivValidator() + genesis := NewDefaultGenesisConfig(pv.GetPubKey(), tm.ChainID()) + + // Add self as validator + self := pv.GetPubKey() + genesis.Validators = []bft.GenesisValidator{ + { + Address: self.Address(), + PubKey: self, + Power: 10, + Name: "self", + }, + } + + return &InMemoryNodeConfig{ + PrivValidator: pv, + TMConfig: tm, + Genesis: genesis, + GenesisMaxVMCycles: 10_000_000, + } +} + +func (cfg *InMemoryNodeConfig) validate() error { + if cfg.PrivValidator == nil { + return fmt.Errorf("`PrivValidator` is required but not provided") + } + + if cfg.TMConfig == nil { + return fmt.Errorf("`TMConfig` is required but not provided") + } + + if cfg.TMConfig.RootDir == "" { + return fmt.Errorf("`TMConfig.RootDir` is required to locate `stdlibs` directory") + } + + return nil +} + +// NewInMemoryNode creates an in-memory gnoland node. In this mode, the node does not +// persist any data and uses an in-memory database. The `InMemoryNodeConfig.TMConfig.RootDir` +// should point to the correct gno repository to load the stdlibs. +func NewInMemoryNode(logger *slog.Logger, cfg *InMemoryNodeConfig) (*node.Node, error) { + if err := cfg.validate(); err != nil { + return nil, fmt.Errorf("validate config error: %w", err) + } + + // Initialize the application with the provided options + gnoApp, err := NewAppWithOptions(&AppOptions{ + Logger: logger, + GnoRootDir: cfg.TMConfig.RootDir, + SkipFailingGenesisTxs: cfg.SkipFailingGenesisTxs, + MaxCycles: cfg.GenesisMaxVMCycles, + DB: db.NewMemDB(), + }) + if err != nil { + return nil, fmt.Errorf("error initializing new app: %w", err) + } + + cfg.TMConfig.LocalApp = gnoApp + + // Setup app client creator + appClientCreator := proxy.DefaultClientCreator( + cfg.TMConfig.LocalApp, + cfg.TMConfig.ProxyApp, + cfg.TMConfig.ABCI, + cfg.TMConfig.DBDir(), + ) + + // Create genesis factory + genProvider := func() (*bft.GenesisDoc, error) { return cfg.Genesis, nil } + + dbProvider := func(*node.DBContext) (db.DB, error) { return db.NewMemDB(), nil } + + // generate p2p node identity + // XXX: do we need to configur + nodekey := &p2p.NodeKey{PrivKey: ed25519.GenPrivKey()} + + // Create and return the in-memory node instance + return node.NewNode(cfg.TMConfig, + cfg.PrivValidator, nodekey, + appClientCreator, + genProvider, + dbProvider, + logger, + ) +} + +// GetNodeReadiness waits until the node is ready, signaling via the EventNewBlock event. +// XXX: This should be replace by https://github.com/gnolang/gno/pull/1216 +func GetNodeReadiness(n *node.Node) <-chan struct{} { + const listenerID = "first_block_listener" + + var once sync.Once + + nb := make(chan struct{}) + ready := func() { + close(nb) + n.EventSwitch().RemoveListener(listenerID) + } + + n.EventSwitch().AddListener(listenerID, func(ev events.Event) { + if _, ok := ev.(bft.EventNewBlock); ok { + once.Do(ready) + } + }) + + if n.BlockStore().Height() > 0 { + once.Do(ready) + } + + return nb +} diff --git a/gno.land/pkg/gnoland/types.go b/gno.land/pkg/gnoland/types.go index 1c762366ae9..5d68064c9c5 100644 --- a/gno.land/pkg/gnoland/types.go +++ b/gno.land/pkg/gnoland/types.go @@ -1,9 +1,20 @@ package gnoland import ( + "errors" + "fmt" + "strings" + + bft "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/std" ) +var ( + ErrBalanceEmptyAddress = errors.New("balance address is empty") + ErrBalanceEmptyAmount = errors.New("balance amount is empty") +) + type GnoAccount struct { std.BaseAccount } @@ -13,6 +24,56 @@ func ProtoGnoAccount() std.Account { } type GnoGenesisState struct { - Balances []string `json:"balances"` - Txs []std.Tx `json:"txs"` + Balances []Balance `json:"balances"` + Txs []std.Tx `json:"txs"` +} + +type Balance struct { + Address bft.Address + Amount std.Coins +} + +func (b *Balance) Verify() error { + if b.Address.IsZero() { + return ErrBalanceEmptyAddress + } + + if b.Amount.Len() == 0 { + return ErrBalanceEmptyAmount + } + + return nil +} + +func (b *Balance) Parse(entry string) error { + parts := strings.Split(strings.TrimSpace(entry), "=") //
= + if len(parts) != 2 { + return fmt.Errorf("malformed entry: %q", entry) + } + + var err error + + b.Address, err = crypto.AddressFromBech32(parts[0]) + if err != nil { + return fmt.Errorf("invalid address %q: %w", parts[0], err) + } + + b.Amount, err = std.ParseCoins(parts[1]) + if err != nil { + return fmt.Errorf("invalid amount %q: %w", parts[1], err) + } + + return nil +} + +func (b *Balance) UnmarshalAmino(rep string) error { + return b.Parse(rep) +} + +func (b Balance) MarshalAmino() (string, error) { + return b.String(), nil +} + +func (b Balance) String() string { + return fmt.Sprintf("%s=%s", b.Address.String(), b.Amount.String()) } diff --git a/gno.land/pkg/gnoland/types_test.go b/gno.land/pkg/gnoland/types_test.go new file mode 100644 index 00000000000..97222d0cdfd --- /dev/null +++ b/gno.land/pkg/gnoland/types_test.go @@ -0,0 +1,98 @@ +package gnoland + +import ( + "fmt" + "testing" + + "github.com/gnolang/gno/tm2/pkg/amino" + bft "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/jaekwon/testify/assert" + "github.com/jaekwon/testify/require" +) + +func TestBalance_Verify(t *testing.T) { + validAddress := crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + emptyAmount := std.Coins{} + nonEmptyAmount := std.NewCoins(std.NewCoin("test", 100)) + + tests := []struct { + name string + balance Balance + expectErr bool + }{ + {"empty amount", Balance{Address: validAddress, Amount: emptyAmount}, true}, + {"empty address", Balance{Address: bft.Address{}, Amount: nonEmptyAmount}, true}, + {"valid balance", Balance{Address: validAddress, Amount: nonEmptyAmount}, false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.balance.Verify() + if tc.expectErr { + assert.Error(t, err, fmt.Sprintf("TestVerifyBalance: %s", tc.name)) + } else { + assert.NoError(t, err, fmt.Sprintf("TestVerifyBalance: %s", tc.name)) + } + }) + } +} + +func TestBalance_Parse(t *testing.T) { + validAddress := crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5") + validBalance := Balance{Address: validAddress, Amount: std.NewCoins(std.NewCoin("test", 100))} + + tests := []struct { + name string + entry string + expected Balance + expectErr bool + }{ + {"valid entry", "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5=100test", validBalance, false}, + {"invalid address", "invalid=100test", Balance{}, true}, + {"incomplete entry", "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", Balance{}, true}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + balance := Balance{} + err := balance.Parse(tc.entry) + if tc.expectErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tc.expected, balance) + } + }) + } +} + +func TestBalance_AminoUnmarshalJSON(t *testing.T) { + expected := Balance{ + Address: crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"), + Amount: std.MustParseCoins("100ugnot"), + } + value := fmt.Sprintf("[%q]", expected.String()) + + var balances []Balance + err := amino.UnmarshalJSON([]byte(value), &balances) + require.NoError(t, err) + require.Len(t, balances, 1, "there should be one balance after unmarshaling") + + balance := balances[0] + require.Equal(t, expected.Address, balance.Address) + require.True(t, expected.Amount.IsEqual(balance.Amount)) +} + +func TestBalance_AminoMarshalJSON(t *testing.T) { + expected := Balance{ + Address: crypto.MustAddressFromString("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5"), + Amount: std.MustParseCoins("100ugnot"), + } + expectedJSON := fmt.Sprintf("[%q]", expected.String()) + + balancesJSON, err := amino.MarshalJSON([]Balance{expected}) + require.NoError(t, err) + require.JSONEq(t, expectedJSON, string(balancesJSON)) +} diff --git a/gno.land/pkg/gnoweb/gnoweb.go b/gno.land/pkg/gnoweb/gnoweb.go new file mode 100644 index 00000000000..451389c7d73 --- /dev/null +++ b/gno.land/pkg/gnoweb/gnoweb.go @@ -0,0 +1,508 @@ +package gnoweb + +import ( + "embed" + "encoding/json" + "errors" + "fmt" + "io" + "io/fs" + "net/http" + "os" + "path/filepath" + "runtime" + "strings" + "time" + + "golang.org/x/exp/slog" + + "github.com/gnolang/gno/tm2/pkg/amino" + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/gorilla/mux" + "github.com/gotuna/gotuna" + + // for static files + "github.com/gnolang/gno/gno.land/pkg/gnoweb/static" + "github.com/gnolang/gno/gno.land/pkg/sdk/vm" // for error types + // "github.com/gnolang/gno/tm2/pkg/sdk" // for baseapp (info, status) +) + +const ( + qFileStr = "vm/qfile" +) + +//go:embed views/* +var defaultViewsFiles embed.FS + +type Config struct { + RemoteAddr string + CaptchaSite string + FaucetURL string + ViewsDir string + HelpChainID string + HelpRemote string + WithAnalytics bool +} + +func NewDefaultConfig() Config { + return Config{ + RemoteAddr: "127.0.0.1:26657", + CaptchaSite: "", + FaucetURL: "http://localhost:5050", + ViewsDir: "", + HelpChainID: "dev", + HelpRemote: "127.0.0.1:26657", + WithAnalytics: false, + } +} + +func MakeApp(logger *slog.Logger, cfg Config) gotuna.App { + var viewFiles fs.FS + + // Get specific views directory if specified + if cfg.ViewsDir != "" { + viewFiles = os.DirFS(cfg.ViewsDir) + } else { + // Get embed views + var err error + viewFiles, err = fs.Sub(defaultViewsFiles, "views") + if err != nil { + panic("unable to get views directory from embed fs: " + err.Error()) + } + } + + app := gotuna.App{ + ViewFiles: viewFiles, + Router: gotuna.NewMuxRouter(), + Static: static.EmbeddedStatic, + } + + // realm aliases + aliases := map[string]string{ + "/": "/r/gnoland/home", + "/about": "/r/gnoland/pages:p/about", + "/gnolang": "/r/gnoland/pages:p/gnolang", + "/ecosystem": "/r/gnoland/pages:p/ecosystem", + "/partners": "/r/gnoland/pages:p/partners", + "/testnets": "/r/gnoland/pages:p/testnets", + "/start": "/r/gnoland/pages:p/start", + "/game-of-realms": "/r/gnoland/pages:p/gor", // XXX: replace with gor realm + "/events": "/r/gnoland/pages:p/events", // XXX: replace with events realm + } + for from, to := range aliases { + app.Router.Handle(from, handlerRealmAlias(logger, app, &cfg, to)) + } + // http redirects + redirects := map[string]string{ + "/r/demo/boards:gnolang/6": "/r/demo/boards:gnolang/3", // XXX: temporary + "/blog": "/r/gnoland/blog", + "/gor": "/game-of-realms", + "/grants": "/partners", + "/language": "/gnolang", + "/getting-started": "/start", + } + for from, to := range redirects { + app.Router.Handle(from, handlerRedirect(logger, app, &cfg, to)) + } + // realm routes + // NOTE: see rePathPart. + app.Router.Handle("/r/{rlmname:[a-z][a-z0-9_]*(?:/[a-z][a-z0-9_]*)+}/{filename:(?:.*\\.(?:gno|md|txt)$)?}", handlerRealmFile(logger, app, &cfg)) + app.Router.Handle("/r/{rlmname:[a-z][a-z0-9_]*(?:/[a-z][a-z0-9_]*)+}", handlerRealmMain(logger, app, &cfg)) + app.Router.Handle("/r/{rlmname:[a-z][a-z0-9_]*(?:/[a-z][a-z0-9_]*)+}:{querystr:.*}", handlerRealmRender(logger, app, &cfg)) + app.Router.Handle("/p/{filepath:.*}", handlerPackageFile(logger, app, &cfg)) + + // other + app.Router.Handle("/faucet", handlerFaucet(logger, app, &cfg)) + app.Router.Handle("/static/{path:.+}", handlerStaticFile(logger, app, &cfg)) + app.Router.Handle("/favicon.ico", handlerFavicon(logger, app, &cfg)) + + // api + app.Router.Handle("/status.json", handlerStatusJSON(logger, app, &cfg)) + + app.Router.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + path := r.RequestURI + handleNotFound(app, &cfg, path, w, r) + }) + return app +} + +// handlerRealmAlias is used to render official pages from realms. +// url is intended to be shorter. +// UX is intended to be more minimalistic. +// A link to the realm realm is added. +func handlerRealmAlias(logger *slog.Logger, app gotuna.App, cfg *Config, rlmpath string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + rlmfullpath := "gno.land" + rlmpath + querystr := "" // XXX: "?gnoweb-alias=1" + parts := strings.Split(rlmpath, ":") + switch len(parts) { + case 1: // continue + case 2: // r/realm:querystr + rlmfullpath = "gno.land" + parts[0] + querystr = parts[1] + querystr + default: + panic("should not happen") + } + rlmname := strings.TrimPrefix(rlmfullpath, "gno.land/r/") + qpath := "vm/qrender" + data := []byte(fmt.Sprintf("%s\n%s", rlmfullpath, querystr)) + res, err := makeRequest(logger, cfg, qpath, data) + if err != nil { + writeError(logger, w, fmt.Errorf("gnoweb failed to query gnoland: %w", err)) + return + } + + queryParts := strings.Split(querystr, "/") + pathLinks := []pathLink{} + for i, part := range queryParts { + pathLinks = append(pathLinks, pathLink{ + URL: "/r/" + rlmname + ":" + strings.Join(queryParts[:i+1], "/"), + Text: part, + }) + } + + tmpl := app.NewTemplatingEngine() + // XXX: extract title from realm's output + // XXX: extract description from realm's output + tmpl.Set("RealmName", rlmname) + tmpl.Set("RealmPath", rlmpath) + tmpl.Set("Query", querystr) + tmpl.Set("PathLinks", pathLinks) + tmpl.Set("Contents", string(res.Data)) + tmpl.Set("Config", cfg) + tmpl.Set("IsAlias", true) + tmpl.Render(w, r, "realm_render.html", "funcs.html") + }) +} + +func handlerFaucet(logger *slog.Logger, app gotuna.App, cfg *Config) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + app.NewTemplatingEngine(). + Set("Config", cfg). + Render(w, r, "faucet.html", "funcs.html") + }) +} + +func handlerStatusJSON(logger *slog.Logger, app gotuna.App, cfg *Config) http.Handler { + startedAt := time.Now() + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var ret struct { + Gnoland struct { + Connected bool `json:"connected"` + Error *string `json:"error,omitempty"` + Height *int64 `json:"height,omitempty"` + // processed txs + // active connections + + Version *string `json:"version,omitempty"` + // Uptime *float64 `json:"uptime-seconds,omitempty"` + // Goarch *string `json:"goarch,omitempty"` + // Goos *string `json:"goos,omitempty"` + // GoVersion *string `json:"go-version,omitempty"` + // NumCPU *int `json:"num_cpu,omitempty"` + } `json:"gnoland"` + Website struct { + // Version string `json:"version"` + Uptime float64 `json:"uptime-seconds"` + Goarch string `json:"goarch"` + Goos string `json:"goos"` + GoVersion string `json:"go-version"` + NumCPU int `json:"num_cpu"` + } `json:"website"` + } + ret.Website.Uptime = time.Since(startedAt).Seconds() + ret.Website.Goarch = runtime.GOARCH + ret.Website.Goos = runtime.GOOS + ret.Website.NumCPU = runtime.NumCPU() + ret.Website.GoVersion = runtime.Version() + + ret.Gnoland.Connected = true + res, err := makeRequest(logger, cfg, ".app/version", []byte{}) + if err != nil { + ret.Gnoland.Connected = false + errmsg := err.Error() + ret.Gnoland.Error = &errmsg + } else { + version := string(res.Value) + ret.Gnoland.Version = &version + ret.Gnoland.Height = &res.Height + } + + out, _ := json.MarshalIndent(ret, "", " ") + w.Header().Set("Content-Type", "application/json") + w.Write(out) + }) +} + +func handlerRedirect(logger *slog.Logger, app gotuna.App, cfg *Config, to string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, to, http.StatusFound) + tmpl := app.NewTemplatingEngine() + tmpl.Set("To", to) + tmpl.Set("Config", cfg) + tmpl.Render(w, r, "redirect.html", "funcs.html") + }) +} + +func handlerRealmMain(logger *slog.Logger, app gotuna.App, cfg *Config) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + rlmname := vars["rlmname"] + rlmpath := "gno.land/r/" + rlmname + query := r.URL.Query() + + logger.Info("handling", "name", rlmname, "path", rlmpath) + if query.Has("help") { + // Render function helper. + funcName := query.Get("__func") + qpath := "vm/qfuncs" + data := []byte(rlmpath) + res, err := makeRequest(logger, cfg, qpath, data) + if err != nil { + writeError(logger, w, fmt.Errorf("request failed: %w", err)) + return + } + var fsigs vm.FunctionSignatures + amino.MustUnmarshalJSON(res.Data, &fsigs) + // Fill fsigs with query parameters. + for i := range fsigs { + fsig := &(fsigs[i]) + for j := range fsig.Params { + param := &(fsig.Params[j]) + value := query.Get(param.Name) + param.Value = value + } + } + // Render template. + tmpl := app.NewTemplatingEngine() + tmpl.Set("FuncName", funcName) + tmpl.Set("RealmPath", rlmpath) + tmpl.Set("DirPath", pathOf(rlmpath)) + tmpl.Set("FunctionSignatures", fsigs) + tmpl.Set("Config", cfg) + tmpl.Render(w, r, "realm_help.html", "funcs.html") + } else { + // Ensure realm exists. TODO optimize. + qpath := qFileStr + data := []byte(rlmpath) + _, err := makeRequest(logger, cfg, qpath, data) + if err != nil { + writeError(logger, w, errors.New("error querying realm package")) + return + } + // Render blank query path, /r/REALM:. + handleRealmRender(logger, app, cfg, w, r) + } + }) +} + +type pathLink struct { + URL string + Text string +} + +func handlerRealmRender(logger *slog.Logger, app gotuna.App, cfg *Config) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handleRealmRender(logger, app, cfg, w, r) + }) +} + +func handleRealmRender(logger *slog.Logger, app gotuna.App, cfg *Config, w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + rlmname := vars["rlmname"] + rlmpath := "gno.land/r/" + rlmname + querystr := vars["querystr"] + if r.URL.Path == "/r/"+rlmname+":" { + // Redirect to /r/REALM if querypath is empty. + http.Redirect(w, r, "/r/"+rlmname, http.StatusFound) + return + } + qpath := "vm/qrender" + data := []byte(fmt.Sprintf("%s\n%s", rlmpath, querystr)) + res, err := makeRequest(logger, cfg, qpath, data) + if err != nil { + // XXX hack + if strings.Contains(err.Error(), "Render not declared") { + res = &abci.ResponseQuery{} + res.Data = []byte("realm package has no Render() function") + } else { + writeError(logger, w, err) + return + } + } + // linkify querystr. + queryParts := strings.Split(querystr, "/") + pathLinks := []pathLink{} + for i, part := range queryParts { + pathLinks = append(pathLinks, pathLink{ + URL: "/r/" + rlmname + ":" + strings.Join(queryParts[:i+1], "/"), + Text: part, + }) + } + // Render template. + tmpl := app.NewTemplatingEngine() + // XXX: extract title from realm's output + // XXX: extract description from realm's output + tmpl.Set("RealmName", rlmname) + tmpl.Set("RealmPath", rlmpath) + tmpl.Set("Query", querystr) + tmpl.Set("PathLinks", pathLinks) + tmpl.Set("Contents", string(res.Data)) + tmpl.Set("Config", cfg) + tmpl.Render(w, r, "realm_render.html", "funcs.html") +} + +func handlerRealmFile(logger *slog.Logger, app gotuna.App, cfg *Config) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + diruri := "gno.land/r/" + vars["rlmname"] + filename := vars["filename"] + renderPackageFile(logger, app, cfg, w, r, diruri, filename) + }) +} + +func handlerPackageFile(logger *slog.Logger, app gotuna.App, cfg *Config) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + pkgpath := "gno.land/p/" + vars["filepath"] + diruri, filename := std.SplitFilepath(pkgpath) + if filename == "" && diruri == pkgpath { + // redirect to diruri + "/" + http.Redirect(w, r, "/p/"+vars["filepath"]+"/", http.StatusFound) + return + } + renderPackageFile(logger, app, cfg, w, r, diruri, filename) + }) +} + +func renderPackageFile(logger *slog.Logger, app gotuna.App, cfg *Config, w http.ResponseWriter, r *http.Request, diruri string, filename string) { + if filename == "" { + // Request is for a folder. + qpath := qFileStr + data := []byte(diruri) + res, err := makeRequest(logger, cfg, qpath, data) + if err != nil { + writeError(logger, w, err) + return + } + files := strings.Split(string(res.Data), "\n") + // Render template. + tmpl := app.NewTemplatingEngine() + tmpl.Set("DirURI", diruri) + tmpl.Set("DirPath", pathOf(diruri)) + tmpl.Set("Files", files) + tmpl.Set("Config", cfg) + tmpl.Render(w, r, "package_dir.html", "funcs.html") + } else { + // Request is for a file. + filepath := diruri + "/" + filename + qpath := qFileStr + data := []byte(filepath) + res, err := makeRequest(logger, cfg, qpath, data) + if err != nil { + writeError(logger, w, err) + return + } + // Render template. + tmpl := app.NewTemplatingEngine() + tmpl.Set("DirURI", diruri) + tmpl.Set("DirPath", pathOf(diruri)) + tmpl.Set("FileName", filename) + tmpl.Set("FileContents", string(res.Data)) + tmpl.Set("Config", cfg) + tmpl.Render(w, r, "package_file.html", "funcs.html") + } +} + +func makeRequest(log *slog.Logger, cfg *Config, qpath string, data []byte) (res *abci.ResponseQuery, err error) { + opts2 := client.ABCIQueryOptions{ + // Height: height, XXX + // Prove: false, XXX + } + remote := cfg.RemoteAddr + cli := client.NewHTTP(remote, "/websocket") + qres, err := cli.ABCIQueryWithOptions( + qpath, data, opts2) + if err != nil { + log.Error("request error", "path", qpath, "error", err) + return nil, fmt.Errorf("unable to query path %q: %w", qpath, err) + } + if qres.Response.Error != nil { + log.Error("response error", "path", qpath, "log", qres.Response.Log) + return nil, qres.Response.Error + } + return &qres.Response, nil +} + +func handlerStaticFile(logger *slog.Logger, app gotuna.App, cfg *Config) http.Handler { + fs := http.FS(app.Static) + fileapp := http.StripPrefix("/static", http.FileServer(fs)) + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + fpath := filepath.Clean(vars["path"]) + f, err := fs.Open(fpath) + if os.IsNotExist(err) { + handleNotFound(app, cfg, fpath, w, r) + return + } + stat, err := f.Stat() + if err != nil || stat.IsDir() { + handleNotFound(app, cfg, fpath, w, r) + return + } + + // TODO: ModTime doesn't work for embed? + // w.Header().Set("ETag", fmt.Sprintf("%x", stat.ModTime().UnixNano())) + // w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%s", "31536000")) + fileapp.ServeHTTP(w, r) + }) +} + +func handlerFavicon(logger *slog.Logger, app gotuna.App, cfg *Config) http.Handler { + fs := http.FS(app.Static) + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fpath := "img/favicon.ico" + f, err := fs.Open(fpath) + if os.IsNotExist(err) { + handleNotFound(app, cfg, fpath, w, r) + return + } + w.Header().Set("Content-Type", "image/x-icon") + w.Header().Set("Cache-Control", "public, max-age=604800") // 7d + io.Copy(w, f) + }) +} + +func handleNotFound(app gotuna.App, cfg *Config, path string, w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + app.NewTemplatingEngine(). + Set("title", "Not found"). + Set("path", path). + Set("Config", cfg). + Render(w, r, "404.html", "funcs.html") +} + +func writeError(logger *slog.Logger, w http.ResponseWriter, err error) { + if details := errors.Unwrap(err); details != nil { + logger.Error("handler", "error", err, "details", details) + } else { + logger.Error("handler", "error:", err) + } + + // XXX: writeError should return an error page template. + w.WriteHeader(500) + w.Write([]byte(err.Error())) +} + +func pathOf(diruri string) string { + parts := strings.Split(diruri, "/") + if parts[0] == "gno.land" { + return "/" + strings.Join(parts[1:], "/") + } else { + panic(fmt.Sprintf("invalid dir-URI %q", diruri)) + } +} diff --git a/gno.land/pkg/gnoweb/gnoweb_test.go b/gno.land/pkg/gnoweb/gnoweb_test.go new file mode 100644 index 00000000000..82c76266683 --- /dev/null +++ b/gno.land/pkg/gnoweb/gnoweb_test.go @@ -0,0 +1,131 @@ +package gnoweb + +import ( + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/gnolang/gno/gno.land/pkg/integration" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/tm2/pkg/log" + "github.com/gotuna/gotuna/test/assert" +) + +func TestRoutes(t *testing.T) { + const ( + ok = http.StatusOK + found = http.StatusFound + notFound = http.StatusNotFound + ) + routes := []struct { + route string + status int + substring string + }{ + {"/", ok, "Welcome"}, // assert / gives 200 (OK). assert / contains "Welcome". + {"/about", ok, "blockchain"}, + {"/r/gnoland/blog", ok, ""}, // whatever content + {"/r/gnoland/blog?help", ok, "exposed"}, + {"/r/gnoland/blog/", ok, "admin.gno"}, + {"/r/gnoland/blog/admin.gno", ok, "func "}, + {"/r/demo/users:administrator", ok, "address"}, + {"/r/demo/users", ok, "manfred"}, + {"/r/demo/users/types.gno", ok, "type "}, + {"/r/demo/deep/very/deep", ok, "it works!"}, + {"/r/demo/deep/very/deep:bob", ok, "hi bob"}, + {"/r/demo/deep/very/deep?help", ok, "exposed"}, + {"/r/demo/deep/very/deep/", ok, "render.gno"}, + {"/r/demo/deep/very/deep/render.gno", ok, "func Render("}, + {"/game-of-realms", ok, "/r/gnoland/pages:p/gor"}, + {"/gor", found, "/game-of-realms"}, + {"/blog", found, "/r/gnoland/blog"}, + {"/404-not-found", notFound, "/404-not-found"}, + } + + config, _ := integration.TestingNodeConfig(t, gnoenv.RootDir()) + node, remoteAddr := integration.TestingInMemoryNode(t, log.NewTestingLogger(t), config) + defer node.Stop() + + cfg := NewDefaultConfig() + + logger := log.NewTestingLogger(t) + + // set the `remoteAddr` of the client to the listening address of the + // node, which is randomly assigned. + cfg.RemoteAddr = remoteAddr + app := MakeApp(logger, cfg) + + for _, r := range routes { + t.Run(fmt.Sprintf("test route %s", r.route), func(t *testing.T) { + request := httptest.NewRequest(http.MethodGet, r.route, nil) + response := httptest.NewRecorder() + app.Router.ServeHTTP(response, request) + assert.Equal(t, r.status, response.Code) + + assert.Contains(t, response.Body.String(), r.substring) + // println(response.Body.String()) + }) + } +} + +func TestAnalytics(t *testing.T) { + routes := []string{ + // special realms + "/", // home + "/about", + "/start", + + // redirects + "/game-of-realms", + "/getting-started", + "/blog", + "/boards", + + // realm, source, help page + "/r/gnoland/blog", + "/r/gnoland/blog/admin.gno", + "/r/demo/users:administrator", + "/r/gnoland/blog?help", + + // special pages + "/404-not-found", + } + + config, _ := integration.TestingNodeConfig(t, gnoenv.RootDir()) + node, remoteAddr := integration.TestingInMemoryNode(t, log.NewTestingLogger(t), config) + defer node.Stop() + + cfg := NewDefaultConfig() + cfg.RemoteAddr = remoteAddr + + logger := log.NewTestingLogger(t) + + t.Run("with", func(t *testing.T) { + for _, route := range routes { + t.Run(route, func(t *testing.T) { + ccfg := cfg // clone config + ccfg.WithAnalytics = true + app := MakeApp(logger, ccfg) + request := httptest.NewRequest(http.MethodGet, route, nil) + response := httptest.NewRecorder() + app.Router.ServeHTTP(response, request) + assert.Contains(t, response.Body.String(), "sa.gno.services") + }) + } + }) + t.Run("without", func(t *testing.T) { + for _, route := range routes { + t.Run(route, func(t *testing.T) { + ccfg := cfg // clone config + ccfg.WithAnalytics = false + app := MakeApp(logger, ccfg) + request := httptest.NewRequest(http.MethodGet, route, nil) + response := httptest.NewRecorder() + app.Router.ServeHTTP(response, request) + assert.Equal(t, strings.Contains(response.Body.String(), "sa.gno.services"), false) + }) + } + }) +} diff --git a/gno.land/cmd/gnoweb/static/css/app.css b/gno.land/pkg/gnoweb/static/css/app.css similarity index 100% rename from gno.land/cmd/gnoweb/static/css/app.css rename to gno.land/pkg/gnoweb/static/css/app.css diff --git a/gno.land/cmd/gnoweb/static/css/normalize.css b/gno.land/pkg/gnoweb/static/css/normalize.css similarity index 100% rename from gno.land/cmd/gnoweb/static/css/normalize.css rename to gno.land/pkg/gnoweb/static/css/normalize.css diff --git a/gno.land/cmd/gnoweb/static/font/README.md b/gno.land/pkg/gnoweb/static/font/README.md similarity index 100% rename from gno.land/cmd/gnoweb/static/font/README.md rename to gno.land/pkg/gnoweb/static/font/README.md diff --git a/gno.land/cmd/gnoweb/static/font/roboto/LICENSE.txt b/gno.land/pkg/gnoweb/static/font/roboto/LICENSE.txt similarity index 100% rename from gno.land/cmd/gnoweb/static/font/roboto/LICENSE.txt rename to gno.land/pkg/gnoweb/static/font/roboto/LICENSE.txt diff --git a/gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-Bold.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Bold.woff similarity index 100% rename from gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-Bold.woff rename to gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Bold.woff diff --git a/gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-BoldItalic.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-BoldItalic.woff similarity index 100% rename from gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-BoldItalic.woff rename to gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-BoldItalic.woff diff --git a/gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-Italic.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Italic.woff similarity index 100% rename from gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-Italic.woff rename to gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Italic.woff diff --git a/gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-Light.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Light.woff similarity index 100% rename from gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-Light.woff rename to gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Light.woff diff --git a/gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-LightItalic.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-LightItalic.woff similarity index 100% rename from gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-LightItalic.woff rename to gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-LightItalic.woff diff --git a/gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-Medium.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Medium.woff similarity index 100% rename from gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-Medium.woff rename to gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Medium.woff diff --git a/gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-MediumItalic.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-MediumItalic.woff similarity index 100% rename from gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-MediumItalic.woff rename to gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-MediumItalic.woff diff --git a/gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-Regular.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Regular.woff similarity index 100% rename from gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-Regular.woff rename to gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Regular.woff diff --git a/gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-Thin.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Thin.woff similarity index 100% rename from gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-Thin.woff rename to gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-Thin.woff diff --git a/gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-ThinItalic.woff b/gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-ThinItalic.woff similarity index 100% rename from gno.land/cmd/gnoweb/static/font/roboto/RobotoMono-ThinItalic.woff rename to gno.land/pkg/gnoweb/static/font/roboto/RobotoMono-ThinItalic.woff diff --git a/gno.land/cmd/gnoweb/static/img/favicon.ico b/gno.land/pkg/gnoweb/static/img/favicon.ico similarity index 100% rename from gno.land/cmd/gnoweb/static/img/favicon.ico rename to gno.land/pkg/gnoweb/static/img/favicon.ico diff --git a/gno.land/cmd/gnoweb/static/img/github-mark-32px.png b/gno.land/pkg/gnoweb/static/img/github-mark-32px.png similarity index 100% rename from gno.land/cmd/gnoweb/static/img/github-mark-32px.png rename to gno.land/pkg/gnoweb/static/img/github-mark-32px.png diff --git a/gno.land/cmd/gnoweb/static/img/github-mark-64px.png b/gno.land/pkg/gnoweb/static/img/github-mark-64px.png similarity index 100% rename from gno.land/cmd/gnoweb/static/img/github-mark-64px.png rename to gno.land/pkg/gnoweb/static/img/github-mark-64px.png diff --git a/gno.land/cmd/gnoweb/static/img/ico-discord.svg b/gno.land/pkg/gnoweb/static/img/ico-discord.svg similarity index 100% rename from gno.land/cmd/gnoweb/static/img/ico-discord.svg rename to gno.land/pkg/gnoweb/static/img/ico-discord.svg diff --git a/gno.land/cmd/gnoweb/static/img/ico-email.svg b/gno.land/pkg/gnoweb/static/img/ico-email.svg similarity index 100% rename from gno.land/cmd/gnoweb/static/img/ico-email.svg rename to gno.land/pkg/gnoweb/static/img/ico-email.svg diff --git a/gno.land/cmd/gnoweb/static/img/ico-telegram.svg b/gno.land/pkg/gnoweb/static/img/ico-telegram.svg similarity index 100% rename from gno.land/cmd/gnoweb/static/img/ico-telegram.svg rename to gno.land/pkg/gnoweb/static/img/ico-telegram.svg diff --git a/gno.land/cmd/gnoweb/static/img/ico-twitter.svg b/gno.land/pkg/gnoweb/static/img/ico-twitter.svg similarity index 100% rename from gno.land/cmd/gnoweb/static/img/ico-twitter.svg rename to gno.land/pkg/gnoweb/static/img/ico-twitter.svg diff --git a/gno.land/cmd/gnoweb/static/img/ico-youtube.svg b/gno.land/pkg/gnoweb/static/img/ico-youtube.svg similarity index 100% rename from gno.land/cmd/gnoweb/static/img/ico-youtube.svg rename to gno.land/pkg/gnoweb/static/img/ico-youtube.svg diff --git a/gno.land/cmd/gnoweb/static/img/list-alt.png b/gno.land/pkg/gnoweb/static/img/list-alt.png similarity index 100% rename from gno.land/cmd/gnoweb/static/img/list-alt.png rename to gno.land/pkg/gnoweb/static/img/list-alt.png diff --git a/gno.land/cmd/gnoweb/static/img/list.png b/gno.land/pkg/gnoweb/static/img/list.png similarity index 100% rename from gno.land/cmd/gnoweb/static/img/list.png rename to gno.land/pkg/gnoweb/static/img/list.png diff --git a/gno.land/cmd/gnoweb/static/img/logo-square.png b/gno.land/pkg/gnoweb/static/img/logo-square.png similarity index 100% rename from gno.land/cmd/gnoweb/static/img/logo-square.png rename to gno.land/pkg/gnoweb/static/img/logo-square.png diff --git a/gno.land/cmd/gnoweb/static/img/logo-square.svg b/gno.land/pkg/gnoweb/static/img/logo-square.svg similarity index 100% rename from gno.land/cmd/gnoweb/static/img/logo-square.svg rename to gno.land/pkg/gnoweb/static/img/logo-square.svg diff --git a/gno.land/cmd/gnoweb/static/img/logo-v1.png b/gno.land/pkg/gnoweb/static/img/logo-v1.png similarity index 100% rename from gno.land/cmd/gnoweb/static/img/logo-v1.png rename to gno.land/pkg/gnoweb/static/img/logo-v1.png diff --git a/gno.land/cmd/gnoweb/static/img/logo.png b/gno.land/pkg/gnoweb/static/img/logo.png similarity index 100% rename from gno.land/cmd/gnoweb/static/img/logo.png rename to gno.land/pkg/gnoweb/static/img/logo.png diff --git a/gno.land/cmd/gnoweb/static/img/logo.svg b/gno.land/pkg/gnoweb/static/img/logo.svg similarity index 100% rename from gno.land/cmd/gnoweb/static/img/logo.svg rename to gno.land/pkg/gnoweb/static/img/logo.svg diff --git a/gno.land/cmd/gnoweb/static/invites.txt b/gno.land/pkg/gnoweb/static/invites.txt similarity index 100% rename from gno.land/cmd/gnoweb/static/invites.txt rename to gno.land/pkg/gnoweb/static/invites.txt diff --git a/gno.land/cmd/gnoweb/static/js/highlight.min.js b/gno.land/pkg/gnoweb/static/js/highlight.min.js similarity index 100% rename from gno.land/cmd/gnoweb/static/js/highlight.min.js rename to gno.land/pkg/gnoweb/static/js/highlight.min.js diff --git a/gno.land/cmd/gnoweb/static/js/marked.min.js b/gno.land/pkg/gnoweb/static/js/marked.min.js similarity index 100% rename from gno.land/cmd/gnoweb/static/js/marked.min.js rename to gno.land/pkg/gnoweb/static/js/marked.min.js diff --git a/gno.land/cmd/gnoweb/static/js/purify.min.js b/gno.land/pkg/gnoweb/static/js/purify.min.js similarity index 100% rename from gno.land/cmd/gnoweb/static/js/purify.min.js rename to gno.land/pkg/gnoweb/static/js/purify.min.js diff --git a/gno.land/cmd/gnoweb/static/js/realm_help.js b/gno.land/pkg/gnoweb/static/js/realm_help.js similarity index 100% rename from gno.land/cmd/gnoweb/static/js/realm_help.js rename to gno.land/pkg/gnoweb/static/js/realm_help.js diff --git a/gno.land/cmd/gnoweb/static/js/renderer.js b/gno.land/pkg/gnoweb/static/js/renderer.js similarity index 100% rename from gno.land/cmd/gnoweb/static/js/renderer.js rename to gno.land/pkg/gnoweb/static/js/renderer.js diff --git a/gno.land/cmd/gnoweb/static/js/umbrella.js b/gno.land/pkg/gnoweb/static/js/umbrella.js similarity index 99% rename from gno.land/cmd/gnoweb/static/js/umbrella.js rename to gno.land/pkg/gnoweb/static/js/umbrella.js index 2a02a61f6f3..9735d031265 100644 --- a/gno.land/cmd/gnoweb/static/js/umbrella.js +++ b/gno.land/pkg/gnoweb/static/js/umbrella.js @@ -617,7 +617,7 @@ u.prototype.scroll = function () { // [INTERNAL USE ONLY] -// Select the adecuate part from the context +// Select the adequate part from the context u.prototype.select = function (parameter, context) { // Allow for spaces before or after parameter = parameter.replace(/^\s*/, '').replace(/\s*$/, ''); diff --git a/gno.land/cmd/gnoweb/static/js/umbrella.min.js b/gno.land/pkg/gnoweb/static/js/umbrella.min.js similarity index 100% rename from gno.land/cmd/gnoweb/static/js/umbrella.min.js rename to gno.land/pkg/gnoweb/static/js/umbrella.min.js diff --git a/gno.land/cmd/gnoweb/static/static.go b/gno.land/pkg/gnoweb/static/static.go similarity index 100% rename from gno.land/cmd/gnoweb/static/static.go rename to gno.land/pkg/gnoweb/static/static.go diff --git a/gno.land/cmd/gnoweb/views/404.html b/gno.land/pkg/gnoweb/views/404.html similarity index 73% rename from gno.land/cmd/gnoweb/views/404.html rename to gno.land/pkg/gnoweb/views/404.html index c51543ca1cd..fee4fff8689 100644 --- a/gno.land/cmd/gnoweb/views/404.html +++ b/gno.land/pkg/gnoweb/views/404.html @@ -2,8 +2,7 @@ - - + {{ template "html_head" . }} 404 - Not Found @@ -13,6 +12,7 @@

{{.Data.path}}

+ {{ template "analytics" .}} {{- end -}} diff --git a/gno.land/cmd/gnoweb/views/faucet.html b/gno.land/pkg/gnoweb/views/faucet.html similarity index 92% rename from gno.land/cmd/gnoweb/views/faucet.html rename to gno.land/pkg/gnoweb/views/faucet.html index 84bcc6f34e5..36c5987b9c9 100644 --- a/gno.land/cmd/gnoweb/views/faucet.html +++ b/gno.land/pkg/gnoweb/views/faucet.html @@ -2,8 +2,8 @@ + {{ template "html_head" . }} Gno.land - {{ template "html_head" }}
@@ -20,7 +20,7 @@ {{ end }} -
+
{{ if .Data.captchaSite }} diff --git a/gno.land/cmd/gnoweb/views/funcs.html b/gno.land/pkg/gnoweb/views/funcs.html similarity index 86% rename from gno.land/cmd/gnoweb/views/funcs.html rename to gno.land/pkg/gnoweb/views/funcs.html index 8ac2fe6328b..391675755aa 100644 --- a/gno.land/cmd/gnoweb/views/funcs.html +++ b/gno.land/pkg/gnoweb/views/funcs.html @@ -1,4 +1,4 @@ -{{ define "header_buttons" }} +{{- define "header_buttons" -}}
-{{ end }} {{ define "html_head" }} +{{- end -}} + +{{- define "html_head" -}} + +{{if .Data.Description}}{{end}} -{{ end }} {{ define "header_logo" }} +{{- end -}} + +{{- define "header_logo" -}} -{{ end }} {{ define "footer" }} +{{- end -}} + +{{- define "footer" -}}
Gno.land
-{{ end }} {{ define "js" }} +{{- end -}} + +{{- define "js" -}} + -{{ end }} {{ define "subscribe" }} +{{ template "analytics" .}} +{{- end -}} + +{{- define "analytics" -}} +{{- if .Data.Config.WithAnalytics -}} + + + +{{- end -}} +{{- end -}} + +{{- define "subscribe" -}}
-{{ end }} +{{- end -}} diff --git a/gno.land/cmd/gnoweb/views/generic.html b/gno.land/pkg/gnoweb/views/generic.html similarity index 78% rename from gno.land/cmd/gnoweb/views/generic.html rename to gno.land/pkg/gnoweb/views/generic.html index 117ea9d59fe..e671625e26a 100644 --- a/gno.land/cmd/gnoweb/views/generic.html +++ b/gno.land/pkg/gnoweb/views/generic.html @@ -3,8 +3,7 @@ Gno.land - {{ .Data.Title }} - - {{ template "html_head" }} + {{ template "html_head" . }}
@@ -16,7 +15,7 @@
{{ template "footer" }}
- {{ template "js" }} + {{ template "js" .}} {{- end -}} diff --git a/gno.land/cmd/gnoweb/views/package_dir.html b/gno.land/pkg/gnoweb/views/package_dir.html similarity index 81% rename from gno.land/cmd/gnoweb/views/package_dir.html rename to gno.land/pkg/gnoweb/views/package_dir.html index 6b0bf5cfd48..efaf4d7ad0c 100644 --- a/gno.land/cmd/gnoweb/views/package_dir.html +++ b/gno.land/pkg/gnoweb/views/package_dir.html @@ -2,8 +2,8 @@ - Gno.land - {{ template "html_head" }} + {{ template "html_head" . }} + Gno.land - {{.Data.DirPath}}
@@ -12,13 +12,14 @@ {{ .Data.DirPath }}/*
{{ template "dir_contents" . }}
- {{ template "footer" }} - {{ template "js" }} + {{ template "js" . }} -{{- end -}} {{ define "dir_contents" }} +{{- end -}} + +{{- define "dir_contents" -}}
{{ $dirPath := .Data.DirPath }}
    @@ -29,4 +30,4 @@ {{ end }}
-{{ end }} +{{- end -}} diff --git a/gno.land/cmd/gnoweb/views/package_file.html b/gno.land/pkg/gnoweb/views/package_file.html similarity index 87% rename from gno.land/cmd/gnoweb/views/package_file.html rename to gno.land/pkg/gnoweb/views/package_file.html index 7068854d16c..71aa8b68452 100644 --- a/gno.land/cmd/gnoweb/views/package_file.html +++ b/gno.land/pkg/gnoweb/views/package_file.html @@ -2,8 +2,8 @@ - Gno.land - {{ template "html_head" }} + {{ template "html_head" . }} + Gno.land - {{.Data.DirPath}}/{{.Data.FileName}}
@@ -18,7 +18,7 @@ {{ template "footer" }}
- {{ template "js" }} + {{ template "js" .}} - + {{ template "js" . }} -{{- end -}} +{{ define "func_specs" }} +{{- end -}} + +{{- define "func_specs" -}}
{{ $funcName := .Data.FuncName }} {{ $found := false }} {{ if eq $funcName "" }} {{ range .Data.FunctionSignatures }} {{ template "func_spec" . }} {{ end }} {{ else }} {{ range .Data.FunctionSignatures }} {{ if eq .FuncName $funcName }} {{ $found = true }} {{ template "func_spec" . }} {{ end }} {{ end }} {{ if not $found }} {{ $funcName }} not found. {{ end }} {{ end }}
-{{ end }} {{ define "func_spec" }} +{{- end -}} + +{{- define "func_spec" -}}
@@ -67,7 +69,9 @@
-{{ end }} {{ define "func_param" }} +{{- end -}} + +{{- define "func_param" -}} {{ .Name }} @@ -75,7 +79,9 @@ {{ .Type }} -{{ end }} {{ define "func_result" }} +{{- end -}} + +{{- define "func_result" -}} {{ .Name }} {{ .Type }} diff --git a/gno.land/cmd/gnoweb/views/realm_render.html b/gno.land/pkg/gnoweb/views/realm_render.html similarity index 74% rename from gno.land/cmd/gnoweb/views/realm_render.html rename to gno.land/pkg/gnoweb/views/realm_render.html index 8a2c35adeca..6337d77aafa 100644 --- a/gno.land/cmd/gnoweb/views/realm_render.html +++ b/gno.land/pkg/gnoweb/views/realm_render.html @@ -2,8 +2,8 @@ - Gno.land - {{ template "html_head" }} + {{ template "html_head" . }} + Gno.land - {{.Data.RealmName}}
@@ -26,10 +26,7 @@
{{ template "footer" }} - {{ template "js" }} - - - + {{ template "js" .}} {{- end -}} diff --git a/gno.land/pkg/gnoweb/views/redirect.html b/gno.land/pkg/gnoweb/views/redirect.html new file mode 100644 index 00000000000..6fe43a7138b --- /dev/null +++ b/gno.land/pkg/gnoweb/views/redirect.html @@ -0,0 +1,16 @@ +{{- define "app" -}} + + + + + + + + Redirecting to {{.Data.To}} + + + {{.Data.To}} + {{ template "analytics" .}} + + +{{- end -}} diff --git a/gno.land/pkg/integration/doc.go b/gno.land/pkg/integration/doc.go new file mode 100644 index 00000000000..fb0b0bdf7a0 --- /dev/null +++ b/gno.land/pkg/integration/doc.go @@ -0,0 +1,91 @@ +// Package integration offers utilities to run txtar-based tests against the gnoland system +// by extending the functionalities provided by the standard testscript package. This package is +// currently in an experimental phase and may undergo significant changes in the future. +// +// SetupGnolandTestScript, sets up the environment for running txtar tests, introducing additional +// commands like "gnoland" and "gnokey" into the test script ecosystem. Specifically, it allows the +// user to initiate an in-memory gnoland node and interact with it via the `gnokey` command. +// +// Additional Command Overview: +// +// 1. `gnoland [start|stop]`: +// - The gnoland node doesn't start automatically. This enables the user to do some +// pre-configuration or pass custom arguments to the start command. +// +// 2. `gnokey`: +// - Supports most of the common commands. +// - `--remote`, `--insecure-password-stdin`, and `--home` flags are set automatically to +// communicate with the gnoland node. +// +// 3. `adduser`: +// - Creates a new user in the default keybase directory. +// - Must be run before `gnoland start`. +// +// Logging: +// +// Gnoland logs aren't forwarded to stdout to avoid overwhelming the tests with too much +// information. Instead, a log directory can be specified with `LOG_DIR`, or you +// can set `TESTWORK=true` +// to persist logs in the txtar working directory. In any case, the log file should be printed +// on start if one of these environment variables is set. +// +// Accounts: +// +// By default, only the test1 user will be created in the default keybase directory, +// with no password set. The default gnoland genesis balance file and the genesis +// transaction file are also registered by default. +// +// Examples: +// +// Examples can be found in the `testdata` directory of this package. +// +// Environment Variables: +// +// Input: +// +// - LOG_LEVEL: +// The logging level to be used, which can be one of "error", "debug", "info", or an empty string. +// If empty, the log level defaults to "debug". +// +// - LOG_DIR: +// If set, logs will be directed to the specified directory. +// +// - TESTWORK: +// A boolean that, when enabled, retains working directories after tests for +// inspection. If enabled, gnoland logs will be persisted inside this +// folder. +// +// - UPDATE_SCRIPTS: +// A boolean that, when enabled, updates the test scripts if a `cmp` command +// fails and its second argument refers to a file inside the testscript +// file. The content will be quoted with txtar.Quote if needed, requiring +// manual edits if it's not unquoted in the script. +// +// Output (available inside testscripts files): +// +// - WORK: +// The path to the temporary work directory tree created for each script. +// +// - GNOROOT: +// Points to the local location of the gno repository, serving as the GOROOT equivalent for gno. +// +// - GNOHOME: +// Refers to the local directory where gnokey stores its keys. +// +// - GNODATA: +// The path where the gnoland node stores its configuration and data. It's +// set only if the node has started. +// +// - USER_SEED_test1: +// Contains the seed for the test1 account. +// +// - USER_ADDR_test1: +// Contains the address for the test1 account. +// +// - RPC_ADDR: +// Points to the gnoland node's remote address. It's set only if the node has started. +// +// For a more comprehensive guide on original behaviors, additional commands and environment +// variables, refer to the original documentation of testscripts available here: +// https://github.com/rogpeppe/go-internal/blob/master/testscript/doc.go +package integration diff --git a/gno.land/pkg/integration/integration_test.go b/gno.land/pkg/integration/integration_test.go new file mode 100644 index 00000000000..f88707f2b90 --- /dev/null +++ b/gno.land/pkg/integration/integration_test.go @@ -0,0 +1,11 @@ +package integration + +import ( + "testing" +) + +func TestTestdata(t *testing.T) { + t.Parallel() + + RunGnolandTestscripts(t, "testdata") +} diff --git a/gno.land/pkg/integration/testdata/adduser.txtar b/gno.land/pkg/integration/testdata/adduser.txtar new file mode 100644 index 00000000000..ebd0e4abb43 --- /dev/null +++ b/gno.land/pkg/integration/testdata/adduser.txtar @@ -0,0 +1,34 @@ +adduser test8 + +## start a new node +gnoland start + +## add bar.gno package located in $WORK directory as gno.land/r/foobar/bar +gnokey maketx addpkg -pkgdir $WORK/bar -pkgpath gno.land/r/foobar/bar -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test8 + +## execute Render +gnokey maketx run -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test8 $WORK/script/script.gno + +## compare render +stdout 'main: --- hello from foo ---' +stdout 'OK!' +stdout 'GAS WANTED: 200000' +stdout 'GAS USED: ' + +# should fail if user is added after node is started +! adduser test5 +stderr '"adduser" error: adduser must be used before starting node' + +-- bar/bar.gno -- +package bar + +func Render(path string) string { + return "hello from foo" +} + +-- script/script.gno -- +package main +import "gno.land/r/foobar/bar" +func main() { + println("main: ---", bar.Render(""), "---") +} diff --git a/gno.land/pkg/integration/testdata/gnokey.txtar b/gno.land/pkg/integration/testdata/gnokey.txtar new file mode 100644 index 00000000000..123a0ce291c --- /dev/null +++ b/gno.land/pkg/integration/testdata/gnokey.txtar @@ -0,0 +1,24 @@ +# test basic gnokey integrations commands +# golden files have been generated using UPDATE_SCRIPTS=true + +# start gnoland +gnoland start + +## test1 account should be available on default +gnokey query auth/accounts/${USER_ADDR_test1} +stdout 'height: 0' +stdout 'data: {' +stdout ' "BaseAccount": {' +stdout ' "address": "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5",' +stdout ' "coins": "[0-9]*ugnot",' # dynamic +stdout ' "public_key": null,' +stdout ' "account_number": "0",' +stdout ' "sequence": "0"' +stdout ' }' +stdout '}' +! stderr '.+' # empty + +## invalid gnokey command should raise an error +! gnokey query foo/bar +stdout 'Log:' +stderr '"gnokey" error: unknown request error' diff --git a/gno.land/pkg/integration/testdata/gnoland.txtar b/gno.land/pkg/integration/testdata/gnoland.txtar new file mode 100644 index 00000000000..c675e7578b6 --- /dev/null +++ b/gno.land/pkg/integration/testdata/gnoland.txtar @@ -0,0 +1,43 @@ +# test basic gnoland integrations commands +# golden files have been generated using UPDATE_SCRIPTS=true + +## no arguments should fail +! gnoland +cmp stdout gnoland-no-arguments.stdout.golden +cmp stderr gnoland-no-arguments.stderr.golden + +## should be able to start +gnoland start +cmp stdout gnoland-start.stdout.golden +cmp stderr gnoland-start.stderr.golden + +## should not be able to start a node twice +! gnoland start +cmp stdout gnoland-already-start.stdout.golden +cmp stderr gnoland-already-start.stderr.golden + +## should be able to stop default +gnoland stop +cmp stdout gnoland-stop.stdout.golden +cmp stderr gnoland-stop.stderr.golden + +## should not be able to stop a node twice +! gnoland stop +cmp stdout gnoland-already-stop.stdout.golden +cmp stderr gnoland-already-stop.stderr.golden + +-- gnoland-no-arguments.stdout.golden -- +-- gnoland-no-arguments.stderr.golden -- +"gnoland" error: syntax: gnoland [start|stop] +-- gnoland-start.stdout.golden -- +node started successfully +-- gnoland-start.stderr.golden -- +-- gnoland-already-start.stdout.golden -- +-- gnoland-already-start.stderr.golden -- +"gnoland start" error: node already started +-- gnoland-stop.stdout.golden -- +node stopped successfully +-- gnoland-stop.stderr.golden -- +-- gnoland-already-stop.stdout.golden -- +-- gnoland-already-stop.stderr.golden -- +"gnoland stop" error: node not started cannot be stopped diff --git a/gno.land/pkg/integration/testing.go b/gno.land/pkg/integration/testing.go new file mode 100644 index 00000000000..7803e213da1 --- /dev/null +++ b/gno.land/pkg/integration/testing.go @@ -0,0 +1,39 @@ +package integration + +import ( + "errors" + + "github.com/jaekwon/testify/assert" + "github.com/jaekwon/testify/require" + "github.com/rogpeppe/go-internal/testscript" +) + +// This error is from testscript.Fatalf and is needed to correctly +// handle the FailNow method. +// see: https://github.com/rogpeppe/go-internal/blob/32ae33786eccde1672d4ba373c80e1bc282bfbf6/testscript/testscript.go#L799-L812 +var errFailNow = errors.New("fail now!") //nolint:stylecheck + +var ( + _ require.TestingT = (*testingTS)(nil) + _ assert.TestingT = (*testingTS)(nil) +) + +type TestingTS = require.TestingT + +type testingTS struct { + *testscript.TestScript +} + +func TSTestingT(ts *testscript.TestScript) TestingTS { + return &testingTS{ts} +} + +func (t *testingTS) Errorf(format string, args ...interface{}) { + defer recover() // we can ignore recover result, we just want to catch it up + t.Fatalf(format, args...) +} + +func (t *testingTS) FailNow() { + // unfortunately we can't access underlying `t.t.FailNow` method + panic(errFailNow) +} diff --git a/gno.land/pkg/integration/testing_integration.go b/gno.land/pkg/integration/testing_integration.go new file mode 100644 index 00000000000..b0dcc5737e6 --- /dev/null +++ b/gno.land/pkg/integration/testing_integration.go @@ -0,0 +1,374 @@ +package integration + +import ( + "context" + "errors" + "fmt" + "hash/crc32" + "os" + "path/filepath" + "strconv" + "strings" + "testing" + + "golang.org/x/exp/slog" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + "github.com/gnolang/gno/gno.land/pkg/keyscli" + "github.com/gnolang/gno/gno.land/pkg/log" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/tm2/pkg/bft/node" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto/bip39" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" + tm2Log "github.com/gnolang/gno/tm2/pkg/log" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/rogpeppe/go-internal/testscript" + "go.uber.org/zap/zapcore" +) + +const numTestAccounts int = 4 + +type tSeqShim struct{ *testing.T } + +// noop Parallel method allow us to run test sequentially +func (tSeqShim) Parallel() {} + +func (t tSeqShim) Run(name string, f func(testscript.T)) { + t.T.Run(name, func(t *testing.T) { + f(tSeqShim{t}) + }) +} + +func (t tSeqShim) Verbose() bool { + return testing.Verbose() +} + +// RunGnolandTestscripts sets up and runs txtar integration tests for gnoland nodes. +// It prepares an in-memory gnoland node and initializes the necessary environment and custom commands. +// The function adapts the test setup for use with the testscript package, enabling +// the execution of gnoland and gnokey commands within txtar scripts. +// +// Refer to package documentation in doc.go for more information on commands and example txtar scripts. +func RunGnolandTestscripts(t *testing.T, txtarDir string) { + t.Helper() + + p := setupGnolandTestScript(t, txtarDir) + if deadline, ok := t.Deadline(); ok && p.Deadline.IsZero() { + p.Deadline = deadline + } + + testscript.RunT(tSeqShim{t}, p) +} + +type testNode struct { + *node.Node + nGnoKeyExec uint // Counter for execution of gnokey. +} + +func setupGnolandTestScript(t *testing.T, txtarDir string) testscript.Params { + t.Helper() + + tmpdir := t.TempDir() + + // `gnoRootDir` should point to the local location of the gno repository. + // It serves as the gno equivalent of GOROOT. + gnoRootDir := gnoenv.RootDir() + + // `gnoHomeDir` should be the local directory where gnokey stores keys. + gnoHomeDir := filepath.Join(tmpdir, "gno") + + // Testscripts run concurrently by default, so we need to be prepared for that. + nodes := map[string]*testNode{} + + // Track new user balances added via the `adduser` command. These are added to the genesis + // state when the node is started. + var newUserBalances []gnoland.Balance + + updateScripts, _ := strconv.ParseBool(os.Getenv("UPDATE_SCRIPTS")) + persistWorkDir, _ := strconv.ParseBool(os.Getenv("TESTWORK")) + return testscript.Params{ + UpdateScripts: updateScripts, + TestWork: persistWorkDir, + Dir: txtarDir, + Setup: func(env *testscript.Env) error { + kb, err := keys.NewKeyBaseFromDir(gnoHomeDir) + if err != nil { + return err + } + + // create sessions ID + var sid string + { + works := env.Getenv("WORK") + sum := crc32.ChecksumIEEE([]byte(works)) + sid = strconv.FormatUint(uint64(sum), 16) + env.Setenv("SID", sid) + } + + // setup logger + var logger *slog.Logger + { + logger = tm2Log.NewNoopLogger() + if persistWorkDir || os.Getenv("LOG_PATH_DIR") != "" { + logname := fmt.Sprintf("txtar-gnoland-%s.log", sid) + logger, err = getTestingLogger(env, logname) + if err != nil { + return fmt.Errorf("unable to setup logger: %w", err) + } + } + + env.Values["_logger"] = logger + } + + // test1 must be created outside of the loop below because it is already included in genesis so + // attempting to recreate results in it getting overwritten and breaking existing tests that + // rely on its address being static. + kb.CreateAccount(DefaultAccount_Name, DefaultAccount_Seed, "", "", 0, 0) + env.Setenv("USER_SEED_"+DefaultAccount_Name, DefaultAccount_Seed) + env.Setenv("USER_ADDR_"+DefaultAccount_Name, DefaultAccount_Address) + + // Create test accounts starting from test2. + for i := 1; i < numTestAccounts; i++ { + accountName := "test" + strconv.Itoa(i+1) + + balance, err := createAccount(env, kb, accountName) + if err != nil { + return fmt.Errorf("error creating account %s: %w", accountName, err) + } + + newUserBalances = append(newUserBalances, balance) + } + + env.Setenv("GNOROOT", gnoRootDir) + env.Setenv("GNOHOME", gnoHomeDir) + + return nil + }, + Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){ + "gnoland": func(ts *testscript.TestScript, neg bool, args []string) { + if len(args) == 0 { + tsValidateError(ts, "gnoland", neg, fmt.Errorf("syntax: gnoland [start|stop]")) + return + } + + logger := ts.Value("_logger").(*slog.Logger) // grab logger + sid := getNodeSID(ts) // grab session id + + var cmd string + cmd, args = args[0], args[1:] + + var err error + switch cmd { + case "start": + if nodeIsRunning(nodes, sid) { + err = fmt.Errorf("node already started") + break + } + + // Warp up `ts` so we can pass it to other testing method + t := TSTestingT(ts) + + // Generate config and node + cfg, _ := TestingNodeConfig(t, gnoRootDir) + + // Add balances for users added via the `adduser` command. + genesis := cfg.Genesis + genesisState := gnoland.GnoGenesisState{ + Balances: genesis.AppState.(gnoland.GnoGenesisState).Balances, + Txs: genesis.AppState.(gnoland.GnoGenesisState).Txs, + } + genesisState.Balances = append(genesisState.Balances, newUserBalances...) + cfg.Genesis.AppState = genesisState + + n, remoteAddr := TestingInMemoryNode(t, logger, cfg) + + // Register cleanup + nodes[sid] = &testNode{Node: n} + + // Add default environements + ts.Setenv("RPC_ADDR", remoteAddr) + + fmt.Fprintln(ts.Stdout(), "node started successfully") + case "stop": + n, ok := nodes[sid] + if !ok { + err = fmt.Errorf("node not started cannot be stopped") + break + } + + if err = n.Stop(); err == nil { + delete(nodes, sid) + + // Unset gnoland environements + ts.Setenv("RPC_ADDR", "") + fmt.Fprintln(ts.Stdout(), "node stopped successfully") + } + default: + err = fmt.Errorf("invalid gnoland subcommand: %q", cmd) + } + + tsValidateError(ts, "gnoland "+cmd, neg, err) + }, + "gnokey": func(ts *testscript.TestScript, neg bool, args []string) { + logger := ts.Value("_logger").(*slog.Logger) // grab logger + sid := ts.Getenv("SID") // grab session id + + // Setup IO command + io := commands.NewTestIO() + io.SetOut(commands.WriteNopCloser(ts.Stdout())) + io.SetErr(commands.WriteNopCloser(ts.Stderr())) + cmd := keyscli.NewRootCmd(io, client.DefaultBaseOptions) + + io.SetIn(strings.NewReader("\n")) // Inject empty password to stdin. + defaultArgs := []string{ + "-home", gnoHomeDir, + "-insecure-password-stdin=true", // There no use to not have this param by default. + } + + if n, ok := nodes[sid]; ok { + if raddr := n.Config().RPC.ListenAddress; raddr != "" { + defaultArgs = append(defaultArgs, "-remote", raddr) + } + + n.nGnoKeyExec++ + headerlog := fmt.Sprintf("%.02d!EXEC_GNOKEY", n.nGnoKeyExec) + + // Log the command inside gnoland logger, so we can better scope errors. + logger.Info(headerlog, "args", strings.Join(args, " ")) + defer logger.Info(headerlog, "delimiter", "END") + } + + // Inject default argument, if duplicate + // arguments, it should be override by the ones + // user provided. + args = append(defaultArgs, args...) + + err := cmd.ParseAndRun(context.Background(), args) + + tsValidateError(ts, "gnokey", neg, err) + }, + // adduser commands must be executed before starting the node; it errors out otherwise. + "adduser": func(ts *testscript.TestScript, neg bool, args []string) { + if nodeIsRunning(nodes, getNodeSID(ts)) { + tsValidateError(ts, "adduser", neg, errors.New("adduser must be used before starting node")) + return + } + + if len(args) == 0 { + ts.Fatalf("new user name required") + } + + kb, err := keys.NewKeyBaseFromDir(gnoHomeDir) + if err != nil { + ts.Fatalf("unable to get keybase") + } + + balance, err := createAccount(ts, kb, args[0]) + if err != nil { + ts.Fatalf("error creating account %s: %s", args[0], err) + } + + newUserBalances = append(newUserBalances, balance) + }, + }, + } +} + +func getNodeSID(ts *testscript.TestScript) string { + return ts.Getenv("SID") +} + +func nodeIsRunning(nodes map[string]*testNode, sid string) bool { + _, ok := nodes[sid] + return ok +} + +func getTestingLogger(env *testscript.Env, logname string) (*slog.Logger, error) { + var path string + + if logdir := os.Getenv("LOG_PATH_DIR"); logdir != "" { + if err := os.MkdirAll(logdir, 0o755); err != nil { + return nil, fmt.Errorf("unable to make log directory %q", logdir) + } + + var err error + if path, err = filepath.Abs(filepath.Join(logdir, logname)); err != nil { + return nil, fmt.Errorf("unable to get absolute path of logdir %q", logdir) + } + } else if workdir := env.Getenv("WORK"); workdir != "" { + path = filepath.Join(workdir, logname) + } else { + return tm2Log.NewNoopLogger(), nil + } + + f, err := os.Create(path) + if err != nil { + return nil, fmt.Errorf("unable to create log file %q: %w", path, err) + } + + env.Defer(func() { + if err := f.Close(); err != nil { + panic(fmt.Errorf("unable to close log file %q: %w", path, err)) + } + }) + + // Initialize the logger + logLevel, err := zapcore.ParseLevel(strings.ToLower(os.Getenv("LOG_LEVEL"))) + if err != nil { + return nil, fmt.Errorf("unable to parse log level, %w", err) + } + + // Build zap logger for testing + zapLogger := log.NewZapTestingLogger(f, logLevel) + env.Defer(func() { zapLogger.Sync() }) + + env.T().Log("starting logger", path) + return log.ZapLoggerToSlog(zapLogger), nil +} + +func tsValidateError(ts *testscript.TestScript, cmd string, neg bool, err error) { + if err != nil { + fmt.Fprintf(ts.Stderr(), "%q error: %+v\n", cmd, err) + if !neg { + ts.Fatalf("unexpected %q command failure: %s", cmd, err) + } + } else { + if neg { + ts.Fatalf("unexpected %q command success", cmd) + } + } +} + +type envSetter interface { + Setenv(key, value string) +} + +// createAccount creates a new account with the given name and adds it to the keybase. +func createAccount(env envSetter, kb keys.Keybase, accountName string) (gnoland.Balance, error) { + var balance gnoland.Balance + entropy, err := bip39.NewEntropy(256) + if err != nil { + return balance, fmt.Errorf("error creating entropy: %w", err) + } + + mnemonic, err := bip39.NewMnemonic(entropy) + if err != nil { + return balance, fmt.Errorf("error generating mnemonic: %w", err) + } + + var keyInfo keys.Info + if keyInfo, err = kb.CreateAccount(accountName, mnemonic, "", "", 0, 0); err != nil { + return balance, fmt.Errorf("unable to create account: %w", err) + } + + address := keyInfo.GetAddress() + env.Setenv("USER_SEED_"+accountName, mnemonic) + env.Setenv("USER_ADDR_"+accountName, address.String()) + + return gnoland.Balance{ + Address: address, + Amount: std.Coins{std.NewCoin("ugnot", 1000000000000000000)}, + }, nil +} diff --git a/gno.land/pkg/integration/testing_node.go b/gno.land/pkg/integration/testing_node.go new file mode 100644 index 00000000000..cc0262b8105 --- /dev/null +++ b/gno.land/pkg/integration/testing_node.go @@ -0,0 +1,156 @@ +package integration + +import ( + "path/filepath" + "time" + + "golang.org/x/exp/slog" + + "github.com/gnolang/gno/gno.land/pkg/gnoland" + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + tmcfg "github.com/gnolang/gno/tm2/pkg/bft/config" + "github.com/gnolang/gno/tm2/pkg/bft/node" + bft "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/jaekwon/testify/require" +) + +const ( + DefaultAccount_Name = "test1" + DefaultAccount_Address = "g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5" + DefaultAccount_Seed = "source bonus chronic canvas draft south burst lottery vacant surface solve popular case indicate oppose farm nothing bullet exhibit title speed wink action roast" +) + +// TestingInMemoryNode initializes and starts an in-memory node for testing. +// It returns the node instance and its RPC remote address. +func TestingInMemoryNode(t TestingTS, logger *slog.Logger, config *gnoland.InMemoryNodeConfig) (*node.Node, string) { + node, err := gnoland.NewInMemoryNode(logger, config) + require.NoError(t, err) + + err = node.Start() + require.NoError(t, err) + + select { + case <-node.Ready(): + case <-time.After(time.Second * 10): + require.FailNow(t, "timeout while waiting for the node to start") + } + + return node, node.Config().RPC.ListenAddress +} + +// TestingNodeConfig constructs an in-memory node configuration +// with default packages and genesis transactions already loaded. +// It will return the default creator address of the loaded packages. +func TestingNodeConfig(t TestingTS, gnoroot string) (*gnoland.InMemoryNodeConfig, bft.Address) { + cfg := TestingMinimalNodeConfig(t, gnoroot) + + creator := crypto.MustAddressFromString(DefaultAccount_Address) // test1 + + balances := LoadDefaultGenesisBalanceFile(t, gnoroot) + txs := []std.Tx{} + txs = append(txs, LoadDefaultPackages(t, creator, gnoroot)...) + txs = append(txs, LoadDefaultGenesisTXsFile(t, cfg.Genesis.ChainID, gnoroot)...) + + cfg.Genesis.AppState = gnoland.GnoGenesisState{ + Balances: balances, + Txs: txs, + } + + return cfg, creator +} + +// TestingMinimalNodeConfig constructs the default minimal in-memory node configuration for testing. +func TestingMinimalNodeConfig(t TestingTS, gnoroot string) *gnoland.InMemoryNodeConfig { + tmconfig := DefaultTestingTMConfig(gnoroot) + + // Create Mocked Identity + pv := gnoland.NewMockedPrivValidator() + + // Generate genesis config + genesis := DefaultTestingGenesisConfig(t, gnoroot, pv.GetPubKey(), tmconfig) + + return &gnoland.InMemoryNodeConfig{ + PrivValidator: pv, + Genesis: genesis, + TMConfig: tmconfig, + } +} + +func DefaultTestingGenesisConfig(t TestingTS, gnoroot string, self crypto.PubKey, tmconfig *tmcfg.Config) *bft.GenesisDoc { + return &bft.GenesisDoc{ + GenesisTime: time.Now(), + ChainID: tmconfig.ChainID(), + ConsensusParams: abci.ConsensusParams{ + Block: &abci.BlockParams{ + MaxTxBytes: 1_000_000, // 1MB, + MaxDataBytes: 2_000_000, // 2MB, + MaxGas: 10_0000_000, // 10M gas + TimeIotaMS: 100, // 100ms + }, + }, + Validators: []bft.GenesisValidator{ + { + Address: self.Address(), + PubKey: self, + Power: 10, + Name: "self", + }, + }, + AppState: gnoland.GnoGenesisState{ + Balances: []gnoland.Balance{ + { + Address: crypto.MustAddressFromString(DefaultAccount_Address), + Amount: std.MustParseCoins("10000000000000ugnot"), + }, + }, + Txs: []std.Tx{}, + }, + } +} + +// LoadDefaultPackages loads the default packages for testing using a given creator address and gnoroot directory. +func LoadDefaultPackages(t TestingTS, creator bft.Address, gnoroot string) []std.Tx { + examplesDir := filepath.Join(gnoroot, "examples") + + defaultFee := std.NewFee(50000, std.MustParseCoin("1000000ugnot")) + txs, err := gnoland.LoadPackagesFromDir(examplesDir, creator, defaultFee, nil) + require.NoError(t, err) + + return txs +} + +// LoadDefaultGenesisBalanceFile loads the default genesis balance file for testing. +func LoadDefaultGenesisBalanceFile(t TestingTS, gnoroot string) []gnoland.Balance { + balanceFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_balances.txt") + + genesisBalances, err := gnoland.LoadGenesisBalancesFile(balanceFile) + require.NoError(t, err) + + return genesisBalances +} + +// LoadDefaultGenesisTXsFile loads the default genesis transactions file for testing. +func LoadDefaultGenesisTXsFile(t TestingTS, chainid string, gnoroot string) []std.Tx { + txsFile := filepath.Join(gnoroot, "gno.land", "genesis", "genesis_txs.txt") + + // NOTE: We dont care about giving a correct address here, as it's only for display + // XXX: Do we care loading this TXs for testing ? + genesisTXs, err := gnoland.LoadGenesisTxsFile(txsFile, chainid, "https://127.0.0.1:26657") + require.NoError(t, err) + + return genesisTXs +} + +// DefaultTestingTMConfig constructs the default Tendermint configuration for testing. +func DefaultTestingTMConfig(gnoroot string) *tmcfg.Config { + const defaultListner = "tcp://127.0.0.1:0" + + tmconfig := tmcfg.TestConfig().SetRootDir(gnoroot) + tmconfig.Consensus.CreateEmptyBlocks = true + tmconfig.Consensus.CreateEmptyBlocksInterval = time.Duration(0) + tmconfig.RPC.ListenAddress = defaultListner + tmconfig.P2P.ListenAddress = defaultListner + return tmconfig +} diff --git a/gno.land/pkg/keyscli/README.md b/gno.land/pkg/keyscli/README.md new file mode 100644 index 00000000000..a6d27fa4d40 --- /dev/null +++ b/gno.land/pkg/keyscli/README.md @@ -0,0 +1,12 @@ +## keycli + +`keycli` is an extension of `tm2/keys/client`, enhancing its functionality. It provides the following features: + +- **addpkg**: Allows you to upload a new package to the blockchain. +- **run**: Execute Gno code by invoking the main() function from the target package. +- **call**: Executes a single function call within a Realm. +- **maketx**: Compose a transaction (tx) document to sign (and possibly broadcast). + +--- + +Most of these features have been extracted from `tm2/keys/client` to ensure that `tm2` remains completely independent of `gnovm` and `gno.land`. For more detailed information regarding this change, please refer to [PR#1483](https://github.com/gnolang/gno/pull/1483) diff --git a/gno.land/pkg/keyscli/addpkg.go b/gno.land/pkg/keyscli/addpkg.go new file mode 100644 index 00000000000..1882f6de3d0 --- /dev/null +++ b/gno.land/pkg/keyscli/addpkg.go @@ -0,0 +1,138 @@ +package keyscli + +import ( + "context" + "flag" + "fmt" + + "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" + "github.com/gnolang/gno/tm2/pkg/errors" + "github.com/gnolang/gno/tm2/pkg/std" +) + +type MakeAddPkgCfg struct { + RootCfg *client.MakeTxCfg + + PkgPath string + PkgDir string + Deposit string +} + +func NewMakeAddPkgCmd(rootCfg *client.MakeTxCfg, io commands.IO) *commands.Command { + cfg := &MakeAddPkgCfg{ + RootCfg: rootCfg, + } + + return commands.NewCommand( + commands.Metadata{ + Name: "addpkg", + ShortUsage: "addpkg [flags] ", + ShortHelp: "Uploads a new package", + }, + cfg, + func(_ context.Context, args []string) error { + return execMakeAddPkg(cfg, args, io) + }, + ) +} + +func (c *MakeAddPkgCfg) RegisterFlags(fs *flag.FlagSet) { + fs.StringVar( + &c.PkgPath, + "pkgpath", + "", + "package path (required)", + ) + + fs.StringVar( + &c.PkgDir, + "pkgdir", + "", + "path to package files (required)", + ) + + fs.StringVar( + &c.Deposit, + "deposit", + "", + "deposit coins", + ) +} + +func execMakeAddPkg(cfg *MakeAddPkgCfg, args []string, io commands.IO) error { + if cfg.PkgPath == "" { + return errors.New("pkgpath not specified") + } + if cfg.PkgDir == "" { + return errors.New("pkgdir not specified") + } + + if len(args) != 1 { + return flag.ErrHelp + } + + // read account pubkey. + nameOrBech32 := args[0] + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.RootCfg.Home) + if err != nil { + return err + } + info, err := kb.GetByNameOrAddress(nameOrBech32) + if err != nil { + return err + } + creator := info.GetAddress() + // info.GetPubKey() + + // parse deposit. + deposit, err := std.ParseCoins(cfg.Deposit) + if err != nil { + panic(err) + } + + // open files in directory as MemPackage. + memPkg := gno.ReadMemPackage(cfg.PkgDir, cfg.PkgPath) + if memPkg.IsEmpty() { + panic(fmt.Sprintf("found an empty package %q", cfg.PkgPath)) + } + + // precompile and validate syntax + err = gno.PrecompileAndCheckMempkg(memPkg) + if err != nil { + panic(err) + } + + // parse gas wanted & fee. + gaswanted := cfg.RootCfg.GasWanted + gasfee, err := std.ParseCoin(cfg.RootCfg.GasFee) + if err != nil { + panic(err) + } + // construct msg & tx and marshal. + msg := vm.MsgAddPackage{ + Creator: creator, + Package: memPkg, + Deposit: deposit, + } + tx := std.Tx{ + Msgs: []std.Msg{msg}, + Fee: std.NewFee(gaswanted, gasfee), + Signatures: nil, + Memo: cfg.RootCfg.Memo, + } + + if cfg.RootCfg.Broadcast { + err := client.ExecSignAndBroadcast(cfg.RootCfg, args, tx, io) + if err != nil { + return err + } + } else { + fmt.Println(string(amino.MustMarshalJSON(tx))) + } + return nil +} diff --git a/tm2/pkg/crypto/keys/client/call.go b/gno.land/pkg/keyscli/call.go similarity index 63% rename from tm2/pkg/crypto/keys/client/call.go rename to gno.land/pkg/keyscli/call.go index bcb7be3e550..5afbdf457b8 100644 --- a/tm2/pkg/crypto/keys/client/call.go +++ b/gno.land/pkg/keyscli/call.go @@ -1,4 +1,4 @@ -package client +package keyscli import ( "context" @@ -9,22 +9,23 @@ import ( "github.com/gnolang/gno/tm2/pkg/amino" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" "github.com/gnolang/gno/tm2/pkg/errors" "github.com/gnolang/gno/tm2/pkg/std" ) -type callCfg struct { - rootCfg *makeTxCfg +type MakeCallCfg struct { + RootCfg *client.MakeTxCfg - send string - pkgPath string - funcName string - args commands.StringArr + Send string + PkgPath string + FuncName string + Args commands.StringArr } -func newCallCmd(rootCfg *makeTxCfg) *commands.Command { - cfg := &callCfg{ - rootCfg: rootCfg, +func NewMakeCallCmd(rootCfg *client.MakeTxCfg, io commands.IO) *commands.Command { + cfg := &MakeCallCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -35,63 +36,63 @@ func newCallCmd(rootCfg *makeTxCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execCall(cfg, args, commands.NewDefaultIO()) + return execMakeCall(cfg, args, io) }, ) } -func (c *callCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *MakeCallCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( - &c.send, + &c.Send, "send", "", "send amount", ) fs.StringVar( - &c.pkgPath, + &c.PkgPath, "pkgpath", "", "package path (required)", ) fs.StringVar( - &c.funcName, + &c.FuncName, "func", "", "contract to call (required)", ) fs.Var( - &c.args, + &c.Args, "args", "arguments to contract", ) } -func execCall(cfg *callCfg, args []string, io *commands.IO) error { - if cfg.pkgPath == "" { +func execMakeCall(cfg *MakeCallCfg, args []string, io commands.IO) error { + if cfg.PkgPath == "" { return errors.New("pkgpath not specified") } - if cfg.funcName == "" { + if cfg.FuncName == "" { return errors.New("func not specified") } if len(args) != 1 { return flag.ErrHelp } - if cfg.rootCfg.gasWanted == 0 { + if cfg.RootCfg.GasWanted == 0 { return errors.New("gas-wanted not specified") } - if cfg.rootCfg.gasFee == "" { + if cfg.RootCfg.GasFee == "" { return errors.New("gas-fee not specified") } // read statement. - fnc := cfg.funcName + fnc := cfg.FuncName // read account pubkey. nameOrBech32 := args[0] - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.rootCfg.Home) + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.RootCfg.Home) if err != nil { return err } @@ -103,14 +104,14 @@ func execCall(cfg *callCfg, args []string, io *commands.IO) error { // info.GetPubKey() // Parse send amount. - send, err := std.ParseCoins(cfg.send) + send, err := std.ParseCoins(cfg.Send) if err != nil { return errors.Wrap(err, "parsing send coins") } // parse gas wanted & fee. - gaswanted := cfg.rootCfg.gasWanted - gasfee, err := std.ParseCoin(cfg.rootCfg.gasFee) + gaswanted := cfg.RootCfg.GasWanted + gasfee, err := std.ParseCoin(cfg.RootCfg.GasFee) if err != nil { return errors.Wrap(err, "parsing gas fee coin") } @@ -119,19 +120,19 @@ func execCall(cfg *callCfg, args []string, io *commands.IO) error { msg := vm.MsgCall{ Caller: caller, Send: send, - PkgPath: cfg.pkgPath, + PkgPath: cfg.PkgPath, Func: fnc, - Args: cfg.args, + Args: cfg.Args, } tx := std.Tx{ Msgs: []std.Msg{msg}, Fee: std.NewFee(gaswanted, gasfee), Signatures: nil, - Memo: cfg.rootCfg.memo, + Memo: cfg.RootCfg.Memo, } - if cfg.rootCfg.broadcast { - err := signAndBroadcast(cfg.rootCfg, args, tx, io) + if cfg.RootCfg.Broadcast { + err := client.ExecSignAndBroadcast(cfg.RootCfg, args, tx, io) if err != nil { return err } diff --git a/gno.land/pkg/keyscli/maketx.go b/gno.land/pkg/keyscli/maketx.go new file mode 100644 index 00000000000..3aa54546863 --- /dev/null +++ b/gno.land/pkg/keyscli/maketx.go @@ -0,0 +1,83 @@ +package keyscli + +import ( + "flag" + + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" +) + +type MakeTxCfg struct { + RootCfg *client.BaseCfg + + GasWanted int64 + GasFee string + Memo string + + Broadcast bool + ChainID string +} + +func NewMakeTxCmd(rootCfg *client.BaseCfg, io commands.IO) *commands.Command { + cfg := &client.MakeTxCfg{ + RootCfg: rootCfg, + } + + cmd := commands.NewCommand( + commands.Metadata{ + Name: "maketx", + ShortUsage: " [flags] [...]", + ShortHelp: "Composes a tx document to sign", + }, + cfg, + commands.HelpExec, + ) + + cmd.AddSubCommands( + client.NewMakeSendCmd(cfg, io), + + // custom commands + NewMakeAddPkgCmd(cfg, io), + NewMakeCallCmd(cfg, io), + NewMakeRunCmd(cfg, io), + ) + + return cmd +} + +func (c *MakeTxCfg) RegisterFlags(fs *flag.FlagSet) { + fs.Int64Var( + &c.GasWanted, + "gas-wanted", + 0, + "gas requested for tx", + ) + + fs.StringVar( + &c.GasFee, + "gas-fee", + "", + "gas payment fee", + ) + + fs.StringVar( + &c.Memo, + "memo", + "", + "any descriptive text", + ) + + fs.BoolVar( + &c.Broadcast, + "broadcast", + false, + "sign and broadcast", + ) + + fs.StringVar( + &c.ChainID, + "chainid", + "dev", + "chainid to sign for (only useful if --broadcast)", + ) +} diff --git a/gno.land/pkg/keyscli/root.go b/gno.land/pkg/keyscli/root.go new file mode 100644 index 00000000000..dc5a4f1f9af --- /dev/null +++ b/gno.land/pkg/keyscli/root.go @@ -0,0 +1,47 @@ +// Dedicated to my love, Lexi. +package keyscli + +import ( + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" + + "github.com/peterbourgon/ff/v3" + "github.com/peterbourgon/ff/v3/fftoml" +) + +func NewRootCmd(io commands.IO, base client.BaseOptions) *commands.Command { + cfg := &client.BaseCfg{ + BaseOptions: base, + } + + cmd := commands.NewCommand( + commands.Metadata{ + ShortUsage: " [flags] [...]", + LongHelp: "Manages private keys for the node", + Options: []ff.Option{ + ff.WithConfigFileFlag("config"), + ff.WithConfigFileParser(fftoml.Parser), + }, + }, + cfg, + commands.HelpExec, + ) + + cmd.AddSubCommands( + client.NewAddCmd(cfg, io), + client.NewDeleteCmd(cfg, io), + client.NewGenerateCmd(cfg, io), + client.NewExportCmd(cfg, io), + client.NewImportCmd(cfg, io), + client.NewListCmd(cfg, io), + client.NewSignCmd(cfg, io), + client.NewVerifyCmd(cfg, io), + client.NewQueryCmd(cfg, io), + client.NewBroadcastCmd(cfg, io), + + // Custom MakeTX command + NewMakeTxCmd(cfg, io), + ) + + return cmd +} diff --git a/gno.land/pkg/keyscli/run.go b/gno.land/pkg/keyscli/run.go new file mode 100644 index 00000000000..7d329c18566 --- /dev/null +++ b/gno.land/pkg/keyscli/run.go @@ -0,0 +1,141 @@ +package keyscli + +import ( + "context" + "flag" + "fmt" + "io" + "os" + + "github.com/gnolang/gno/gno.land/pkg/sdk/vm" + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" + "github.com/gnolang/gno/tm2/pkg/errors" + "github.com/gnolang/gno/tm2/pkg/std" +) + +type MakeRunCfg struct { + RootCfg *client.MakeTxCfg +} + +func NewMakeRunCmd(rootCfg *client.MakeTxCfg, cmdio commands.IO) *commands.Command { + cfg := &MakeRunCfg{ + RootCfg: rootCfg, + } + + return commands.NewCommand( + commands.Metadata{ + Name: "run", + ShortUsage: "run [flags] ", + ShortHelp: "Runs Gno code by invoking main() in a package", + }, + cfg, + func(_ context.Context, args []string) error { + return execMakeRun(cfg, args, cmdio) + }, + ) +} + +func (c *MakeRunCfg) RegisterFlags(fs *flag.FlagSet) {} + +func execMakeRun(cfg *MakeRunCfg, args []string, cmdio commands.IO) error { + if len(args) != 2 { + return flag.ErrHelp + } + if cfg.RootCfg.GasWanted == 0 { + return errors.New("gas-wanted not specified") + } + if cfg.RootCfg.GasFee == "" { + return errors.New("gas-fee not specified") + } + + nameOrBech32 := args[0] + sourcePath := args[1] // can be a file path, a dir path, or '-' for stdin + + // read account pubkey. + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.RootCfg.Home) + if err != nil { + return err + } + info, err := kb.GetByNameOrAddress(nameOrBech32) + if err != nil { + return err + } + caller := info.GetAddress() + + // parse gas wanted & fee. + gaswanted := cfg.RootCfg.GasWanted + gasfee, err := std.ParseCoin(cfg.RootCfg.GasFee) + if err != nil { + return errors.Wrap(err, "parsing gas fee coin") + } + + memPkg := &std.MemPackage{} + if sourcePath == "-" { // stdin + data, err := io.ReadAll(cmdio.In()) + if err != nil { + return fmt.Errorf("could not read stdin: %w", err) + } + memPkg.Files = []*std.MemFile{ + { + Name: "stdin.gno", + Body: string(data), + }, + } + } else { + info, err := os.Stat(sourcePath) + if err != nil { + return fmt.Errorf("could not read source path: %q, %w", sourcePath, err) + } + if info.IsDir() { + memPkg = gno.ReadMemPackage(sourcePath, "") + } else { // is file + b, err := os.ReadFile(sourcePath) + if err != nil { + return fmt.Errorf("could not read %q: %w", sourcePath, err) + } + memPkg.Files = []*std.MemFile{ + { + Name: info.Name(), + Body: string(b), + }, + } + } + } + if memPkg.IsEmpty() { + panic(fmt.Sprintf("found an empty package %q", memPkg.Path)) + } + // precompile and validate syntax + err = gno.PrecompileAndCheckMempkg(memPkg) + if err != nil { + panic(err) + } + memPkg.Name = "main" + // Set to empty; this will be automatically set by the VM keeper. + memPkg.Path = "" + + // construct msg & tx and marshal. + msg := vm.MsgRun{ + Caller: caller, + Package: memPkg, + } + tx := std.Tx{ + Msgs: []std.Msg{msg}, + Fee: std.NewFee(gaswanted, gasfee), + Signatures: nil, + Memo: cfg.RootCfg.Memo, + } + + if cfg.RootCfg.Broadcast { + err := client.ExecSignAndBroadcast(cfg.RootCfg, args, tx, cmdio) + if err != nil { + return err + } + } else { + fmt.Println(string(amino.MustMarshalJSON(tx))) + } + return nil +} diff --git a/gno.land/pkg/log/format.go b/gno.land/pkg/log/format.go new file mode 100644 index 00000000000..1272ffe23b6 --- /dev/null +++ b/gno.land/pkg/log/format.go @@ -0,0 +1,14 @@ +package log + +// Format is the log format +type Format string + +const ( + JSONFormat Format = "json" + ConsoleFormat Format = "console" + TestingFormat Format = "testing" +) + +func (f Format) String() string { + return string(f) +} diff --git a/gno.land/pkg/log/zap.go b/gno.land/pkg/log/zap.go new file mode 100644 index 00000000000..a68e034eab6 --- /dev/null +++ b/gno.land/pkg/log/zap.go @@ -0,0 +1,76 @@ +package log + +import ( + "io" + + "golang.org/x/exp/slog" + + "go.uber.org/zap" + "go.uber.org/zap/exp/zapslog" + "go.uber.org/zap/zapcore" +) + +// NewZapLoggerFn is the zap logger init declaration +type NewZapLoggerFn func(w io.Writer, level zapcore.Level, opts ...zap.Option) *zap.Logger + +// GetZapLoggerFn returns the appropriate init callback +// for the zap logger, given the requested format +func GetZapLoggerFn(format Format) NewZapLoggerFn { + switch format { + case JSONFormat: + return NewZapJSONLogger + case TestingFormat: + return NewZapTestingLogger + default: + return NewZapConsoleLogger + } +} + +// NewZapJSONLogger creates a zap logger with a JSON encoder for production use. +func NewZapJSONLogger(w io.Writer, level zapcore.Level, opts ...zap.Option) *zap.Logger { + // Build encoder config + jsonConfig := zap.NewProductionEncoderConfig() + + // Build encoder + enc := zapcore.NewJSONEncoder(jsonConfig) + return NewZapLogger(enc, w, level, opts...) +} + +// NewZapConsoleLogger creates a zap logger with a console encoder for development use. +func NewZapConsoleLogger(w io.Writer, level zapcore.Level, opts ...zap.Option) *zap.Logger { + // Build encoder config + consoleConfig := zap.NewDevelopmentEncoderConfig() + consoleConfig.EncodeLevel = stableWidthCapitalColorLevelEncoder + consoleConfig.EncodeName = stableWidthNameEncoder + + // Build encoder + enc := zapcore.NewConsoleEncoder(consoleConfig) + return NewZapLogger(enc, w, level, opts...) +} + +// NewZapTestingLogger creates a zap logger with a console encoder optimized for testing. +func NewZapTestingLogger(w io.Writer, level zapcore.Level, opts ...zap.Option) *zap.Logger { + // Build encoder config + consoleConfig := zap.NewDevelopmentEncoderConfig() + consoleConfig.TimeKey = "" + consoleConfig.EncodeLevel = stableWidthCapitalLevelEncoder + consoleConfig.EncodeName = stableWidthNameEncoder + + // Build encoder + enc := zapcore.NewConsoleEncoder(consoleConfig) + return NewZapLogger(enc, w, level, opts...) +} + +// NewZapLogger creates a new zap logger instance, for the given level, writer and zap encoder. +func NewZapLogger(enc zapcore.Encoder, w io.Writer, level zapcore.Level, opts ...zap.Option) *zap.Logger { + ws := zapcore.AddSync(w) + + // Create zap core + core := zapcore.NewCore(enc, ws, zap.NewAtomicLevelAt(level)) + return zap.New(core, opts...) +} + +// ZapLoggerToSlog wraps the given zap logger to an log/slog Logger +func ZapLoggerToSlog(logger *zap.Logger) *slog.Logger { + return slog.New(zapslog.NewHandler(logger.Core())) +} diff --git a/gno.land/pkg/log/zap_encoder.go b/gno.land/pkg/log/zap_encoder.go new file mode 100644 index 00000000000..a8459197301 --- /dev/null +++ b/gno.land/pkg/log/zap_encoder.go @@ -0,0 +1,48 @@ +package log + +import ( + "fmt" + + "go.uber.org/zap/zapcore" +) + +func stableWidthCapitalLevelEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(fmt.Sprintf("%-5s", l.CapitalString())) +} + +func stableWidthNameEncoder(loggerName string, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(fmt.Sprintf("%-18s", loggerName)) +} + +//nolint:varcheck,deadcode // we don't care if it's unused +const ( + black uint8 = iota + 30 + red + green + yellow + blue + magenta + cyan + white +) + +func stableWidthCapitalColorLevelEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { + switch l { + case zapcore.DebugLevel: + enc.AppendString(fmt.Sprintf("\x1b[%dm%s\x1b[0m", magenta, "DEBUG")) + case zapcore.InfoLevel: + enc.AppendString(fmt.Sprintf("\x1b[%dm%s\x1b[0m", blue, "INFO ")) + case zapcore.WarnLevel: + enc.AppendString(fmt.Sprintf("\x1b[%dm%s\x1b[0m", yellow, "WARN ")) + case zapcore.ErrorLevel: + enc.AppendString(fmt.Sprintf("\x1b[%dm%s\x1b[0m", red, "ERROR")) + case zapcore.DPanicLevel: + enc.AppendString(fmt.Sprintf("\x1b[%dm%s\x1b[0m", red, "DPANIC")) + case zapcore.PanicLevel: + enc.AppendString(fmt.Sprintf("\x1b[%dm%s\x1b[0m", red, "PANIC")) + case zapcore.FatalLevel: + enc.AppendString(fmt.Sprintf("\x1b[%dm%s\x1b[0m", red, "FATAL")) + default: + enc.AppendString(fmt.Sprintf("\x1b[%dm%s\x1b[0m", red, l.CapitalString())) + } +} diff --git a/gno.land/pkg/sdk/vm/builtins.go b/gno.land/pkg/sdk/vm/builtins.go index 270a4f7c37e..ef2ac93f617 100644 --- a/gno.land/pkg/sdk/vm/builtins.go +++ b/gno.land/pkg/sdk/vm/builtins.go @@ -40,47 +40,10 @@ func (vm *VMKeeper) initBuiltinPackagesAndTypes(store gno.Store) { return m2.RunMemPackage(memPkg, true) } store.SetPackageGetter(getPackage) - store.SetPackageInjector(vm.packageInjector) + store.SetNativeStore(stdlibs.NativeStore) stdlibs.InjectNativeMappings(store) } -func (vm *VMKeeper) packageInjector(store gno.Store, pn *gno.PackageNode) { - // Also inject stdlibs native functions. - stdlibs.InjectPackage(store, pn) - // vm (this package) specific injections: - switch pn.PkgPath { - case "std": - /* XXX deleteme - // Also see stdlibs/InjectPackage. - pn.DefineNative("AssertOriginCall", - gno.Flds( // params - ), - gno.Flds( // results - ), - func(m *gno.Machine) { - isOrigin := len(m.Frames) == 2 - if !isOrigin { - panic("invalid non-origin call") - } - }, - ) - pn.DefineNative("IsOriginCall", - gno.Flds( // params - ), - gno.Flds( // results - "isOrigin", "bool", - ), - func(m *gno.Machine) { - isOrigin := len(m.Frames) == 2 - res0 := gno.TypedValue{T: gno.BoolType} - res0.SetBool(isOrigin) - m.PushValue(res0) - }, - ) - */ - } -} - // ---------------------------------------- // SDKBanker diff --git a/gno.land/pkg/sdk/vm/common_test.go b/gno.land/pkg/sdk/vm/common_test.go index 60a92906cb6..8a75697115f 100644 --- a/gno.land/pkg/sdk/vm/common_test.go +++ b/gno.land/pkg/sdk/vm/common_test.go @@ -35,7 +35,7 @@ func setupTestEnv() testEnv { ms.MountStoreWithDB(iavlCapKey, iavl.StoreConstructor, db) ms.LoadLatestVersion() - ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{ChainID: "test-chain-id"}, log.NewNopLogger()) + ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{ChainID: "test-chain-id"}, log.NewNoopLogger()) acck := authm.NewAccountKeeper(iavlCapKey, std.ProtoBaseAccount) bank := bankm.NewBankKeeper(acck) stdlibsDir := filepath.Join("..", "..", "..", "..", "gnovm", "stdlibs") diff --git a/gno.land/pkg/sdk/vm/convert.go b/gno.land/pkg/sdk/vm/convert.go index de4db67fb04..f70f99403a8 100644 --- a/gno.land/pkg/sdk/vm/convert.go +++ b/gno.land/pkg/sdk/vm/convert.go @@ -5,9 +5,16 @@ import ( "fmt" "strconv" + "github.com/cockroachdb/apd/v3" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" ) +func assertCharNotPlus(b byte) { + if b == '+' { + panic("numbers cannot start with +") + } +} + // These convert string representations of public-facing arguments to GNO types. // The limited set of input types available should map 1:1 to types supported // in FunctionSignature{}. @@ -34,9 +41,7 @@ func convertArgToGno(arg string, argT gno.Type) (tv gno.TypedValue) { tv.SetString(gno.StringValue(arg)) return case gno.IntType: - if arg[0] == '+' { - panic("numbers cannot start with +") - } + assertCharNotPlus(arg[0]) i64, err := strconv.ParseInt(arg, 10, 64) if err != nil { panic(fmt.Sprintf( @@ -46,9 +51,7 @@ func convertArgToGno(arg string, argT gno.Type) (tv gno.TypedValue) { tv.SetInt(int(i64)) return case gno.Int8Type: - if arg[0] == '+' { - panic("numbers cannot start with +") - } + assertCharNotPlus(arg[0]) i8, err := strconv.ParseInt(arg, 10, 8) if err != nil { panic(fmt.Sprintf( @@ -58,9 +61,7 @@ func convertArgToGno(arg string, argT gno.Type) (tv gno.TypedValue) { tv.SetInt8(int8(i8)) return case gno.Int16Type: - if arg[0] == '+' { - panic("numbers cannot start with +") - } + assertCharNotPlus(arg[0]) i16, err := strconv.ParseInt(arg, 10, 16) if err != nil { panic(fmt.Sprintf( @@ -70,9 +71,7 @@ func convertArgToGno(arg string, argT gno.Type) (tv gno.TypedValue) { tv.SetInt16(int16(i16)) return case gno.Int32Type: - if arg[0] == '+' { - panic("numbers cannot start with +") - } + assertCharNotPlus(arg[0]) i32, err := strconv.ParseInt(arg, 10, 32) if err != nil { panic(fmt.Sprintf( @@ -82,9 +81,7 @@ func convertArgToGno(arg string, argT gno.Type) (tv gno.TypedValue) { tv.SetInt32(int32(i32)) return case gno.Int64Type: - if arg[0] == '+' { - panic("numbers cannot start with +") - } + assertCharNotPlus(arg[0]) i64, err := strconv.ParseInt(arg, 10, 64) if err != nil { panic(fmt.Sprintf( @@ -94,9 +91,7 @@ func convertArgToGno(arg string, argT gno.Type) (tv gno.TypedValue) { tv.SetInt64(i64) return case gno.UintType: - if arg[0] == '+' { - panic("numbers cannot start with +") - } + assertCharNotPlus(arg[0]) u64, err := strconv.ParseUint(arg, 10, 64) if err != nil { panic(fmt.Sprintf( @@ -106,9 +101,7 @@ func convertArgToGno(arg string, argT gno.Type) (tv gno.TypedValue) { tv.SetUint(uint(u64)) return case gno.Uint8Type: - if arg[0] == '+' { - panic("numbers cannot start with +") - } + assertCharNotPlus(arg[0]) u8, err := strconv.ParseUint(arg, 10, 8) if err != nil { panic(fmt.Sprintf( @@ -118,9 +111,7 @@ func convertArgToGno(arg string, argT gno.Type) (tv gno.TypedValue) { tv.SetUint8(uint8(u8)) return case gno.Uint16Type: - if arg[0] == '+' { - panic("numbers cannot start with +") - } + assertCharNotPlus(arg[0]) u16, err := strconv.ParseUint(arg, 10, 16) if err != nil { panic(fmt.Sprintf( @@ -130,9 +121,7 @@ func convertArgToGno(arg string, argT gno.Type) (tv gno.TypedValue) { tv.SetUint16(uint16(u16)) return case gno.Uint32Type: - if arg[0] == '+' { - panic("numbers cannot start with +") - } + assertCharNotPlus(arg[0]) u32, err := strconv.ParseUint(arg, 10, 32) if err != nil { panic(fmt.Sprintf( @@ -142,9 +131,7 @@ func convertArgToGno(arg string, argT gno.Type) (tv gno.TypedValue) { tv.SetUint32(uint32(u32)) return case gno.Uint64Type: - if arg[0] == '+' { - panic("numbers cannot start with +") - } + assertCharNotPlus(arg[0]) u64, err := strconv.ParseUint(arg, 10, 64) if err != nil { panic(fmt.Sprintf( @@ -153,6 +140,14 @@ func convertArgToGno(arg string, argT gno.Type) (tv gno.TypedValue) { } tv.SetUint64(u64) return + case gno.Float32Type: + value := convertFloat(arg, 32) + tv.SetFloat32(float32(value)) + return + case gno.Float64Type: + value := convertFloat(arg, 64) + tv.SetFloat64(value) + return default: panic(fmt.Sprintf("unexpected primitive type %s", bt.String())) } @@ -195,3 +190,18 @@ func convertArgToGno(arg string, argT gno.Type) (tv gno.TypedValue) { panic(fmt.Sprintf("unexpected type in contract arg: %v", argT)) } } + +func convertFloat(value string, precision int) float64 { + assertCharNotPlus(value[0]) + dec, _, err := apd.NewFromString(value) + if err != nil { + panic(fmt.Sprintf("error parsing float%d %s: %v", precision, value, err)) + } + + f64, err := strconv.ParseFloat(dec.String(), precision) + if err != nil { + panic(fmt.Sprintf("error value exceeds float%d precision %s: %v", precision, value, err)) + } + + return f64 +} diff --git a/gno.land/pkg/sdk/vm/handler.go b/gno.land/pkg/sdk/vm/handler.go index accaa70e059..6c3a97696d6 100644 --- a/gno.land/pkg/sdk/vm/handler.go +++ b/gno.land/pkg/sdk/vm/handler.go @@ -27,6 +27,8 @@ func (vh vmHandler) Process(ctx sdk.Context, msg std.Msg) sdk.Result { return vh.handleMsgAddPackage(ctx, msg) case MsgCall: return vh.handleMsgCall(ctx, msg) + case MsgRun: + return vh.handleMsgRun(ctx, msg) default: errMsg := fmt.Sprintf("unrecognized vm message type: %T", msg) return abciResult(std.ErrUnknownRequest(errMsg)) @@ -77,6 +79,25 @@ func (vh vmHandler) handleMsgCall(ctx sdk.Context, msg MsgCall) (res sdk.Result) */ } +// Handle MsgRun. +func (vh vmHandler) handleMsgRun(ctx sdk.Context, msg MsgRun) (res sdk.Result) { + amount, err := std.ParseCoins("1000000ugnot") // XXX calculate + if err != nil { + return abciResult(err) + } + err = vh.vm.bank.SendCoins(ctx, msg.Caller, auth.FeeCollectorAddress(), amount) + if err != nil { + return abciResult(err) + } + resstr := "" + resstr, err = vh.vm.Run(ctx, msg) + if err != nil { + return abciResult(err) + } + res.Data = []byte(resstr) + return +} + //---------------------------------------- // Query diff --git a/gno.land/pkg/sdk/vm/keeper.go b/gno.land/pkg/sdk/vm/keeper.go index 28531f0a773..54f424ee058 100644 --- a/gno.land/pkg/sdk/vm/keeper.go +++ b/gno.land/pkg/sdk/vm/keeper.go @@ -3,8 +3,10 @@ package vm // TODO: move most of the logic in ROOT/gno.land/... import ( + "bytes" "fmt" "os" + "regexp" "strings" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" @@ -27,6 +29,7 @@ const ( type VMKeeperI interface { AddPackage(ctx sdk.Context, msg MsgAddPackage) error Call(ctx sdk.Context, msg MsgCall) (res string, err error) + Run(ctx sdk.Context, msg MsgRun) (res string, err error) } var _ VMKeeperI = &VMKeeper{} @@ -128,6 +131,8 @@ func (vm *VMKeeper) getGnoStore(ctx sdk.Context) gno.Store { } } +var reRunPath = regexp.MustCompile(`gno\.land/r/g[a-z0-9]+/run`) + // AddPackage adds a package with given fileset. func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) error { creator := msg.Creator @@ -150,6 +155,11 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) error { if pv := store.GetPackage(pkgPath, false); pv != nil { return ErrInvalidPkgPath("package already exists: " + pkgPath) } + + if reRunPath.MatchString(pkgPath) { + return ErrInvalidPkgPath("reserved package name: " + pkgPath) + } + // Pay deposit from creator. pkgAddr := gno.DerivePkgAddr(pkgPath) @@ -188,7 +198,8 @@ func (vm *VMKeeper) AddPackage(ctx sdk.Context, msg MsgAddPackage) error { }) defer m2.Release() m2.RunMemPackage(memPkg, true) - fmt.Println("CPUCYCLES addpkg", m2.Cycles) + + ctx.Logger().Info("CPUCYCLES", "addpkg", m2.Cycles) return nil } @@ -229,6 +240,9 @@ func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) { if cx.Varg { panic("variadic calls not yet supported") } + if len(msg.Args) != len(ft.Params) { + panic(fmt.Sprintf("wrong number of arguments in call to %s: want %d got %d", fnc, len(ft.Params), len(msg.Args))) + } for i, arg := range msg.Args { argType := ft.Params[i].Type atv := convertArgToGno(arg, argType) @@ -270,7 +284,7 @@ func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) { m.Release() }() rtvs := m.Eval(xn) - fmt.Println("CPUCYCLES call", m.Cycles) + ctx.Logger().Info("CPUCYCLES call", "num-cycles", m.Cycles) for i, rtv := range rtvs { res = res + rtv.String() if i < len(rtvs)-1 { @@ -281,6 +295,87 @@ func (vm *VMKeeper) Call(ctx sdk.Context, msg MsgCall) (res string, err error) { // TODO pay for gas? TODO see context? } +// Run executes arbitrary Gno code in the context of the caller's realm. +func (vm *VMKeeper) Run(ctx sdk.Context, msg MsgRun) (res string, err error) { + caller := msg.Caller + pkgAddr := caller + store := vm.getGnoStore(ctx) + send := msg.Send + memPkg := msg.Package + + // coerce path to right one. + // the path in the message must be "" or the following path. + // this is already checked in MsgRun.ValidateBasic + memPkg.Path = "gno.land/r/" + msg.Caller.String() + "/run" + + // Validate arguments. + callerAcc := vm.acck.GetAccount(ctx, caller) + if callerAcc == nil { + return "", std.ErrUnknownAddress(fmt.Sprintf("account %s does not exist", caller)) + } + if err := msg.Package.Validate(); err != nil { + return "", ErrInvalidPkgPath(err.Error()) + } + + // Send send-coins to pkg from caller. + err = vm.bank.SendCoins(ctx, caller, pkgAddr, send) + if err != nil { + return "", err + } + + // Parse and run the files, construct *PV. + msgCtx := stdlibs.ExecContext{ + ChainID: ctx.ChainID(), + Height: ctx.BlockHeight(), + Timestamp: ctx.BlockTime().Unix(), + Msg: msg, + OrigCaller: caller.Bech32(), + OrigSend: send, + OrigSendSpent: new(std.Coins), + OrigPkgAddr: pkgAddr.Bech32(), + Banker: NewSDKBanker(vm, ctx), + } + // Parse and run the files, construct *PV. + buf := new(bytes.Buffer) + m := gno.NewMachineWithOptions( + gno.MachineOptions{ + PkgPath: "", + Output: buf, + Store: store, + Alloc: store.GetAllocator(), + Context: msgCtx, + MaxCycles: vm.maxCycles, + }) + defer m.Release() + _, pv := m.RunMemPackage(memPkg, false) + ctx.Logger().Info("CPUCYCLES", "addpkg", m.Cycles) + + m2 := gno.NewMachineWithOptions( + gno.MachineOptions{ + PkgPath: "", + Output: buf, + Store: store, + Alloc: store.GetAllocator(), + Context: msgCtx, + MaxCycles: vm.maxCycles, + }) + m2.SetActivePackage(pv) + defer func() { + if r := recover(); r != nil { + err = errors.Wrap(fmt.Errorf("%v", r), "VM call panic: %v\n%s\n", + r, m2.String()) + return + } + m2.Release() + }() + m2.RunMain() + ctx.Logger().Info("CPUCYCLES call", + "cycles", m2.Cycles, + ) + res = buf.String() + return res, nil +} + // QueryFuncs returns public facing function signatures. func (vm *VMKeeper) QueryFuncs(ctx sdk.Context, pkgPath string) (fsigs FunctionSignatures, err error) { store := vm.getGnoStore(ctx) diff --git a/gno.land/pkg/sdk/vm/keeper_test.go b/gno.land/pkg/sdk/vm/keeper_test.go index 27a1054e914..bc6bc285704 100644 --- a/gno.land/pkg/sdk/vm/keeper_test.go +++ b/gno.land/pkg/sdk/vm/keeper_test.go @@ -337,3 +337,102 @@ func GetAdmin() string { assert.NoError(t, err) assert.Equal(t, res, addrString) } + +// Call Run without imports, without variables. +func TestVMKeeperRunSimple(t *testing.T) { + env := setupTestEnv() + ctx := env.ctx + + // Give "addr1" some gnots. + addr := crypto.AddressFromPreimage([]byte("addr1")) + acc := env.acck.NewAccountWithAddress(ctx, addr) + env.acck.SetAccount(ctx, acc) + + files := []*std.MemFile{ + {"script.gno", ` +package main + +func main() { + println("hello world!") +} +`}, + } + + coins := std.MustParseCoins("") + msg2 := NewMsgRun(addr, coins, files) + res, err := env.vmk.Run(ctx, msg2) + assert.NoError(t, err) + assert.Equal(t, res, "hello world!\n") +} + +// Call Run with stdlibs. +func TestVMKeeperRunImportStdlibs(t *testing.T) { + env := setupTestEnv() + ctx := env.ctx + + // Give "addr1" some gnots. + addr := crypto.AddressFromPreimage([]byte("addr1")) + acc := env.acck.NewAccountWithAddress(ctx, addr) + env.acck.SetAccount(ctx, acc) + + files := []*std.MemFile{ + {"script.gno", ` +package main + +import "std" + +func main() { + addr := std.GetOrigCaller() + println("hello world!", addr) +} +`}, + } + + coins := std.MustParseCoins("") + msg2 := NewMsgRun(addr, coins, files) + res, err := env.vmk.Run(ctx, msg2) + assert.NoError(t, err) + expectedString := fmt.Sprintf("hello world! %s\n", addr.String()) + assert.Equal(t, res, expectedString) +} + +func TestNumberOfArgsError(t *testing.T) { + env := setupTestEnv() + ctx := env.ctx + + // Give "addr1" some gnots. + addr := crypto.AddressFromPreimage([]byte("addr1")) + acc := env.acck.NewAccountWithAddress(ctx, addr) + env.acck.SetAccount(ctx, acc) + env.bank.SetCoins(ctx, addr, std.MustParseCoins("10000000ugnot")) + assert.True(t, env.bank.GetCoins(ctx, addr).IsEqual(std.MustParseCoins("10000000ugnot"))) + + // Create test package. + files := []*std.MemFile{ + { + Name: "test.gno", + Body: `package test + +import "std" + +func Echo(msg string) string { + return "echo:"+msg +}`, + }, + } + pkgPath := "gno.land/r/test" + msg1 := NewMsgAddPackage(addr, pkgPath, files) + err := env.vmk.AddPackage(ctx, msg1) + assert.NoError(t, err) + + // Call Echo function with wrong number of arguments + coins := std.MustParseCoins("1ugnot") + msg2 := NewMsgCall(addr, coins, pkgPath, "Echo", []string{"hello world", "extra arg"}) + assert.PanicsWithValue( + t, + func() { + env.vmk.Call(ctx, msg2) + }, + "wrong number of arguments in call to Echo: want 1 got 2", + ) +} diff --git a/gno.land/pkg/sdk/vm/msgs.go b/gno.land/pkg/sdk/vm/msgs.go index 3cfc6c58224..e42babe1510 100644 --- a/gno.land/pkg/sdk/vm/msgs.go +++ b/gno.land/pkg/sdk/vm/msgs.go @@ -1,6 +1,7 @@ package vm import ( + "fmt" "strings" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" @@ -135,3 +136,71 @@ func (msg MsgCall) GetSigners() []crypto.Address { func (msg MsgCall) GetReceived() std.Coins { return msg.Send } + +//---------------------------------------- +// MsgRun + +// MsgRun - executes arbitrary Gno code. +type MsgRun struct { + Caller crypto.Address `json:"caller" yaml:"caller"` + Send std.Coins `json:"send" yaml:"send"` + Package *std.MemPackage `json:"package" yaml:"package"` +} + +var _ std.Msg = MsgRun{} + +func NewMsgRun(caller crypto.Address, send std.Coins, files []*std.MemFile) MsgRun { + for _, file := range files { + if strings.HasSuffix(file.Name, ".gno") { + pkgName := string(gno.PackageNameFromFileBody(file.Name, file.Body)) + if pkgName != "main" { + panic("package name should be 'main'") + } + } + } + return MsgRun{ + Caller: caller, + Send: send, + Package: &std.MemPackage{ + Name: "main", + Path: "", // auto set by the handler + Files: files, + }, + } +} + +// Implements Msg. +func (msg MsgRun) Route() string { return RouterKey } + +// Implements Msg. +func (msg MsgRun) Type() string { return "run" } + +// Implements Msg. +func (msg MsgRun) ValidateBasic() error { + if msg.Caller.IsZero() { + return std.ErrInvalidAddress("missing caller address") + } + + // Force memPkg path to the reserved run path. + wantPath := "gno.land/r/" + msg.Caller.String() + "/run" + if path := msg.Package.Path; path != "" && path != wantPath { + return ErrInvalidPkgPath(fmt.Sprintf("invalid pkgpath for MsgRun: %q", path)) + } + + return nil +} + +// Implements Msg. +func (msg MsgRun) GetSignBytes() []byte { + return std.MustSortJSON(amino.MustMarshalJSON(msg)) +} + +// Implements Msg. +func (msg MsgRun) GetSigners() []crypto.Address { + return []crypto.Address{msg.Caller} +} + +// Implements ReceiveMsg. +func (msg MsgRun) GetReceived() std.Coins { + return msg.Send +} diff --git a/gno.land/pkg/sdk/vm/package.go b/gno.land/pkg/sdk/vm/package.go index 5d05c108bd0..01fad3284e3 100644 --- a/gno.land/pkg/sdk/vm/package.go +++ b/gno.land/pkg/sdk/vm/package.go @@ -13,6 +13,7 @@ var Package = amino.RegisterPackage(amino.NewPackage( std.Package, ).WithTypes( MsgCall{}, "m_call", + MsgRun{}, "m_run", MsgAddPackage{}, "m_addpkg", // TODO rename both to MsgAddPkg? // errors diff --git a/gno.land/pkg/sdk/vm/vm.proto b/gno.land/pkg/sdk/vm/vm.proto index 27e68042bdf..b99be0a85ff 100644 --- a/gno.land/pkg/sdk/vm/vm.proto +++ b/gno.land/pkg/sdk/vm/vm.proto @@ -8,17 +8,17 @@ import "github.com/gnolang/gno/tm2/pkg/std/std.proto"; // messages message m_call { - string Caller = 1; - string Send = 2; - string PkgPath = 3; - string Func = 4; - repeated string Args = 5; + string caller = 1; + string send = 2; + string pkg_path = 3; + string func = 4; + repeated string args = 5; } message m_addpkg { - string Creator = 1; - std.MemPackage Package = 2; - string Deposit = 3; + string creator = 1; + std.MemPackage package = 2; + string deposit = 3; } message InvalidPkgPathError { @@ -28,4 +28,4 @@ message InvalidStmtError { } message InvalidExprError { -} +} \ No newline at end of file diff --git a/gnovm/Makefile b/gnovm/Makefile index 5fcacf94f62..599ca58cd39 100644 --- a/gnovm/Makefile +++ b/gnovm/Makefile @@ -1,3 +1,5 @@ +GNOROOT_DIR ?= $(abspath $(lastword $(MAKEFILE_LIST))/../../) + .PHONY: help help: @echo "Available make commands:" @@ -5,13 +7,16 @@ help: rundep=go run -modfile ../misc/devdeps/go.mod +# We can't use '-trimpath' yet as amino use absolute path from call stack +# to find some directory: see #1236 +GOBUILD_FLAGS := -ldflags "-X github.com/gnolang/gno/gnovm/pkg/gnoenv._GNOROOT=$(GNOROOT_DIR)" .PHONY: build build: - go build -o build/gno ./cmd/gno + go build $(GOBUILD_FLAGS) -o build/gno ./cmd/gno .PHONY: install install: - go install ./cmd/gno + go install $(GOBUILD_FLAGS) ./cmd/gno .PHONY: clean clean: @@ -29,6 +34,11 @@ fmt: $(rundep) mvdan.cc/gofumpt $(GOFMT_FLAGS) . $(rundep) mvdan.cc/gofumpt $(GOFMT_FLAGS) `find stdlibs -name "*.gno"` +.PHONY: imports +GOIMPORTS_FLAGS ?= -w +imports: + $(rundep) golang.org/x/tools/cmd/goimports $(GOIMPORTS_FLAGS) . + ######################################## # Test suite .PHONY: test @@ -46,18 +56,23 @@ _test.pkg: .PHONY: _test.gnolang _test.gnolang: _test.gnolang.native _test.gnolang.stdlibs _test.gnolang.realm _test.gnolang.pkg0 _test.gnolang.pkg1 _test.gnolang.pkg2 _test.gnolang.other -_test.gnolang.other:; go test $(GOTEST_FLAGS) tests/*.go -run "(TestFileStr|TestSelectors)" -_test.gnolang.realm:; go test $(GOTEST_FLAGS) tests/*.go -run "TestFiles/^zrealm" -_test.gnolang.pkg0:; go test $(GOTEST_FLAGS) tests/*.go -run "TestPackages/(bufio|crypto|encoding|errors|internal|io|math|sort|std|stdshim|strconv|strings|testing|unicode)" -_test.gnolang.pkg1:; go test $(GOTEST_FLAGS) tests/*.go -run "TestPackages/regexp" -_test.gnolang.pkg2:; go test $(GOTEST_FLAGS) tests/*.go -run "TestPackages/bytes" -_test.gnolang.native:; go test $(GOTEST_FLAGS) tests/*.go -test.short -run "TestFilesNative/" -_test.gnolang.stdlibs:; go test $(GOTEST_FLAGS) tests/*.go -test.short -run 'TestFiles$$/' -_test.gnolang.native.sync:; go test $(GOTEST_FLAGS) tests/*.go -test.short -run "TestFilesNative/" --update-golden-tests -_test.gnolang.stdlibs.sync:; go test $(GOTEST_FLAGS) tests/*.go -test.short -run 'TestFiles$$/' --update-golden-tests +_test.gnolang.other:; go test tests/*.go -run "(TestFileStr|TestSelectors)" $(GOTEST_FLAGS) +_test.gnolang.realm:; go test tests/*.go -run "TestFiles/^zrealm" $(GOTEST_FLAGS) +_test.gnolang.pkg0:; go test tests/*.go -run "TestPackages/(bufio|crypto|encoding|errors|internal|io|math|sort|std|stdshim|strconv|strings|testing|unicode)" $(GOTEST_FLAGS) +_test.gnolang.pkg1:; go test tests/*.go -run "TestPackages/regexp" $(GOTEST_FLAGS) +_test.gnolang.pkg2:; go test tests/*.go -run "TestPackages/bytes" $(GOTEST_FLAGS) +_test.gnolang.native:; go test tests/*.go -test.short -run "TestFilesNative/" $(GOTEST_FLAGS) +_test.gnolang.stdlibs:; go test tests/*.go -test.short -run 'TestFiles$$/' $(GOTEST_FLAGS) +_test.gnolang.native.sync:; go test tests/*.go -test.short -run "TestFilesNative/" --update-golden-tests $(GOTEST_FLAGS) +_test.gnolang.stdlibs.sync:; go test tests/*.go -test.short -run 'TestFiles$$/' --update-golden-tests $(GOTEST_FLAGS) ######################################## # Code gen +# TODO: move _dev.stringer to go:generate instructions, simplify generate +# to just go generate. +.PHONY: generate +generate: _dev.stringer _dev.generate + stringer_cmd=$(rundep) golang.org/x/tools/cmd/stringer .PHONY: _dev.stringer _dev.stringer: @@ -68,5 +83,9 @@ _dev.stringer: $(stringer_cmd) -type=VPType ./pkg/gnolang $(stringer_cmd) -type=Word ./pkg/gnolang +.PHONY: _dev.generate +_dev.generate: + go generate -x ./... + # genproto: # see top-level Makefile. diff --git a/gnovm/cmd/gno/build.go b/gnovm/cmd/gno/build.go deleted file mode 100644 index b80711586e4..00000000000 --- a/gnovm/cmd/gno/build.go +++ /dev/null @@ -1,92 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "os" - - gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/tm2/pkg/commands" -) - -type buildCfg struct { - verbose bool - goBinary string -} - -var defaultBuildOptions = &buildCfg{ - verbose: false, - goBinary: "go", -} - -func newBuildCmd(io *commands.IO) *commands.Command { - cfg := &buildCfg{} - - return commands.NewCommand( - commands.Metadata{ - Name: "build", - ShortUsage: "build [flags] ", - ShortHelp: "Builds the specified gno package", - }, - cfg, - func(_ context.Context, args []string) error { - return execBuild(cfg, args, io) - }, - ) -} - -func (c *buildCfg) RegisterFlags(fs *flag.FlagSet) { - fs.BoolVar( - &c.verbose, - "verbose", - defaultBuildOptions.verbose, - "verbose output when building", - ) - - fs.StringVar( - &c.goBinary, - "go-binary", - defaultBuildOptions.goBinary, - "go binary to use for building", - ) -} - -func execBuild(cfg *buildCfg, args []string, io *commands.IO) error { - if len(args) < 1 { - return flag.ErrHelp - } - - paths, err := gnoPackagesFromArgs(args) - if err != nil { - return fmt.Errorf("list packages: %w", err) - } - - errCount := 0 - for _, pkgPath := range paths { - err = goBuildFileOrPkg(pkgPath, cfg) - if err != nil { - err = fmt.Errorf("%s: build pkg: %w", pkgPath, err) - io.ErrPrintfln("%s\n", err.Error()) - - errCount++ - } - } - - if errCount > 0 { - return fmt.Errorf("%d go build errors", errCount) - } - - return nil -} - -func goBuildFileOrPkg(fileOrPkg string, cfg *buildCfg) error { - verbose := cfg.verbose - goBinary := cfg.goBinary - - if verbose { - fmt.Fprintf(os.Stderr, "%s\n", fileOrPkg) - } - - return gno.PrecompileBuildPackage(fileOrPkg, goBinary) -} diff --git a/gnovm/cmd/gno/build_test.go b/gnovm/cmd/gno/build_test.go deleted file mode 100644 index 89339ee8a6e..00000000000 --- a/gnovm/cmd/gno/build_test.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -import "testing" - -func TestBuildApp(t *testing.T) { - tc := []testMainCase{ - { - args: []string{"build"}, - errShouldBe: "flag: help requested", - }, - - // {args: []string{"build", "..."}, stdoutShouldContain: "..."}, - // TODO: auto precompilation - // TODO: error handling - } - testMainCaseRun(t, tc) -} diff --git a/gnovm/cmd/gno/clean.go b/gnovm/cmd/gno/clean.go index 999cc5f1f53..444131986cb 100644 --- a/gnovm/cmd/gno/clean.go +++ b/gnovm/cmd/gno/clean.go @@ -9,9 +9,9 @@ import ( "path/filepath" "strings" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" "github.com/gnolang/gno/gnovm/pkg/gnomod" "github.com/gnolang/gno/tm2/pkg/commands" - "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" ) type cleanCfg struct { @@ -20,7 +20,7 @@ type cleanCfg struct { modCache bool // clean -modcache flag } -func newCleanCmd(io *commands.IO) *commands.Command { +func newCleanCmd(io commands.IO) *commands.Command { cfg := &cleanCfg{} return commands.NewCommand( @@ -59,7 +59,7 @@ func (c *cleanCfg) RegisterFlags(fs *flag.FlagSet) { ) } -func execClean(cfg *cleanCfg, args []string, io *commands.IO) error { +func execClean(cfg *cleanCfg, args []string, io commands.IO) error { if len(args) > 0 { return flag.ErrHelp } @@ -82,7 +82,7 @@ func execClean(cfg *cleanCfg, args []string, io *commands.IO) error { } if cfg.modCache { - modCacheDir := filepath.Join(client.HomeDir(), "pkg", "mod") + modCacheDir := filepath.Join(gnoenv.HomeDir(), "pkg", "mod") if !cfg.dryRun { if err := os.RemoveAll(modCacheDir); err != nil { return err @@ -96,7 +96,7 @@ func execClean(cfg *cleanCfg, args []string, io *commands.IO) error { } // clean removes generated files from a directory. -func clean(dir string, cfg *cleanCfg, io *commands.IO) error { +func clean(dir string, cfg *cleanCfg, io commands.IO) error { return filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { if err != nil { return err diff --git a/gnovm/cmd/gno/doc.go b/gnovm/cmd/gno/doc.go index 0de49470c2a..794dd1ba7bb 100644 --- a/gnovm/cmd/gno/doc.go +++ b/gnovm/cmd/gno/doc.go @@ -10,6 +10,7 @@ import ( "path/filepath" "github.com/gnolang/gno/gnovm/pkg/doc" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" "github.com/gnolang/gno/gnovm/pkg/gnomod" "github.com/gnolang/gno/tm2/pkg/commands" ) @@ -22,7 +23,7 @@ type docCfg struct { rootDir string } -func newDocCmd(io *commands.IO) *commands.Command { +func newDocCmd(io commands.IO) *commands.Command { c := &docCfg{} return commands.NewCommand( commands.Metadata{ @@ -70,14 +71,14 @@ func (c *docCfg) RegisterFlags(fs *flag.FlagSet) { &c.rootDir, "root-dir", "", - "clone location of github.com/gnolang/gno (gnodev tries to guess it)", + "clone location of github.com/gnolang/gno (gno binary tries to guess it)", ) } -func execDoc(cfg *docCfg, args []string, io *commands.IO) error { +func execDoc(cfg *docCfg, args []string, io commands.IO) error { // guess opts.RootDir if cfg.rootDir == "" { - cfg.rootDir = guessRootDir() + cfg.rootDir = gnoenv.RootDir() } wd, err := os.Getwd() @@ -112,7 +113,7 @@ func execDoc(cfg *docCfg, args []string, io *commands.IO) error { io.Printfln("warning: error parsing some candidate packages:\n%v", err) } return res.WriteDocumentation( - io.Out, + io.Out(), &doc.WriteDocumentationOptions{ ShowAll: cfg.all, Source: cfg.src, diff --git a/gnovm/cmd/gno/doc_test.go b/gnovm/cmd/gno/doc_test.go index 3eb90e2a329..513862ad2dc 100644 --- a/gnovm/cmd/gno/doc_test.go +++ b/gnovm/cmd/gno/doc_test.go @@ -10,7 +10,7 @@ func TestGnoDoc(t *testing.T) { }, { args: []string{"doc", "avl"}, - stdoutShouldContain: "func NewNode", + stdoutShouldContain: "func NewTree", }, { args: []string{"doc", "-u", "avl.Node"}, diff --git a/gnovm/cmd/gno/env.go b/gnovm/cmd/gno/env.go new file mode 100644 index 00000000000..4ccd1873c3f --- /dev/null +++ b/gnovm/cmd/gno/env.go @@ -0,0 +1,113 @@ +package main + +import ( + "context" + "flag" + + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + "github.com/gnolang/gno/tm2/pkg/commands" +) + +type envCfg struct { + json bool +} + +func newEnvCmd(io commands.IO) *commands.Command { + c := &envCfg{} + return commands.NewCommand( + commands.Metadata{ + Name: "env", + ShortUsage: "env [flags] ", + ShortHelp: "`env` prints Gno environment information", + }, + c, + func(_ context.Context, args []string) error { + return execEnv(c, args, io) + }, + ) +} + +func (c *envCfg) RegisterFlags(fs *flag.FlagSet) { + fs.BoolVar( + &c.json, + "json", + false, + "Prints the environment in JSON format instead of as a shell script.", + ) +} + +type envVar struct { + Key string + Value string +} + +func findEnv(env []envVar, name string) string { + for _, e := range env { + if e.Key == name { + return e.Value + } + } + return "" +} + +type envPrinter func(vars []envVar, io commands.IO) + +func execEnv(cfg *envCfg, args []string, io commands.IO) error { + envs := []envVar{ + // GNOROOT Should point to the local location of the GNO repository. + // It serves as the gno equivalent of `GOROOT`. + {Key: "GNOROOT", Value: gnoenv.RootDir()}, + // GNOHOME Should point to the user local configuration. + // The most common place for this should be $HOME/gno. + {Key: "GNOHOME", Value: gnoenv.HomeDir()}, + } + + // Setup filters + filters := make([]envVar, len(args)) + for i, arg := range args { + filters[i] = envVar{Key: arg, Value: findEnv(envs, arg)} + } + + // Setup printer + var printerEnv envPrinter + if cfg.json { + printerEnv = printJSON + } else { + printerEnv = getPrinterShell(len(args) == 0) + } + + // Print environements + if len(filters) > 0 { + printerEnv(filters, io) + } else { + printerEnv(envs, io) + } + + return nil +} + +func getPrinterShell(printkeys bool) envPrinter { + return func(vars []envVar, io commands.IO) { + for _, env := range vars { + if printkeys { + io.Printf("%s=%q\n", env.Key, env.Value) + } else { + io.Printf("%s\n", env.Value) + } + } + } +} + +func printJSON(vars []envVar, io commands.IO) { + io.Println("{") + for i, env := range vars { + io.Printf("\t%q: %q", env.Key, env.Value) + if i != len(vars)-1 { + io.Printf(",") + } + + // Jump to next line + io.Printf("\n") + } + io.Println("}") +} diff --git a/gnovm/cmd/gno/env_test.go b/gnovm/cmd/gno/env_test.go new file mode 100644 index 00000000000..8aeb84ab2cc --- /dev/null +++ b/gnovm/cmd/gno/env_test.go @@ -0,0 +1,43 @@ +package main + +import ( + "fmt" + "testing" +) + +func TestEnvApp(t *testing.T) { + const ( + testGnoRootEnv = "/faster/better/stronger" + testGnoHomeEnv = "/around/the/world" + ) + + t.Setenv("GNOROOT", testGnoRootEnv) + t.Setenv("GNOHOME", testGnoHomeEnv) + tc := []testMainCase{ + // shell + {args: []string{"env", "foo"}, stdoutShouldBe: "\n"}, + {args: []string{"env", "foo", "bar"}, stdoutShouldBe: "\n\n"}, + {args: []string{"env", "GNOROOT"}, stdoutShouldBe: testGnoRootEnv + "\n"}, + {args: []string{"env", "GNOHOME", "storm"}, stdoutShouldBe: testGnoHomeEnv + "\n\n"}, + {args: []string{"env"}, stdoutShouldContain: fmt.Sprintf("GNOROOT=%q", testGnoRootEnv)}, + {args: []string{"env"}, stdoutShouldContain: fmt.Sprintf("GNOHOME=%q", testGnoHomeEnv)}, + + // json + {args: []string{"env", "-json"}, stdoutShouldContain: fmt.Sprintf("\"GNOROOT\": %q", testGnoRootEnv)}, + {args: []string{"env", "-json"}, stdoutShouldContain: fmt.Sprintf("\"GNOHOME\": %q", testGnoHomeEnv)}, + { + args: []string{"env", "-json", "GNOROOT"}, + stdoutShouldBe: fmt.Sprintf("{\n\t\"GNOROOT\": %q\n}\n", testGnoRootEnv), + }, + { + args: []string{"env", "-json", "GNOROOT", "storm"}, + stdoutShouldBe: fmt.Sprintf("{\n\t\"GNOROOT\": %q,\n\t\"storm\": \"\"\n}\n", testGnoRootEnv), + }, + { + args: []string{"env", "-json", "storm"}, + stdoutShouldBe: "{\n\t\"storm\": \"\"\n}\n", + }, + } + + testMainCaseRun(t, tc) +} diff --git a/gnovm/cmd/gno/lint.go b/gnovm/cmd/gno/lint.go index 158b9d8db5d..68a5808b309 100644 --- a/gnovm/cmd/gno/lint.go +++ b/gnovm/cmd/gno/lint.go @@ -2,11 +2,17 @@ package main import ( "context" + "errors" "flag" "fmt" "os" "path/filepath" + "regexp" + "strings" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/tests" "github.com/gnolang/gno/tm2/pkg/commands" osm "github.com/gnolang/gno/tm2/pkg/os" ) @@ -19,7 +25,7 @@ type lintCfg struct { // auto-fix: apply suggested fixes automatically. } -func newLintCmd(io *commands.IO) *commands.Command { +func newLintCmd(io commands.IO) *commands.Command { cfg := &lintCfg{} return commands.NewCommand( @@ -36,12 +42,14 @@ func newLintCmd(io *commands.IO) *commands.Command { } func (c *lintCfg) RegisterFlags(fs *flag.FlagSet) { + rootdir := gnoenv.RootDir() + fs.BoolVar(&c.verbose, "verbose", false, "verbose output when lintning") - fs.StringVar(&c.rootDir, "root-dir", "", "clone location of github.com/gnolang/gno (gno tries to guess it)") - fs.IntVar(&c.setExitStatus, "set_exit_status", 1, "set exit status to 1 if any issues are found") + fs.StringVar(&c.rootDir, "root-dir", rootdir, "clone location of github.com/gnolang/gno (gno tries to guess it)") + fs.IntVar(&c.setExitStatus, "set-exit-status", 1, "set exit status to 1 if any issues are found") } -func execLint(cfg *lintCfg, args []string, io *commands.IO) error { +func execLint(cfg *lintCfg, args []string, io commands.IO) error { if len(args) < 1 { return flag.ErrHelp } @@ -51,7 +59,7 @@ func execLint(cfg *lintCfg, args []string, io *commands.IO) error { rootDir = cfg.rootDir ) if rootDir == "" { - rootDir = guessRootDir() + rootDir = gnoenv.RootDir() } pkgPaths, err := gnoPackagesFromArgs(args) @@ -62,15 +70,15 @@ func execLint(cfg *lintCfg, args []string, io *commands.IO) error { hasError := false addIssue := func(issue lintIssue) { hasError = true - fmt.Fprint(io.Err, issue.String()+"\n") + fmt.Fprint(io.Err(), issue.String()+"\n") } for _, pkgPath := range pkgPaths { if verbose { - fmt.Fprintf(io.Err, "Linting %q...\n", pkgPath) + fmt.Fprintf(io.Err(), "Linting %q...\n", pkgPath) } - // 'gno.mod' exists? + // Check if 'gno.mod' exists gnoModPath := filepath.Join(pkgPath, "gno.mod") if !osm.FileExists(gnoModPath) { addIssue(lintIssue{ @@ -81,20 +89,131 @@ func execLint(cfg *lintCfg, args []string, io *commands.IO) error { }) } - // TODO: add more checkers + // Handle runtime errors + catchRuntimeError(pkgPath, addIssue, func() { + stdout, stdin, stderr := io.Out(), io.In(), io.Err() + testStore := tests.TestStore( + rootDir, "", + stdin, stdout, stderr, + tests.ImportModeStdlibsOnly, + ) + + targetPath := pkgPath + info, err := os.Stat(pkgPath) + if err == nil && !info.IsDir() { + targetPath = filepath.Dir(pkgPath) + } + + memPkg := gno.ReadMemPackage(targetPath, targetPath) + tm := tests.TestMachine(testStore, stdout, memPkg.Name) + + // Check package + tm.RunMemPackage(memPkg, true) + + // Check test files + testfiles := &gno.FileSet{} + for _, mfile := range memPkg.Files { + if !strings.HasSuffix(mfile.Name, ".gno") { + continue // Skip non-GNO files + } + + n, _ := gno.ParseFile(mfile.Name, mfile.Body) + if n == nil { + continue // Skip empty files + } + + // XXX: package ending with `_test` is not supported yet + if strings.HasSuffix(mfile.Name, "_test.gno") && !strings.HasSuffix(string(n.PkgName), "_test") { + // Keep only test files + testfiles.AddFiles(n) + } + } + + tm.RunFiles(testfiles.Files...) + }) + + // TODO: Add more checkers } if hasError && cfg.setExitStatus != 0 { os.Exit(cfg.setExitStatus) } + return nil } +func guessSourcePath(pkg, source string) string { + if info, err := os.Stat(pkg); !os.IsNotExist(err) && !info.IsDir() { + pkg = filepath.Dir(pkg) + } + + sourceJoin := filepath.Join(pkg, source) + if _, err := os.Stat(sourceJoin); !os.IsNotExist(err) { + return filepath.Clean(sourceJoin) + } + + if _, err := os.Stat(source); !os.IsNotExist(err) { + return filepath.Clean(source) + } + + return filepath.Clean(pkg) +} + +// reParseRecover is a regex designed to parse error details from a string. +// It extracts the file location, line number, and error message from a formatted error string. +// XXX: Ideally, error handling should encapsulate location details within a dedicated error type. +var reParseRecover = regexp.MustCompile(`^([^:]+):(\d+)(?::\d+)?:? *(.*)$`) + +func catchRuntimeError(pkgPath string, addIssue func(issue lintIssue), action func()) { + defer func() { + // Errors catched here mostly come from: gnovm/pkg/gnolang/preprocess.go + r := recover() + if r == nil { + return + } + + var err error + switch verr := r.(type) { + case *gno.PreprocessError: + err = verr.Unwrap() + case error: + err = verr + case string: + err = errors.New(verr) + default: + panic(r) + } + + var issue lintIssue + issue.Confidence = 1 + issue.Code = lintGnoError + + parsedError := strings.TrimSpace(err.Error()) + parsedError = strings.TrimPrefix(parsedError, pkgPath+"/") + + matches := reParseRecover.FindStringSubmatch(parsedError) + if len(matches) == 4 { + sourcepath := guessSourcePath(pkgPath, matches[1]) + issue.Location = fmt.Sprintf("%s:%s", sourcepath, matches[2]) + issue.Msg = strings.TrimSpace(matches[3]) + } else { + issue.Location = fmt.Sprintf("%s:0", filepath.Clean(pkgPath)) + issue.Msg = err.Error() + } + + addIssue(issue) + }() + + action() +} + type lintCode int const ( lintUnknown lintCode = 0 lintNoGnoMod lintCode = iota + lintGnoError + // TODO: add new linter codes here. ) diff --git a/gnovm/cmd/gno/lint_test.go b/gnovm/cmd/gno/lint_test.go index ce200a1fedd..d700467965d 100644 --- a/gnovm/cmd/gno/lint_test.go +++ b/gnovm/cmd/gno/lint_test.go @@ -8,18 +8,25 @@ func TestLintApp(t *testing.T) { args: []string{"lint"}, errShouldBe: "flag: help requested", }, { - args: []string{"lint", "--set_exit_status=0", "../../tests/integ/run-main/"}, + args: []string{"lint", "--set-exit-status=0", "../../tests/integ/run-main/"}, stderrShouldContain: "./../../tests/integ/run-main: missing 'gno.mod' file (code=1).", }, { - args: []string{"lint", "--set_exit_status=0", "../../tests/integ/run-main/"}, + args: []string{"lint", "--set-exit-status=0", "../../tests/integ/undefined-variable-test/undefined_variables_test.gno"}, + stderrShouldContain: "undefined_variables_test.gno:6: name toto not declared (code=2)", + }, { + args: []string{"lint", "--set-exit-status=0", "../../tests/integ/package-not-declared/main.gno"}, + stderrShouldContain: "main.gno:4: name fmt not declared (code=2).", + }, { + args: []string{"lint", "--set-exit-status=0", "../../tests/integ/run-main/"}, stderrShouldContain: "./../../tests/integ/run-main: missing 'gno.mod' file (code=1).", }, { - args: []string{"lint", "--set_exit_status=0", "../../tests/integ/minimalist-gnomod/"}, + args: []string{"lint", "--set-exit-status=0", "../../tests/integ/minimalist-gnomod/"}, // TODO: raise an error because there is a gno.mod, but no .gno files }, { - args: []string{"lint", "--set_exit_status=0", "../../tests/integ/invalid-module-name/"}, + args: []string{"lint", "--set-exit-status=0", "../../tests/integ/invalid-module-name/"}, // TODO: raise an error because gno.mod is invalid }, + // TODO: 'gno mod' is valid? // TODO: is gno source valid? // TODO: are dependencies valid? diff --git a/gnovm/cmd/gno/main.go b/gnovm/cmd/gno/main.go index 433db703b90..aecb5d0f1e5 100644 --- a/gnovm/cmd/gno/main.go +++ b/gnovm/cmd/gno/main.go @@ -2,7 +2,6 @@ package main import ( "context" - "fmt" "os" "github.com/gnolang/gno/tm2/pkg/commands" @@ -11,14 +10,10 @@ import ( func main() { cmd := newGnocliCmd(commands.NewDefaultIO()) - if err := cmd.ParseAndRun(context.Background(), os.Args[1:]); err != nil { - _, _ = fmt.Fprintf(os.Stderr, "%+v\n", err) - - os.Exit(1) - } + cmd.Execute(context.Background(), os.Args[1:]) } -func newGnocliCmd(io *commands.IO) *commands.Command { +func newGnocliCmd(io commands.IO) *commands.Command { cmd := commands.NewCommand( commands.Metadata{ ShortUsage: " [flags] [...]", @@ -33,11 +28,11 @@ func newGnocliCmd(io *commands.IO) *commands.Command { newTestCmd(io), newLintCmd(io), newRunCmd(io), - newBuildCmd(io), newPrecompileCmd(io), newCleanCmd(io), newReplCmd(), newDocCmd(io), + newEnvCmd(io), // fmt -- gofmt // graph // vendor -- download deps from the chain in vendor/ @@ -47,7 +42,7 @@ func newGnocliCmd(io *commands.IO) *commands.Command { // generate // "vm" -- starts an in-memory chain that can be interacted with? // bug -- start a bug report - // version -- show gnodev, golang versions + // version -- show gno, golang versions ) return cmd diff --git a/gnovm/cmd/gno/main_test.go b/gnovm/cmd/gno/main_test.go index 8d19a50e814..1395d120012 100644 --- a/gnovm/cmd/gno/main_test.go +++ b/gnovm/cmd/gno/main_test.go @@ -5,18 +5,16 @@ import ( "context" "fmt" "os" - "os/exec" "path/filepath" "strings" "testing" - "github.com/rogpeppe/go-internal/testscript" "github.com/stretchr/testify/require" "github.com/gnolang/gno/tm2/pkg/commands" ) -func TestMain_Gnodev(t *testing.T) { +func TestMain_Gno(t *testing.T) { tc := []testMainCase{ {args: []string{""}, errShouldBe: "flag: help requested"}, } @@ -144,45 +142,3 @@ func testMainCaseRun(t *testing.T, tc []testMainCase) { }) } } - -func setupTestScript(t *testing.T, txtarDir string) testscript.Params { - t.Helper() - // Get root location of github.com/gnolang/gno - goModPath, err := exec.Command("go", "env", "GOMOD").CombinedOutput() - require.NoError(t, err) - rootDir := filepath.Dir(string(goModPath)) - // Build a fresh gno binary in a temp directory - gnoBin := filepath.Join(t.TempDir(), "gno") - err = exec.Command("go", "build", "-o", gnoBin, filepath.Join(rootDir, "gnovm", "cmd", "gno")).Run() - require.NoError(t, err) - // Define script params - return testscript.Params{ - Setup: func(env *testscript.Env) error { - env.Vars = append(env.Vars, - "GNOROOT="+rootDir, // thx PR 1014 :) - // by default, $HOME=/no-home, but we need an existing $HOME directory - // because some commands needs to access $HOME/.cache/go-build - "HOME="+t.TempDir(), - ) - return nil - }, - Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){ - // add a custom "gno" command so txtar files can easily execute "gno" - // without knowing where is the binary or how it is executed. - "gno": func(ts *testscript.TestScript, neg bool, args []string) { - err := ts.Exec(gnoBin, args...) - if err != nil { - ts.Logf("[%v]\n", err) - if !neg { - ts.Fatalf("unexpected gno command failure") - } - } else { - if neg { - ts.Fatalf("unexpected gno command success") - } - } - }, - }, - Dir: txtarDir, - } -} diff --git a/gnovm/cmd/gno/mod.go b/gnovm/cmd/gno/mod.go index 09194232fec..da8db3ed68a 100644 --- a/gnovm/cmd/gno/mod.go +++ b/gnovm/cmd/gno/mod.go @@ -4,8 +4,12 @@ import ( "context" "flag" "fmt" + "go/parser" + "go/token" "os" "path/filepath" + "sort" + "strings" "github.com/gnolang/gno/gnovm/pkg/gnomod" "github.com/gnolang/gno/tm2/pkg/commands" @@ -17,7 +21,7 @@ type modDownloadCfg struct { verbose bool } -func newModCmd(io *commands.IO) *commands.Command { +func newModCmd(io commands.IO) *commands.Command { cmd := commands.NewCommand( commands.Metadata{ Name: "mod", @@ -31,12 +35,13 @@ func newModCmd(io *commands.IO) *commands.Command { cmd.AddSubCommands( newModDownloadCmd(io), newModInitCmd(), + newModTidy(io), ) return cmd } -func newModDownloadCmd(io *commands.IO) *commands.Command { +func newModDownloadCmd(io commands.IO) *commands.Command { cfg := &modDownloadCfg{} return commands.NewCommand( @@ -66,6 +71,20 @@ func newModInitCmd() *commands.Command { ) } +func newModTidy(io commands.IO) *commands.Command { + return commands.NewCommand( + commands.Metadata{ + Name: "tidy", + ShortUsage: "tidy", + ShortHelp: "Add missing and remove unused modules", + }, + commands.NewEmptyConfig(), + func(_ context.Context, args []string) error { + return execModTidy(args, io) + }, + ) +} + func (c *modDownloadCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( &c.remote, @@ -82,7 +101,7 @@ func (c *modDownloadCfg) RegisterFlags(fs *flag.FlagSet) { ) } -func execModDownload(cfg *modDownloadCfg, args []string, io *commands.IO) error { +func execModDownload(cfg *modDownloadCfg, args []string, io commands.IO) error { if len(args) > 0 { return flag.ErrHelp } @@ -126,7 +145,7 @@ func execModDownload(cfg *modDownloadCfg, args []string, io *commands.IO) error } // write go.mod file - err = gomod.WriteToPath(filepath.Join(path, "go.mod")) + err = gomod.Write(filepath.Join(path, "go.mod")) if err != nil { return fmt.Errorf("write go.mod file: %w", err) } @@ -152,3 +171,89 @@ func execModInit(args []string) error { return nil } + +func execModTidy(args []string, io commands.IO) error { + if len(args) > 0 { + return flag.ErrHelp + } + + wd, err := os.Getwd() + if err != nil { + return err + } + fname := filepath.Join(wd, "gno.mod") + gm, err := gnomod.ParseGnoMod(fname) + if err != nil { + return err + } + + // Drop all existing requires + for _, r := range gm.Require { + gm.DropRequire(r.Mod.Path) + } + + imports, err := getGnoImports(wd) + if err != nil { + return err + } + for _, im := range imports { + // skip if importpath is modulepath + if im == gm.Module.Mod.Path { + continue + } + gm.AddRequire(im, "v0.0.0-latest") + } + + gm.Write(fname) + return nil +} + +// getGnoImports returns the list of gno imports from a given path. +// Note: It ignores subdirs. Since right now we are still deciding on +// how to handle subdirs. +// See: +// - https://github.com/gnolang/gno/issues/1024 +// - https://github.com/gnolang/gno/issues/852 +// +// TODO: move this to better location. +func getGnoImports(path string) ([]string, error) { + entries, err := os.ReadDir(path) + if err != nil { + return nil, err + } + + allImports := make([]string, 0) + seen := make(map[string]struct{}) + for _, e := range entries { + filename := e.Name() + if ext := filepath.Ext(filename); ext != ".gno" { + continue + } + if strings.HasSuffix(filename, "_filetest.gno") { + continue + } + data, err := os.ReadFile(filepath.Join(path, filename)) + if err != nil { + return nil, err + } + fs := token.NewFileSet() + f, err := parser.ParseFile(fs, filename, data, parser.ImportsOnly) + if err != nil { + return nil, err + } + for _, imp := range f.Imports { + importPath := strings.TrimPrefix(strings.TrimSuffix(imp.Path.Value, `"`), `"`) + if !strings.HasPrefix(importPath, "gno.land/") { + continue + } + if _, ok := seen[importPath]; ok { + continue + } + allImports = append(allImports, importPath) + seen[importPath] = struct{}{} + } + } + sort.Strings(allImports) + + return allImports, nil +} diff --git a/gnovm/cmd/gno/mod_test.go b/gnovm/cmd/gno/mod_test.go index fdae3d12c7a..bbf106c8960 100644 --- a/gnovm/cmd/gno/mod_test.go +++ b/gnovm/cmd/gno/mod_test.go @@ -1,6 +1,13 @@ package main -import "testing" +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) func TestModApp(t *testing.T) { tc := []testMainCase{ @@ -9,7 +16,7 @@ func TestModApp(t *testing.T) { errShouldBe: "flag: help requested", }, - // test gno.mod download + // test `gno mod download` { args: []string{"mod", "download"}, testDir: "../../tests/integ/empty-dir", @@ -73,7 +80,7 @@ func TestModApp(t *testing.T) { errShouldContain: "fetch: writepackage: querychain", }, - // test gno.mod init with no module name + // test `gno mod init` with no module name { args: []string{"mod", "init"}, testDir: "../../tests/integ/valid1", @@ -110,7 +117,7 @@ func TestModApp(t *testing.T) { errShouldBe: "create gno.mod file: gno.mod file already exists", }, - // test gno.mod init with module name + // test `gno mod init` with module name { args: []string{"mod", "init", "gno.land/p/demo/foo"}, testDir: "../../tests/integ/empty-dir", @@ -137,6 +144,164 @@ func TestModApp(t *testing.T) { simulateExternalRepo: true, errShouldBe: "create gno.mod file: gno.mod file already exists", }, + + // test `gno mod tidy` with module name + { + args: []string{"mod", "tidy", "arg1"}, + testDir: "../../tests/integ/minimalist-gnomod", + simulateExternalRepo: true, + errShouldContain: "flag: help requested", + }, + { + args: []string{"mod", "tidy"}, + testDir: "../../tests/integ/empty-dir", + simulateExternalRepo: true, + errShouldContain: "could not read gno.mod file", + }, + { + args: []string{"mod", "tidy"}, + testDir: "../../tests/integ/invalid-module-version1", + simulateExternalRepo: true, + errShouldContain: "error parsing gno.mod file at", + }, + { + args: []string{"mod", "tidy"}, + testDir: "../../tests/integ/minimalist-gnomod", + simulateExternalRepo: true, + }, + { + args: []string{"mod", "tidy"}, + testDir: "../../tests/integ/require-remote-module", + simulateExternalRepo: true, + }, + { + args: []string{"mod", "tidy"}, + testDir: "../../tests/integ/valid2", + simulateExternalRepo: true, + }, + { + args: []string{"mod", "tidy"}, + testDir: "../../tests/integ/invalid-gno-file", + simulateExternalRepo: true, + errShouldContain: "expected 'package', found packag", + }, } testMainCaseRun(t, tc) } + +func TestGetGnoImports(t *testing.T) { + workingDir, err := os.Getwd() + require.NoError(t, err) + + // create external dir + tmpDir, cleanUpFn := createTmpDir(t) + defer cleanUpFn() + + // cd to tmp directory + os.Chdir(tmpDir) + defer os.Chdir(workingDir) + + files := []struct { + name, data string + }{ + { + name: "file1.gno", + data: ` + package tmp + + import ( + "std" + + "gno.land/p/demo/pkg1" + ) + `, + }, + { + name: "file2.gno", + data: ` + package tmp + + import ( + "gno.land/p/demo/pkg1" + "gno.land/p/demo/pkg2" + ) + `, + }, + { + name: "file1_test.gno", + data: ` + package tmp + + import ( + "testing" + + "gno.land/p/demo/testpkg" + ) + `, + }, + { + name: "z_0_filetest.gno", + data: ` + package main + + import ( + "gno.land/p/demo/filetestpkg" + ) + `, + }, + + // subpkg files + { + name: filepath.Join("subtmp", "file1.gno"), + data: ` + package subtmp + + import ( + "std" + + "gno.land/p/demo/subpkg1" + ) + `, + }, + { + name: filepath.Join("subtmp", "file2.gno"), + data: ` + package subtmp + + import ( + "gno.land/p/demo/subpkg1" + "gno.land/p/demo/subpkg2" + ) + `, + }, + } + + // Expected list of imports + // - ignore subdirs + // - ignore duplicate + // - ignore *_filetest.gno + // - should be sorted + expected := []string{ + "gno.land/p/demo/pkg1", + "gno.land/p/demo/pkg2", + "gno.land/p/demo/testpkg", + } + + // Create subpkg dir + err = os.Mkdir("subtmp", 0o700) + require.NoError(t, err) + + // Create files + for _, f := range files { + err = os.WriteFile(f.name, []byte(f.data), 0o644) + require.NoError(t, err) + } + + imports, err := getGnoImports(tmpDir) + require.NoError(t, err) + + require.Equal(t, len(expected), len(imports)) + for i := range imports { + assert.Equal(t, expected[i], imports[i]) + } +} diff --git a/gnovm/cmd/gno/precompile.go b/gnovm/cmd/gno/precompile.go index 7e895109706..5583e3d8429 100644 --- a/gnovm/cmd/gno/precompile.go +++ b/gnovm/cmd/gno/precompile.go @@ -18,6 +18,7 @@ type precompileCfg struct { verbose bool skipFmt bool skipImports bool + gobuild bool goBinary string gofmtBinary string output string @@ -30,6 +31,11 @@ type precompileOptions struct { precompiled map[importPath]struct{} } +var defaultPrecompileCfg = &precompileCfg{ + verbose: false, + goBinary: "go", +} + func newPrecompileOptions(cfg *precompileCfg) *precompileOptions { return &precompileOptions{cfg, map[importPath]struct{}{}} } @@ -47,7 +53,7 @@ func (p *precompileOptions) markAsPrecompiled(pkg importPath) { p.precompiled[pkg] = struct{}{} } -func newPrecompileCmd(io *commands.IO) *commands.Command { +func newPrecompileCmd(io commands.IO) *commands.Command { cfg := &precompileCfg{} return commands.NewCommand( @@ -85,6 +91,13 @@ func (c *precompileCfg) RegisterFlags(fs *flag.FlagSet) { "do not precompile imports recursively", ) + fs.BoolVar( + &c.gobuild, + "gobuild", + false, + "run go build on generated go files, ignoring test files", + ) + fs.StringVar( &c.goBinary, "go-binary", @@ -107,7 +120,7 @@ func (c *precompileCfg) RegisterFlags(fs *flag.FlagSet) { ) } -func execPrecompile(cfg *precompileCfg, args []string, io *commands.IO) error { +func execPrecompile(cfg *precompileCfg, args []string, io commands.IO) error { if len(args) < 1 { return flag.ErrHelp } @@ -125,7 +138,6 @@ func execPrecompile(cfg *precompileCfg, args []string, io *commands.IO) error { if err != nil { err = fmt.Errorf("%s: precompile: %w", filepath, err) io.ErrPrintfln("%s", err.Error()) - errCount++ } } @@ -134,6 +146,27 @@ func execPrecompile(cfg *precompileCfg, args []string, io *commands.IO) error { return fmt.Errorf("%d precompile errors", errCount) } + if cfg.gobuild { + paths, err := gnoPackagesFromArgs(args) + if err != nil { + return fmt.Errorf("list packages: %w", err) + } + + errCount = 0 + for _, pkgPath := range paths { + _ = pkgPath + err = goBuildFileOrPkg(pkgPath, cfg) + if err != nil { + err = fmt.Errorf("%s: build pkg: %w", pkgPath, err) + io.ErrPrintfln("%s\n", err.Error()) + errCount++ + } + } + if errCount > 0 { + return fmt.Errorf("%d build errors", errCount) + } + } + return nil } @@ -219,3 +252,14 @@ func precompileFile(srcPath string, opts *precompileOptions) error { return nil } + +func goBuildFileOrPkg(fileOrPkg string, cfg *precompileCfg) error { + verbose := cfg.verbose + goBinary := cfg.goBinary + + if verbose { + fmt.Fprintf(os.Stderr, "%s\n", fileOrPkg) + } + + return gno.PrecompileBuildPackage(fileOrPkg, goBinary) +} diff --git a/gnovm/cmd/gno/precompile_test.go b/gnovm/cmd/gno/precompile_test.go index 56f63b0de35..f8436fad768 100644 --- a/gnovm/cmd/gno/precompile_test.go +++ b/gnovm/cmd/gno/precompile_test.go @@ -1,18 +1,26 @@ package main -import "testing" +import ( + "testing" -func TestPrecompileApp(t *testing.T) { - tc := []testMainCase{ - { - args: []string{"precompile"}, - errShouldBe: "flag: help requested", - }, + "github.com/rogpeppe/go-internal/testscript" + "github.com/stretchr/testify/require" - // {args: []string{"precompile", "..."}, stdoutShouldContain: "..."}, - // TODO: recursive - // TODO: valid files - // TODO: invalid files + "github.com/gnolang/gno/gnovm/pkg/integration" +) + +func Test_ScriptsPrecompile(t *testing.T) { + p := testscript.Params{ + Dir: "testdata/gno_precompile", + } + + if coverdir, ok := integration.ResolveCoverageDir(); ok { + err := integration.SetupTestscriptsCoverage(&p, coverdir) + require.NoError(t, err) } - testMainCaseRun(t, tc) + + err := integration.SetupGno(&p, t.TempDir()) + require.NoError(t, err) + + testscript.Run(t, p) } diff --git a/gnovm/cmd/gno/repl.go b/gnovm/cmd/gno/repl.go index 1acb96c3cb9..18a7db16568 100644 --- a/gnovm/cmd/gno/repl.go +++ b/gnovm/cmd/gno/repl.go @@ -2,13 +2,15 @@ package main import ( "bufio" - "bytes" "context" + "errors" "flag" "fmt" + "go/scanner" "os" "strings" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" "github.com/gnolang/gno/gnovm/pkg/repl" "github.com/gnolang/gno/tm2/pkg/commands" ) @@ -80,7 +82,7 @@ func execRepl(cfg *replCfg, args []string) error { } if cfg.rootDir == "" { - cfg.rootDir = guessRootDir() + cfg.rootDir = gnoenv.RootDir() } if !cfg.skipUsage { @@ -88,10 +90,10 @@ func execRepl(cfg *replCfg, args []string) error { // gno> import "gno.land/p/demo/avl" // import the p/demo/avl package // gno> func a() string { return "a" } // declare a new function named a // gno> /src // print current generated source -// gno> /editor // enter in editor mode to add several lines +// gno> /editor // enter in multi-line mode, end with ';' // gno> /reset // remove all previously inserted code // gno> println(a()) // print the result of calling a() -// gno> /exit +// gno> /exit // alternative to `) } @@ -99,30 +101,59 @@ func execRepl(cfg *replCfg, args []string) error { } func runRepl(cfg *replCfg) error { - // init repl state r := repl.NewRepl() if cfg.initialCommand != "" { handleInput(r, cfg.initialCommand) } - var multiline bool - for { - fmt.Fprint(os.Stdout, "gno> ") + fmt.Fprint(os.Stdout, "gno> ") - input, err := getInput(multiline) - if err != nil { - return err + inEdit := false + prev := "" + liner := bufio.NewScanner(os.Stdin) + + for liner.Scan() { + line := liner.Text() + + if l := strings.TrimSpace(line); l == ";" { + line, inEdit = "", false + } else if l == "/editor" { + line, inEdit = "", true + fmt.Fprintln(os.Stdout, "// enter a single ';' to quit and commit") + } + if prev != "" { + line = prev + "\n" + line + prev = "" + } + if inEdit { + fmt.Fprint(os.Stdout, "... ") + prev = line + continue } - multiline = handleInput(r, input) + if err := handleInput(r, line); err != nil { + var goScanError scanner.ErrorList + if errors.As(err, &goScanError) { + // We assune that a Go scanner error indicates an incomplete Go statement. + // Append next line and retry. + prev = line + } else { + fmt.Fprintln(os.Stderr, err) + } + } + + if prev == "" { + fmt.Fprint(os.Stdout, "gno> ") + } else { + fmt.Fprint(os.Stdout, "... ") + } } + return nil } -// handleInput reads the input string and parses it depending if it -// is a specific command, or source code. It returns true if the following -// input is expected to be on more than one line. -func handleInput(r *repl.Repl, input string) bool { +// handleInput executes specific "/" commands, or evaluates input as Gno source code. +func handleInput(r *repl.Repl, input string) error { switch strings.TrimSpace(input) { case "/reset": r.Reset() @@ -130,49 +161,14 @@ func handleInput(r *repl.Repl, input string) bool { fmt.Fprintln(os.Stdout, r.Src()) case "/exit": os.Exit(0) - case "/editor": - fmt.Fprintln(os.Stdout, "// Entering editor mode (^D to finish)") - return true case "": - // avoid to increase the repl execution counter if sending empty content - fmt.Fprintln(os.Stdout, "") - return false + // Avoid to increase the repl execution counter if no input. default: out, err := r.Process(input) if err != nil { - fmt.Fprintln(os.Stderr, err) + return err } fmt.Fprintln(os.Stdout, out) } - - return false -} - -const ( - inputBreaker = "^D" - nl = "\n" -) - -func getInput(ml bool) (string, error) { - s := bufio.NewScanner(os.Stdin) - var mlOut bytes.Buffer - for s.Scan() { - line := s.Text() - if !ml { - return line, nil - } - - if line == inputBreaker { - break - } - - mlOut.WriteString(line) - mlOut.WriteString(nl) - } - - if err := s.Err(); err != nil { - return "", err - } - - return mlOut.String(), nil + return nil } diff --git a/gnovm/cmd/gno/run.go b/gnovm/cmd/gno/run.go index 2c8d8c77350..429750c23a7 100644 --- a/gnovm/cmd/gno/run.go +++ b/gnovm/cmd/gno/run.go @@ -9,6 +9,7 @@ import ( "path/filepath" "strings" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" "github.com/gnolang/gno/gnovm/tests" "github.com/gnolang/gno/tm2/pkg/commands" @@ -20,7 +21,7 @@ type runCfg struct { expr string } -func newRunCmd(io *commands.IO) *commands.Command { +func newRunCmd(io commands.IO) *commands.Command { cfg := &runCfg{} return commands.NewCommand( @@ -48,7 +49,7 @@ func (c *runCfg) RegisterFlags(fs *flag.FlagSet) { &c.rootDir, "root-dir", "", - "clone location of github.com/gnolang/gno (gnodev tries to guess it)", + "clone location of github.com/gnolang/gno (gno binary tries to guess it)", ) fs.StringVar( @@ -59,18 +60,18 @@ func (c *runCfg) RegisterFlags(fs *flag.FlagSet) { ) } -func execRun(cfg *runCfg, args []string, io *commands.IO) error { +func execRun(cfg *runCfg, args []string, io commands.IO) error { if len(args) == 0 { return flag.ErrHelp } if cfg.rootDir == "" { - cfg.rootDir = guessRootDir() + cfg.rootDir = gnoenv.RootDir() } - stdin := io.In - stdout := io.Out - stderr := io.Err + stdin := io.In() + stdout := io.Out() + stderr := io.Err() // init store and machine testStore := tests.TestStore(cfg.rootDir, @@ -143,7 +144,7 @@ func listNonTestFiles(dir string) ([]string, error) { n := f.Name() if isGnoFile(f) && !strings.HasSuffix(n, "_test.gno") && - !strings.HasPrefix(n, "_filetest.gno") { + !strings.HasSuffix(n, "_filetest.gno") { fn = append(fn, filepath.Join(dir, n)) } } diff --git a/gnovm/cmd/gno/run_test.go b/gnovm/cmd/gno/run_test.go index e439a6bad8d..575798a78dc 100644 --- a/gnovm/cmd/gno/run_test.go +++ b/gnovm/cmd/gno/run_test.go @@ -63,6 +63,10 @@ func TestRunApp(t *testing.T) { args: []string{"run", "-expr", "WithArg(-255)", "../../tests/integ/run-package"}, stdoutShouldContain: "out of range!", }, + { + args: []string{"run", "../../tests/integ/undefined-variable-test/undefined_variables_test.gno"}, + recoverShouldContain: "--- preprocess stack ---", // should contain preprocess debug stack trace + }, // TODO: a test file // TODO: args // TODO: nativeLibs VS stdlibs diff --git a/gnovm/cmd/gno/test.go b/gnovm/cmd/gno/test.go index 8cacfd5623b..ae3f2334ecf 100644 --- a/gnovm/cmd/gno/test.go +++ b/gnovm/cmd/gno/test.go @@ -9,6 +9,7 @@ import ( "log" "os" "path/filepath" + "runtime/debug" "sort" "strings" "text/template" @@ -16,6 +17,7 @@ import ( "go.uber.org/multierr" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" "github.com/gnolang/gno/gnovm/pkg/gnomod" "github.com/gnolang/gno/gnovm/tests" @@ -37,7 +39,7 @@ type testCfg struct { withNativeFallback bool } -func newTestCmd(io *commands.IO) *commands.Command { +func newTestCmd(io commands.IO) *commands.Command { cfg := &testCfg{} return commands.NewCommand( @@ -50,7 +52,7 @@ func newTestCmd(io *commands.IO) *commands.Command { 'gno test' recompiles each package along with any files with names matching the file pattern "*_test.gno" or "*_filetest.gno". -The only supported for now is a directory (relative or absolute). +The can be directory or file path (relative or absolute). - "*_test.gno" files work like "*_test.go" files, but they contain only test functions. Benchmark and fuzz functions aren't supported yet. Similarly, only @@ -157,7 +159,7 @@ func (c *testCfg) RegisterFlags(fs *flag.FlagSet) { ) } -func execTest(cfg *testCfg, args []string, io *commands.IO) error { +func execTest(cfg *testCfg, args []string, io commands.IO) error { if len(args) < 1 { return flag.ErrHelp } @@ -179,12 +181,12 @@ func execTest(cfg *testCfg, args []string, io *commands.IO) error { // guess opts.RootDir if cfg.rootDir == "" { - cfg.rootDir = guessRootDir() + cfg.rootDir = gnoenv.RootDir() } - paths, err := gnoPackagesFromArgs(args) + paths, err := targetsFromPatterns(args) if err != nil { - return fmt.Errorf("list package paths from args: %w", err) + return fmt.Errorf("list targets from patterns: %w", err) } if len(paths) == 0 { io.ErrPrintln("no packages to test") @@ -231,7 +233,7 @@ func execTest(cfg *testCfg, args []string, io *commands.IO) error { if err != nil { return errors.New("cannot resolve build dir") } - err = goBuildFileOrPkg(tempDir, defaultBuildOptions) + err = goBuildFileOrPkg(tempDir, defaultPrecompileCfg) if err != nil { io.ErrPrintln(err) io.ErrPrintln("FAIL") @@ -279,7 +281,7 @@ func gnoTestPkg( unittestFiles, filetestFiles []string, cfg *testCfg, - io *commands.IO, + io commands.IO, ) error { var ( verbose = cfg.verbose @@ -287,9 +289,9 @@ func gnoTestPkg( runFlag = cfg.run printRuntimeMetrics = cfg.printRuntimeMetrics - stdin = io.In - stdout = io.Out - stderr = io.Err + stdin = io.In() + stdout = io.Out() + stderr = io.Err() errs error ) @@ -298,15 +300,6 @@ func gnoTestPkg( // XXX: display a warn? mode = tests.ImportModeStdlibsPreferred } - testStore := tests.TestStore( - rootDir, "", - stdin, stdout, stderr, - mode, - ) - if verbose { - testStore.SetLogStoreOps(true) - } - if !verbose { // TODO: speedup by ignoring if filter is file/*? mockOut := bytes.NewBufferString("") @@ -321,16 +314,29 @@ func gnoTestPkg( if err == nil { gnoPkgPath = modfile.Module.Mod.Path } else { - // unable to read pkgPath from gno.mod, generate a random realm path - gnoPkgPath = gno.GnoRealmPkgsPrefixBefore + random.RandStr(8) + gnoPkgPath = pkgPathFromRootDir(pkgPath, rootDir) + if gnoPkgPath == "" { + // unable to read pkgPath from gno.mod, generate a random realm path + gnoPkgPath = gno.GnoRealmPkgsPrefixBefore + random.RandStr(8) + } } memPkg := gno.ReadMemPackage(pkgPath, gnoPkgPath) // tfiles, ifiles := gno.ParseMemPackageTests(memPkg) tfiles, ifiles := parseMemPackageTests(memPkg) + testPkgName := getPkgNameFromFileset(ifiles) // run test files in pkg - { + if len(tfiles.Files) > 0 { + testStore := tests.TestStore( + rootDir, "", + stdin, stdout, stderr, + mode, + ) + if verbose { + testStore.SetLogStoreOps(true) + } + m := tests.TestMachine(testStore, stdout, gnoPkgPath) if printRuntimeMetrics { // from tm2/pkg/sdk/vm/keeper.go @@ -346,17 +352,38 @@ func gnoTestPkg( } } - // run test files in xxx_test pkg - { - testPkgName := getPkgNameFromFileset(ifiles) - if testPkgName != "" { - m := tests.TestMachine(testStore, stdout, testPkgName) - m.RunMemPackage(memPkg, true) - err := runTestFiles(m, ifiles, testPkgName, verbose, printRuntimeMetrics, runFlag, io) - if err != nil { - errs = multierr.Append(errs, err) + // test xxx_test pkg + if len(ifiles.Files) > 0 { + testStore := tests.TestStore( + rootDir, "", + stdin, stdout, stderr, + mode, + ) + if verbose { + testStore.SetLogStoreOps(true) + } + + m := tests.TestMachine(testStore, stdout, testPkgName) + + memFiles := make([]*std.MemFile, 0, len(ifiles.FileNames())+1) + for _, f := range memPkg.Files { + for _, ifileName := range ifiles.FileNames() { + if f.Name == "gno.mod" || f.Name == ifileName { + memFiles = append(memFiles, f) + break + } } } + + memPkg.Files = memFiles + memPkg.Name = testPkgName + memPkg.Path = memPkg.Path + "_test" + m.RunMemPackage(memPkg, true) + + err := runTestFiles(m, ifiles, testPkgName, verbose, printRuntimeMetrics, runFlag, io) + if err != nil { + errs = multierr.Append(errs, err) + } } } @@ -408,6 +435,35 @@ func gnoTestPkg( return errs } +// attempts to determine the full gno pkg path by analyzing the directory. +func pkgPathFromRootDir(pkgPath, rootDir string) string { + abPkgPath, err := filepath.Abs(pkgPath) + if err != nil { + log.Printf("could not determine abs path: %v", err) + return "" + } + abRootDir, err := filepath.Abs(rootDir) + if err != nil { + log.Printf("could not determine abs path: %v", err) + return "" + } + abRootDir += string(filepath.Separator) + if !strings.HasPrefix(abPkgPath, abRootDir) { + return "" + } + impPath := strings.ReplaceAll(abPkgPath[len(abRootDir):], string(filepath.Separator), "/") + for _, prefix := range [...]string{ + "examples/", + "gnovm/stdlibs/", + "gnovm/tests/stdlibs/", + } { + if strings.HasPrefix(impPath, prefix) { + return impPath[len(prefix):] + } + } + return "" +} + func runTestFiles( m *gno.Machine, files *gno.FileSet, @@ -415,9 +471,13 @@ func runTestFiles( verbose bool, printRuntimeMetrics bool, runFlag string, - io *commands.IO, -) error { - var errs error + io commands.IO, +) (errs error) { + defer func() { + if r := recover(); r != nil { + errs = multierr.Append(fmt.Errorf("panic: %v\nstack:\n%v\ngno machine: %v", r, string(debug.Stack()), m.String()), errs) + } + }() testFuncs := &testFuncs{ PackageName: pkgName, @@ -439,22 +499,15 @@ func runTestFiles( m.RunFiles(n) for _, test := range testFuncs.Tests { - if verbose { - io.ErrPrintfln("=== RUN %s", test.Name) - } - testFuncStr := fmt.Sprintf("%q", test.Name) - startedAt := time.Now() eval := m.Eval(gno.Call("runtest", testFuncStr)) - duration := time.Since(startedAt) - dstr := fmtDuration(duration) ret := eval[0].GetString() if ret == "" { err := errors.New("failed to execute unit test: %q", test.Name) errs = multierr.Append(errs, err) - io.ErrPrintfln("--- FAIL: %s (%v)", test.Name, duration) + io.ErrPrintfln("--- FAIL: %s [internal gno testing error]", test.Name) continue } @@ -463,30 +516,13 @@ func runTestFiles( err = json.Unmarshal([]byte(ret), &rep) if err != nil { errs = multierr.Append(errs, err) - io.ErrPrintfln("--- FAIL: %s (%s)", test.Name, dstr) + io.ErrPrintfln("--- FAIL: %s [internal gno testing error]", test.Name) continue } - switch { - case rep.Filtered: - io.ErrPrintfln("--- FILT: %s", test.Name) - // noop - case rep.Skipped: - if verbose { - io.ErrPrintfln("--- SKIP: %s", test.Name) - } - case rep.Failed: + if rep.Failed { err := errors.New("failed: %q", test.Name) errs = multierr.Append(errs, err) - io.ErrPrintfln("--- FAIL: %s (%s)", test.Name, dstr) - default: - if verbose { - io.ErrPrintfln("--- PASS: %s (%s)", test.Name, dstr) - } - } - - if rep.Output != "" && (verbose || rep.Failed) { - io.ErrPrintfln("output: %s", rep.Output) } if printRuntimeMetrics { @@ -514,12 +550,8 @@ func runTestFiles( // mirror of stdlibs/testing.Report type report struct { - Name string - Verbose bool - Failed bool - Skipped bool - Filtered bool - Output string + Failed bool + Skipped bool } var testmainTmpl = template.Must(template.New("testmain").Parse(` diff --git a/gnovm/cmd/gno/test_test.go b/gnovm/cmd/gno/test_test.go index f5b069a5f03..98320f41cc9 100644 --- a/gnovm/cmd/gno/test_test.go +++ b/gnovm/cmd/gno/test_test.go @@ -1,11 +1,29 @@ package main import ( + "os" + "strconv" "testing" + "github.com/gnolang/gno/gnovm/pkg/integration" "github.com/rogpeppe/go-internal/testscript" + "github.com/stretchr/testify/require" ) -func TestTest(t *testing.T) { - testscript.Run(t, setupTestScript(t, "testdata/gno_test")) +func Test_ScriptsTest(t *testing.T) { + updateScripts, _ := strconv.ParseBool(os.Getenv("UPDATE_SCRIPTS")) + p := testscript.Params{ + UpdateScripts: updateScripts, + Dir: "testdata/gno_test", + } + + if coverdir, ok := integration.ResolveCoverageDir(); ok { + err := integration.SetupTestscriptsCoverage(&p, coverdir) + require.NoError(t, err) + } + + err := integration.SetupGno(&p, t.TempDir()) + require.NoError(t, err) + + testscript.Run(t, p) } diff --git a/gnovm/cmd/gno/testdata/gno_precompile/01_no_args.txtar b/gnovm/cmd/gno/testdata/gno_precompile/01_no_args.txtar new file mode 100644 index 00000000000..239d3860e11 --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_precompile/01_no_args.txtar @@ -0,0 +1,6 @@ +# Run gno precompile without args + +! gno precompile + +! stdout .+ +stderr 'USAGE' diff --git a/gnovm/cmd/gno/testdata/gno_precompile/02_empty_dir.txtar b/gnovm/cmd/gno/testdata/gno_precompile/02_empty_dir.txtar new file mode 100644 index 00000000000..7afc7ed0683 --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_precompile/02_empty_dir.txtar @@ -0,0 +1,6 @@ +# Run gno precompile on an empty dir + +gno precompile . + +! stdout .+ +! stderr .+ diff --git a/gnovm/cmd/gno/testdata/gno_precompile/03_gno_files_parse_error.txtar b/gnovm/cmd/gno/testdata/gno_precompile/03_gno_files_parse_error.txtar new file mode 100644 index 00000000000..f4cd79c1db5 --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_precompile/03_gno_files_parse_error.txtar @@ -0,0 +1,23 @@ +# Run gno precompile with gno files with parse errors + +! gno precompile . + +! stdout .+ +stderr 'precompile: parse: main.gno:3:1: expected declaration, found invalid' +stderr 'precompile: parse: sub/sub.gno:3:1: expected declaration, found invalid' + +# no *.gen.go files are created +! exec test -f main.gno.gen.go +! exec test -f sub/sub.gno.gen.go + +-- main.gno -- +package main + +invalid + +func main() {} + +-- sub/sub.gno -- +package sub + +invalid diff --git a/gnovm/cmd/gno/testdata/gno_precompile/04_valid_gno_files.txtar b/gnovm/cmd/gno/testdata/gno_precompile/04_valid_gno_files.txtar new file mode 100644 index 00000000000..0f9d909f627 --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_precompile/04_valid_gno_files.txtar @@ -0,0 +1,32 @@ +# Run gno precompile with valid gno files + +gno precompile . + +! stdout .+ +! stderr .+ + +cmp main.gno.gen.go main.gno.gen.go.golden +cmp sub/sub.gno.gen.go sub/sub.gno.gen.go.golden + +-- main.gno -- +package main + +func main(){} + +-- sub/sub.gno -- +package sub + +-- main.gno.gen.go.golden -- +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno + +package main + +func main() {} +-- sub/sub.gno.gen.go.golden -- +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno + +package sub diff --git a/gnovm/cmd/gno/testdata/gno_precompile/05_skip_fmt_flag.txtar b/gnovm/cmd/gno/testdata/gno_precompile/05_skip_fmt_flag.txtar new file mode 100644 index 00000000000..0f7780efdad --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_precompile/05_skip_fmt_flag.txtar @@ -0,0 +1,34 @@ +# Run gno precompile with -skip-fmt flag +# NOTE(tb): this flag doesn't actually prevent the code format, because +# `gnolang.Precompile()` calls `format.Node()`. + +gno precompile -skip-fmt . + +! stdout .+ +! stderr .+ + +cmp main.gno.gen.go main.gno.gen.go.golden +cmp sub/sub.gno.gen.go sub/sub.gno.gen.go.golden + +-- main.gno -- +package main + +func main(){} + +-- sub/sub.gno -- +package sub + +-- main.gno.gen.go.golden -- +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno + +package main + +func main() {} +-- sub/sub.gno.gen.go.golden -- +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno + +package sub diff --git a/gnovm/cmd/gno/testdata/gno_precompile/06_build_flag.txtar b/gnovm/cmd/gno/testdata/gno_precompile/06_build_flag.txtar new file mode 100644 index 00000000000..b93fae95fcf --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_precompile/06_build_flag.txtar @@ -0,0 +1,36 @@ +# Run gno precompile with -gobuild flag + +gno precompile -gobuild . + +! stdout .+ +! stderr .+ + +cmp main.gno.gen.go main.gno.gen.go.golden +cmp sub/sub.gno.gen.go sub/sub.gno.gen.go.golden + +-- main.gno -- +package main + +func main() { + var x = 1 + _=x +} +-- sub/sub.gno -- +package sub +-- main.gno.gen.go.golden -- +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno + +package main + +func main() { + var x = 1 + _ = x +} +-- sub/sub.gno.gen.go.golden -- +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno + +package sub diff --git a/gnovm/cmd/gno/testdata/gno_precompile/07_build_flag_with_build_error.txtar b/gnovm/cmd/gno/testdata/gno_precompile/07_build_flag_with_build_error.txtar new file mode 100644 index 00000000000..6543623b266 --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_precompile/07_build_flag_with_build_error.txtar @@ -0,0 +1,34 @@ +# Run gno precompile with -gobuild flag + +! gno precompile -gobuild . + +! stdout .+ +stderr './main.gno.gen.go:8:6: x declared and not used' + +cmp main.gno.gen.go main.gno.gen.go.golden +cmp sub/sub.gno.gen.go sub/sub.gno.gen.go.golden + +-- main.gno -- +package main + +func main() { + var x = 1 +} +-- sub/sub.gno -- +package sub +-- main.gno.gen.go.golden -- +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno + +package main + +func main() { + var x = 1 +} +-- sub/sub.gno.gen.go.golden -- +// Code generated by github.com/gnolang/gno. DO NOT EDIT. + +//go:build gno + +package sub diff --git a/gnovm/cmd/gno/testdata/gno_precompile/08_build_flag_with_parse_error.txtar b/gnovm/cmd/gno/testdata/gno_precompile/08_build_flag_with_parse_error.txtar new file mode 100644 index 00000000000..4d2b5ad8041 --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_precompile/08_build_flag_with_parse_error.txtar @@ -0,0 +1,14 @@ +# Run gno precompile with -gobuild flag on file with parse error + +! gno precompile -gobuild . + +! stdout .+ +stderr 'main.gno: precompile: parse: main.gno:3:1: expected declaration, found invalid' + +# no *.gen.go files are created +! exec test -f main.gno.gen.go + +-- main.gno -- +package main + +invalid diff --git a/gnovm/cmd/gno/testdata/gno_precompile/09_gno_files_whitelist_error.txtar b/gnovm/cmd/gno/testdata/gno_precompile/09_gno_files_whitelist_error.txtar new file mode 100644 index 00000000000..f2386716ce6 --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_precompile/09_gno_files_whitelist_error.txtar @@ -0,0 +1,26 @@ +# Run gno precompile with gno files with whitelist errors + +! gno precompile . + +! stdout .+ +stderr 'main.gno: precompile: import "xxx" is not in the whitelist' +stderr 'sub/sub.gno: precompile: import "xxx" is not in the whitelist' + +# no *.gen.go files are created +! exec test -f main.gno.gen.go +! exec test -f sub/sub.gno.gen.go + +-- main.gno -- +package main + +import ( + "std" + "xxx" +) + +func main() {} + +-- sub/sub.gno -- +package sub + +import "xxx" diff --git a/gnovm/cmd/gno/testdata/gno_test/empty_dir.txtar b/gnovm/cmd/gno/testdata/gno_test/empty_dir.txtar index 73f0da72dfe..ffed64ab9c7 100644 --- a/gnovm/cmd/gno/testdata/gno_test/empty_dir.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/empty_dir.txtar @@ -2,5 +2,10 @@ gno test . +! stdout .+ +stderr '[no test files]' + +gno test ./... + ! stdout .+ stderr 'no packages to test' diff --git a/gnovm/cmd/gno/testdata/gno_test/empty_gno1.txtar b/gnovm/cmd/gno/testdata/gno_test/empty_gno1.txtar index ae73b3ab275..cc673bb38ff 100644 --- a/gnovm/cmd/gno/testdata/gno_test/empty_gno1.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/empty_gno1.txtar @@ -3,7 +3,7 @@ gno test . ! stdout .+ -stderr '\? \./\. \[no test files\]' +stderr '\? \. \[no test files\]' ! gno test --precompile . diff --git a/gnovm/cmd/gno/testdata/gno_test/error_correct.txtar b/gnovm/cmd/gno/testdata/gno_test/error_correct.txtar index c7d3187424c..a66d831b48c 100644 --- a/gnovm/cmd/gno/testdata/gno_test/error_correct.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/error_correct.txtar @@ -5,7 +5,7 @@ gno test -verbose . stdout 'Machine\.RunMain\(\) panic: oups' stderr '=== RUN file/x_filetest.gno' stderr '--- PASS: file/x_filetest.gno \(\d\.\d\ds\)' -stderr 'ok \./\. \d\.\d\ds' +stderr 'ok \. \d\.\d\ds' -- x_filetest.gno -- package main diff --git a/gnovm/cmd/gno/testdata/gno_test/failing_filetest.txtar b/gnovm/cmd/gno/testdata/gno_test/failing_filetest.txtar index bc3efc1a8c9..c739c1ce328 100644 --- a/gnovm/cmd/gno/testdata/gno_test/failing_filetest.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/failing_filetest.txtar @@ -9,8 +9,8 @@ stderr 'panic: fail on failing_filetest.gno: got unexpected error: beep boop' ! gno test -verbose --precompile . stdout 'Machine.RunMain\(\) panic: beep boop' -stderr '=== PREC \./\.' -stderr '=== BUILD \./\.' +stderr '=== PREC \.' +stderr '=== BUILD \.' stderr '=== RUN file/failing_filetest.gno' stderr 'panic: fail on failing_filetest.gno: got unexpected error: beep boop' diff --git a/gnovm/cmd/gno/testdata/gno_test/flag_run.txtar b/gnovm/cmd/gno/testdata/gno_test/flag_run.txtar index 838206d15f9..d950a6aa7bf 100644 --- a/gnovm/cmd/gno/testdata/gno_test/flag_run.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/flag_run.txtar @@ -2,51 +2,46 @@ gno test . -stdout '=== RUN TestRun/hello' # show that t.Run doesn't care about -verbose -stdout '=== RUN TestRun/hi_you' -stdout '=== RUN TestRun/hi_me' - gno test ./run_test.gno -stdout '=== RUN TestRun/hello' # show that t.Run doesn't care about -verbose -stdout '=== RUN TestRun/hi_you' -stdout '=== RUN TestRun/hi_me' - gno test -verbose . -stdout '=== RUN TestRun/hello' -stdout '=== RUN TestRun/hi_you' -stdout '=== RUN TestRun/hi_me' +! stdout .+ +stderr '=== RUN TestRun/hello' +stderr '=== RUN TestRun/hi_you' +stderr '=== RUN TestRun/hi_me' stderr '=== RUN TestRun' stderr '--- PASS: TestRun' gno test -verbose -run .* . -stdout '=== RUN TestRun/hello' -stdout '=== RUN TestRun/hi_you' -stdout '=== RUN TestRun/hi_me' +! stdout .+ +stderr '=== RUN TestRun/hello' +stderr '=== RUN TestRun/hi_you' +stderr '=== RUN TestRun/hi_me' stderr '=== RUN TestRun' stderr '--- PASS: TestRun' gno test -verbose -run NotExists . ! stdout .+ -stderr '=== RUN TestRun' -stderr '--- FILT: TestRun' +! stderr '=== RUN TestRun' gno test -verbose -run .*/hello . -stdout '=== RUN TestRun/hello' -! stdout '=== RUN TestRun/hi_you' -! stdout '=== RUN TestRun/hi_me' +! stdout .+ +stderr '=== RUN TestRun/hello' +! stderr '=== RUN TestRun/hi_you' +! stderr '=== RUN TestRun/hi_me' stderr '=== RUN TestRun' stderr '--- PASS: TestRun' gno test -verbose -run .*/hi . -! stdout '=== RUN TestRun/hello' -stdout '=== RUN TestRun/hi_you' -stdout '=== RUN TestRun/hi_me' +! stdout .+ +! stderr '=== RUN TestRun/hello' +stderr '=== RUN TestRun/hi_you' +stderr '=== RUN TestRun/hi_me' stderr '=== RUN TestRun' stderr '--- PASS: TestRun' @@ -58,25 +53,28 @@ stderr '--- PASS: TestRun' gno test -verbose -run Run/.* . -stdout '=== RUN TestRun/hello' -stdout '=== RUN TestRun/hi_you' -stdout '=== RUN TestRun/hi_me' +! stdout .+ +stderr '=== RUN TestRun/hello' +stderr '=== RUN TestRun/hi_you' +stderr '=== RUN TestRun/hi_me' stderr '=== RUN TestRun' stderr '--- PASS: TestRun' gno test -verbose -run Run/ . -stdout '=== RUN TestRun/hello' -stdout '=== RUN TestRun/hi_you' -stdout '=== RUN TestRun/hi_me' +! stdout .+ +stderr '=== RUN TestRun/hello' +stderr '=== RUN TestRun/hi_you' +stderr '=== RUN TestRun/hi_me' stderr '=== RUN TestRun' stderr '--- PASS: TestRun' gno test -verbose -run Run/hello . -stdout '=== RUN TestRun/hello' -! stdout '=== RUN TestRun/hi_you' -! stdout '=== RUN TestRun/hi_me' +! stdout .+ +stderr '=== RUN TestRun/hello' +! stderr '=== RUN TestRun/hi_you' +! stderr '=== RUN TestRun/hi_me' stderr '=== RUN TestRun' stderr '--- PASS: TestRun' diff --git a/gnovm/cmd/gno/testdata/gno_test/lint_bad_import.txtar b/gnovm/cmd/gno/testdata/gno_test/lint_bad_import.txtar new file mode 100644 index 00000000000..946e1bcba35 --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_test/lint_bad_import.txtar @@ -0,0 +1,19 @@ +# testing gno lint command: bad import error + +! gno lint ./bad_file.gno + +cmp stdout stdout.golden +cmp stderr stderr.golden + +-- bad_file.gno -- +package main + +import "python" + +func main() { + fmt.Println("Hello", 42) +} + +-- stdout.golden -- +-- stderr.golden -- +bad_file.gno:1: unknown import path python (code=2). diff --git a/gnovm/cmd/gno/testdata/gno_test/lint_file_error.txtar b/gnovm/cmd/gno/testdata/gno_test/lint_file_error.txtar new file mode 100644 index 00000000000..9482eeb1f4f --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_test/lint_file_error.txtar @@ -0,0 +1,20 @@ +# gno lint: test file error + +! gno lint ./i_have_error_test.gno + +cmp stdout stdout.golden +cmp stderr stderr.golden + +-- i_have_error_test.gno -- +package main + +import "fmt" + +func TestIHaveSomeError() { + i := undefined_variable + fmt.Println("Hello", 42) +} + +-- stdout.golden -- +-- stderr.golden -- +i_have_error_test.gno:6: name undefined_variable not declared (code=2). diff --git a/gnovm/cmd/gno/testdata/gno_test/lint_file_error_txtar b/gnovm/cmd/gno/testdata/gno_test/lint_file_error_txtar new file mode 100644 index 00000000000..9482eeb1f4f --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_test/lint_file_error_txtar @@ -0,0 +1,20 @@ +# gno lint: test file error + +! gno lint ./i_have_error_test.gno + +cmp stdout stdout.golden +cmp stderr stderr.golden + +-- i_have_error_test.gno -- +package main + +import "fmt" + +func TestIHaveSomeError() { + i := undefined_variable + fmt.Println("Hello", 42) +} + +-- stdout.golden -- +-- stderr.golden -- +i_have_error_test.gno:6: name undefined_variable not declared (code=2). diff --git a/gnovm/cmd/gno/testdata/gno_test/lint_no_error.txtar b/gnovm/cmd/gno/testdata/gno_test/lint_no_error.txtar new file mode 100644 index 00000000000..95356b1ba2b --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_test/lint_no_error.txtar @@ -0,0 +1,18 @@ +# testing simple gno lint command with any error + +gno lint ./good_file.gno + +cmp stdout stdout.golden +cmp stdout stderr.golden + +-- good_file.gno -- +package main + +import "fmt" + +func main() { + fmt.Println("Hello", 42) +} + +-- stdout.golden -- +-- stderr.golden -- diff --git a/gnovm/cmd/gno/testdata/gno_test/lint_no_gnomod.txtar b/gnovm/cmd/gno/testdata/gno_test/lint_no_gnomod.txtar new file mode 100644 index 00000000000..52daa6f0e9b --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_test/lint_no_gnomod.txtar @@ -0,0 +1,19 @@ +# gno lint: no gnomod + +! gno lint . + +cmp stdout stdout.golden +cmp stderr stderr.golden + +-- good_file.gno -- +package main + +import "fmt" + +func main() { + fmt.Println("Hello", 42) +} + +-- stdout.golden -- +-- stderr.golden -- +./.: missing 'gno.mod' file (code=1). diff --git a/gnovm/cmd/gno/testdata/gno_test/lint_not_declared.txtar b/gnovm/cmd/gno/testdata/gno_test/lint_not_declared.txtar new file mode 100644 index 00000000000..7bd74a34855 --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_test/lint_not_declared.txtar @@ -0,0 +1,20 @@ +# testing gno lint command: not declared error + +! gno lint ./bad_file.gno + +cmp stdout stdout.golden +cmp stderr stderr.golden + +-- bad_file.gno -- +package main + +import "fmt" + +func main() { + hello.Foo() + fmt.Println("Hello", 42) +} + +-- stdout.golden -- +-- stderr.golden -- +bad_file.gno:6: name hello not declared (code=2). diff --git a/gnovm/cmd/gno/testdata/gno_test/minim1.txtar b/gnovm/cmd/gno/testdata/gno_test/minim1.txtar index 231ef4a270c..b0a77186086 100644 --- a/gnovm/cmd/gno/testdata/gno_test/minim1.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/minim1.txtar @@ -3,7 +3,7 @@ gno test . ! stdout .+ -stderr '\? \./\. \[no test files\]' +stderr '\? \. \[no test files\]' -- minim.gno -- package minim diff --git a/gnovm/cmd/gno/testdata/gno_test/minim2.txtar b/gnovm/cmd/gno/testdata/gno_test/minim2.txtar index 038dfd19289..3c4d1d085f0 100644 --- a/gnovm/cmd/gno/testdata/gno_test/minim2.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/minim2.txtar @@ -3,7 +3,7 @@ gno test . ! stdout .+ -stderr 'ok \./\. \d\.\d\ds' +stderr 'ok \. \d\.\d\ds' -- minim.gno -- package minim diff --git a/gnovm/cmd/gno/testdata/gno_test/minim3.txtar b/gnovm/cmd/gno/testdata/gno_test/minim3.txtar index 8e657104801..ac8ae0c41d4 100644 --- a/gnovm/cmd/gno/testdata/gno_test/minim3.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/minim3.txtar @@ -3,7 +3,7 @@ gno test . ! stdout .+ -stderr 'ok \./\. \d\.\d\ds' +stderr 'ok \. \d\.\d\ds' -- minim.gno -- package minim diff --git a/gnovm/cmd/gno/testdata/gno_test/no_args.txtar b/gnovm/cmd/gno/testdata/gno_test/no_args.txtar index e69d0994fdc..bd9cd4fc965 100644 --- a/gnovm/cmd/gno/testdata/gno_test/no_args.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/no_args.txtar @@ -3,4 +3,4 @@ ! gno test ! stdout .+ -stderr 'flag: help requested' +stderr 'USAGE' diff --git a/gnovm/cmd/gno/testdata/gno_test/output_correct.txtar b/gnovm/cmd/gno/testdata/gno_test/output_correct.txtar index ce12874f669..4e5495ab839 100644 --- a/gnovm/cmd/gno/testdata/gno_test/output_correct.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/output_correct.txtar @@ -5,7 +5,7 @@ gno test -verbose . ! stdout .+ # stdout should be empty stderr '=== RUN file/x_filetest.gno' stderr '--- PASS: file/x_filetest.gno \(\d\.\d\ds\)' -stderr 'ok \./\. \d\.\d\ds' +stderr 'ok \. \d\.\d\ds' -- x_filetest.gno -- package main diff --git a/gnovm/cmd/gno/testdata/gno_test/output_sync.txtar b/gnovm/cmd/gno/testdata/gno_test/output_sync.txtar index 85fec4ab316..b21db788924 100644 --- a/gnovm/cmd/gno/testdata/gno_test/output_sync.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/output_sync.txtar @@ -5,7 +5,7 @@ gno test -verbose . -update-golden-tests ! stdout .+ # stdout should be empty stderr '=== RUN file/x_filetest.gno' stderr '--- PASS: file/x_filetest.gno \(\d\.\d\ds\)' -stderr 'ok \./\. \d\.\d\ds' +stderr 'ok \. \d\.\d\ds' cmp x_filetest.gno x_filetest.gno.golden diff --git a/gnovm/cmd/gno/testdata/gno_test/panic.txtar b/gnovm/cmd/gno/testdata/gno_test/panic.txtar new file mode 100644 index 00000000000..970b326edac --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_test/panic.txtar @@ -0,0 +1,20 @@ +# Test panic output in a test + +! gno test . + +! stdout .+ +stderr '--- FAIL: TestPanic' +stderr 'panic: hello world' +stderr 'FAIL' + +-- panic.gno -- +package valid + +-- panic_test.gno -- +package valid + +import "testing" + +func TestPanic(t *testing.T) { + panic("hello world") +} diff --git a/gnovm/cmd/gno/testdata/gno_test/pkg_underscore_test.txtar b/gnovm/cmd/gno/testdata/gno_test/pkg_underscore_test.txtar new file mode 100644 index 00000000000..d0455dce53c --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_test/pkg_underscore_test.txtar @@ -0,0 +1,69 @@ +# Test a pkg name with _test as suffix + +# Set up GNOROOT in the current directory. +mkdir $WORK/gnovm +symlink $WORK/gnovm/stdlibs -> $GNOROOT/gnovm/stdlibs +env GNOROOT=$WORK + +gno test -verbose ./examples/gno.land/p/demo/hello + +stderr '=== RUN TestHello' +stderr '--- PASS: TestHello.*' + +stderr '=== RUN TestHullo' +stderr '--- PASS: TestHullo.*' + +stderr '=== RUN file/z0_filetest.*' + +-- examples/gno.land/p/demo/hello/gno.mod -- +module gno.land/p/demo/hello + +-- examples/gno.land/p/demo/hello/hello.gno -- +package hello + +var Name = "foo" + +-- examples/gno.land/p/demo/hello/hello_test.gno -- +package hello_test + +import ( + "testing" + + "gno.land/p/demo/hello" +) + +var Name = "bar" + +func TestHello(t *testing.T) { + s := "hello " + hello.Name + " " + Name + const want = "hello foo bar" + if s != want { + t.Errorf("got: %q want %q", s, want) + } +} + +-- examples/gno.land/p/demo/hello/hullo_test.gno -- +package hello + +import ( + "testing" +) + +func TestHullo(t *testing.T) { + s := "hullo " + Name + const want = "hullo foo" + if s != want { + t.Errorf("got: %q want %q", s, want) + } +} + +-- examples/gno.land/p/demo/hello/z0_filetest.gno -- +package main + +import "gno.land/p/demo/hello" + +func main() { + println("filetest " + hello.Name) +} + +// Output: filetest foo diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar b/gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar index f376e61d9a4..f17f28055f2 100644 --- a/gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/realm_correct.txtar @@ -5,7 +5,7 @@ gno test -verbose . ! stdout .+ # stdout should be empty stderr '=== RUN file/x_filetest.gno' stderr '--- PASS: file/x_filetest.gno \(\d\.\d\ds\)' -stderr 'ok \./\. \d\.\d\ds' +stderr 'ok \. \d\.\d\ds' -- x_filetest.gno -- // PKGPATH: gno.land/r/x @@ -55,6 +55,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/x", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar b/gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar index 3922c34fec8..38794a3e645 100644 --- a/gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/realm_incorrect.txtar @@ -7,7 +7,7 @@ stderr '=== RUN file/x_filetest.gno' stderr 'panic: fail on x_filetest.gno: diff:' stderr '--- Expected' stderr '\+\+\+ Actual' -stderr '@@ -1 \+1,64 @@' +stderr '@@ -1 \+1,66 @@' stderr '-xxx' stderr '\+switchrealm\["gno.land/r/x"\]' diff --git a/gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar b/gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar index 918fb0b88c9..e8c643af0ba 100644 --- a/gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/realm_sync.txtar @@ -5,7 +5,7 @@ gno test -verbose . -update-golden-tests ! stdout .+ # stdout should be empty stderr '=== RUN file/x_filetest.gno' stderr '--- PASS: file/x_filetest.gno \(\d\.\d\ds\)' -stderr 'ok \./\. \d\.\d\ds' +stderr 'ok \. \d\.\d\ds' cmp x_filetest.gno x_filetest.gno.golden @@ -70,6 +70,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/x", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/cmd/gno/testdata/gno_test/recover.txtar b/gnovm/cmd/gno/testdata/gno_test/recover.txtar new file mode 100644 index 00000000000..b08ce6c9593 --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_test/recover.txtar @@ -0,0 +1,62 @@ +# Test recovering + +gno test -verbose -run 'TestRecover$' . + +! stdout .+ +stderr 'recovered bad panic!' +stderr '--- PASS' +stderr 'ok ' + +gno test -verbose -run 'TestRecoverSkip$' . + +! stdout .+ +stderr 'skipped' +! stderr 'recovered.*testing.Recover' +stderr '--- SKIP' +stderr 'ok ' + +gno test -verbose -run 'TestBadRecoverSkip$' . + +! stdout .+ +# should contain warning about using testing.Recover +stderr 'recovered.*testing.Recover' +# the test will still be marked as skipped +stderr '--- SKIP' +stderr 'ok ' + +-- recov.gno -- +package recov + +-- recov_test.gno -- +package recov + +import "testing" + +func TestRecover(t *testing.T) { + defer func() { + err := testing.Recover() + t.Log("recovered", err) + }() + + panic("bad panic!") +} + +func TestRecoverSkip(t *testing.T) { + defer func() { + err := testing.Recover() + t.Log("recovered", err) + }() + + t.Skip("skipped") + panic("bad panic!") +} + +func TestBadRecoverSkip(t *testing.T) { + defer func() { + err := recover() + t.Log("recovered", err) + }() + + t.Skip("skipped") + panic("bad panic!") +} diff --git a/gnovm/cmd/gno/testdata/gno_test/skip.txtar b/gnovm/cmd/gno/testdata/gno_test/skip.txtar new file mode 100644 index 00000000000..8e15a15fd88 --- /dev/null +++ b/gnovm/cmd/gno/testdata/gno_test/skip.txtar @@ -0,0 +1,24 @@ +# Test skipping a test + +gno test -verbose . + +! stdout .+ +stderr 'Hey' +stderr 'I.m on strike!' +! stderr 'this shouldn.t print' +stderr '--- SKIP: TestSkip' +stderr 'ok ' + +-- skip.gno -- +package skip + +-- skip_test.gno -- +package skip + +import "testing" + +func TestSkip(t *testing.T) { + t.Log("Hey") + t.Skip("I'm on strike!") + t.Log("so this shouldn't print") +} diff --git a/gnovm/cmd/gno/testdata/gno_test/valid_filetest.txtar b/gnovm/cmd/gno/testdata/gno_test/valid_filetest.txtar index 51b5f323654..545607a9082 100644 --- a/gnovm/cmd/gno/testdata/gno_test/valid_filetest.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/valid_filetest.txtar @@ -3,14 +3,14 @@ gno test . ! stdout .+ -stderr 'ok \./\. \d\.\d\ds' +stderr 'ok \. \d\.\d\ds' gno test -verbose . ! stdout .+ stderr '=== RUN file/valid_filetest.gno' stderr '--- PASS: file/valid_filetest.gno \(\d\.\d\ds\)' -stderr 'ok \./\. \d\.\d\ds' +stderr 'ok \. \d\.\d\ds' -- valid.gno -- package valid diff --git a/gnovm/cmd/gno/testdata/gno_test/valid_test.txtar b/gnovm/cmd/gno/testdata/gno_test/valid_test.txtar index cb5f7286f60..9590626776c 100644 --- a/gnovm/cmd/gno/testdata/gno_test/valid_test.txtar +++ b/gnovm/cmd/gno/testdata/gno_test/valid_test.txtar @@ -3,7 +3,12 @@ gno test . ! stdout .+ -stderr 'ok \./\. \d\.\d\ds' +stderr 'ok \. \d\.\d\ds' + +gno test ./... + +! stdout .+ +stderr 'ok \. \d\.\d\ds' -- valid.gno -- package valid diff --git a/gnovm/cmd/gno/util.go b/gnovm/cmd/gno/util.go index 8288539c97b..a8e835d4759 100644 --- a/gnovm/cmd/gno/util.go +++ b/gnovm/cmd/gno/util.go @@ -5,13 +5,13 @@ import ( "go/ast" "io" "io/fs" - "log" "os" - "os/exec" "path/filepath" + "regexp" "strings" "time" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" ) @@ -105,24 +105,91 @@ func gnoPackagesFromArgs(args []string) ([]string, error) { return paths, nil } -func fmtDuration(d time.Duration) string { - return fmt.Sprintf("%.2fs", d.Seconds()) -} +// targetsFromPatterns returns a list of target paths that match the patterns. +// Each pattern can represent a file or a directory, and if the pattern +// includes "/...", the "..." is treated as a wildcard, matching any string. +// Intended to be used by gno commands such as `gno test`. +func targetsFromPatterns(patterns []string) ([]string, error) { + paths := []string{} + for _, p := range patterns { + var match func(string) bool + patternLookup := false + dirToSearch := p + + // Check if the pattern includes `/...` + if strings.Contains(p, "/...") { + index := strings.Index(p, "/...") + if index != -1 { + dirToSearch = p[:index] // Extract the directory path to search + } + match = matchPattern(strings.TrimPrefix(p, "./")) + patternLookup = true + } + + info, err := os.Stat(dirToSearch) + if err != nil { + return nil, fmt.Errorf("invalid file or package path: %w", err) + } + + // If the pattern is a file or a directory + // without `/...`, add it to the list. + if !info.IsDir() || !patternLookup { + paths = append(paths, p) + continue + } + + // the pattern is a dir containing `/...`, walk the dir recursively and + // look for directories containing at least one .gno file and match pattern. + visited := map[string]bool{} // used to run the builder only once per folder. + err = filepath.WalkDir(dirToSearch, func(curpath string, f fs.DirEntry, err error) error { + if err != nil { + return fmt.Errorf("%s: walk dir: %w", dirToSearch, err) + } + // Skip directories and non ".gno" files. + if f.IsDir() || !isGnoFile(f) { + return nil + } + + parentDir := filepath.Dir(curpath) + if _, found := visited[parentDir]; found { + return nil + } -func guessRootDir() string { - // try to get the root directory from the GNOROOT environment variable. - if rootdir := os.Getenv("GNOROOT"); rootdir != "" { - return filepath.Clean(rootdir) + visited[parentDir] = true + if match(parentDir) { + paths = append(paths, parentDir) + } + + return nil + }) + if err != nil { + return nil, err + } } + return paths, nil +} - // if GNOROOT is not set, try to guess the root directory using the `go list` command. - cmd := exec.Command("go", "list", "-m", "-mod=mod", "-f", "{{.Dir}}", "github.com/gnolang/gno") - out, err := cmd.CombinedOutput() - if err != nil { - log.Fatal("can't guess --root-dir, please fill it manually or define the GNOROOT environment variable globally.") +// matchPattern(pattern)(name) reports whether +// name matches pattern. Pattern is a limited glob +// pattern in which '...' means 'any string' and there +// is no other special syntax. +// Simplified version of go source's matchPatternInternal +// (see $GOROOT/src/cmd/internal/pkgpattern) +func matchPattern(pattern string) func(name string) bool { + re := regexp.QuoteMeta(pattern) + re = strings.Replace(re, `\.\.\.`, `.*`, -1) + // Special case: foo/... matches foo too. + if strings.HasSuffix(re, `/.*`) { + re = re[:len(re)-len(`/.*`)] + `(/.*)?` + } + reg := regexp.MustCompile(`^` + re + `$`) + return func(name string) bool { + return reg.MatchString(name) } - rootDir := strings.TrimSpace(string(out)) - return rootDir +} + +func fmtDuration(d time.Duration) string { + return fmt.Sprintf("%.2fs", d.Seconds()) } // makeTestGoMod creates the temporary go.mod for test @@ -158,7 +225,7 @@ func ResolvePath(output string, path importPath) (string, error) { if err != nil { return "", err } - pkgPath := strings.TrimPrefix(absPkgPath, guessRootDir()) + pkgPath := strings.TrimPrefix(absPkgPath, gnoenv.RootDir()) return filepath.Join(absOutput, pkgPath), nil } diff --git a/gnovm/cmd/gno/util_test.go b/gnovm/cmd/gno/util_test.go new file mode 100644 index 00000000000..9e9659bfe4f --- /dev/null +++ b/gnovm/cmd/gno/util_test.go @@ -0,0 +1,297 @@ +package main + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestMatchPattern(t *testing.T) { + tests := []struct { + pattern string + names []string + expected []bool + }{ + { + pattern: "foo", + names: []string{"foo", "bar", "baz", "foo/bar"}, + expected: []bool{true, false, false, false}, + }, + { + pattern: "foo/...", + names: []string{"foo", "foo/bar", "foo/bar/baz", "bar", "baz"}, + expected: []bool{true, true, true, false, false}, + }, + { + pattern: "foo/bar/...", + names: []string{"foo/bar", "foo/bar/baz", "foo/baz/bar", "foo", "bar"}, + expected: []bool{true, true, false, false, false}, + }, + { + pattern: "foo/.../baz", + names: []string{"foo/bar", "foo/bar/baz", "foo/baz/bar", "foo", "bar"}, + expected: []bool{false, true, false, false, false}, + }, + { + pattern: "foo/.../baz/...", + names: []string{"foo/bar/baz", "foo/baz/bar", "foo/bar/baz/qux", "foo/baz/bar/qux"}, + expected: []bool{true, false, true, false}, + }, + { + pattern: "...", + names: []string{"foo", "bar", "baz", "foo/bar", "foo/bar/baz"}, + expected: []bool{true, true, true, true, true}, + }, + { + pattern: ".../bar", + names: []string{"foo", "bar", "baz", "foo/bar", "foo/bar/baz"}, + expected: []bool{false, false, false, true, false}, + }, + } + + for _, test := range tests { + t.Run(test.pattern, func(t *testing.T) { + matchFunc := matchPattern(test.pattern) + for i, name := range test.names { + res := matchFunc(name) + assert.Equal(t, test.expected[i], res, "Expected: %v, Got: %v", test.expected[i], res) + } + }) + } +} + +func TestTargetsFromPatterns(t *testing.T) { + tmpDir := t.TempDir() + createGnoPackages(t, tmpDir) + + for _, tc := range []struct { + desc string + in, expected []string + errorShouldContain string + }{ + { + desc: "valid1", + in: []string{ + tmpDir, + }, + expected: []string{ + tmpDir, + }, + }, + { + desc: "valid2", + in: []string{ + tmpDir + "/foo", + }, + expected: []string{ + filepath.Join(tmpDir, "foo"), + }, + }, + { + desc: "valid_recursive1", + in: []string{ + tmpDir + "/...", + }, + expected: []string{ + filepath.Join(tmpDir, "foo"), + filepath.Join(tmpDir, "bar"), + filepath.Join(tmpDir, "baz"), + filepath.Join(tmpDir, "foo", "qux"), + filepath.Join(tmpDir, "bar", "quux"), + filepath.Join(tmpDir, "foo", "qux", "corge"), + }, + }, + { + desc: "valid_recursive2", + in: []string{ + tmpDir + "/foo/...", + }, + expected: []string{ + filepath.Join(tmpDir, "foo"), + filepath.Join(tmpDir, "foo", "qux"), + filepath.Join(tmpDir, "foo", "qux", "corge"), + }, + }, + { + desc: "valid_recursive2", + in: []string{ + tmpDir + "/.../qux", + }, + expected: []string{ + filepath.Join(tmpDir, "foo", "qux"), + }, + }, + { + desc: "valid_recursive3", + in: []string{ + tmpDir + "/.../qux/...", + }, + expected: []string{ + filepath.Join(tmpDir, "foo", "qux"), + filepath.Join(tmpDir, "foo", "qux", "corge"), + }, + }, + { + desc: "multiple_input", + in: []string{ + tmpDir + "/foo", + tmpDir + "/bar", + tmpDir + "/baz", + }, + expected: []string{ + filepath.Join(tmpDir, "foo"), + filepath.Join(tmpDir, "bar"), + filepath.Join(tmpDir, "baz"), + }, + }, + { + desc: "mixed_input1", + in: []string{ + tmpDir + "/foo", + tmpDir + "/bar/...", + }, + expected: []string{ + filepath.Join(tmpDir, "foo"), + filepath.Join(tmpDir, "bar"), + filepath.Join(tmpDir, "bar", "quux"), + }, + }, + { + desc: "mixed_input2", + in: []string{ + tmpDir + "/foo", + tmpDir + "/bar/...", + tmpDir + "/baz/baz.gno", + }, + expected: []string{ + filepath.Join(tmpDir, "foo"), + filepath.Join(tmpDir, "bar"), + filepath.Join(tmpDir, "bar", "quux"), + filepath.Join(tmpDir, "baz", "baz.gno"), + }, + }, + { + desc: "not_exists1", + in: []string{ + tmpDir + "/notexists", // dir path + }, + errorShouldContain: "no such file or directory", + }, + { + desc: "not_exists2", + in: []string{ + tmpDir + "/foo/bar.gno", // file path + }, + errorShouldContain: "no such file or directory", + }, + { + desc: "not_exists3", // mixed + in: []string{ + tmpDir + "/foo", // exists + tmpDir + "/notexists", // not exists + }, + errorShouldContain: "no such file or directory", + }, + } { + t.Run(tc.desc, func(t *testing.T) { + targets, err := targetsFromPatterns(tc.in) + if tc.errorShouldContain != "" { + assert.ErrorContains(t, err, tc.errorShouldContain) + return + } + assert.NoError(t, err) + require.Equal(t, len(tc.expected), len(targets)) + for _, tr := range targets { + assert.Contains(t, tc.expected, tr) + } + }) + } +} + +func createGnoPackages(t *testing.T, tmpDir string) { + t.Helper() + + type file struct { + name, data string + } + // Gno pkgs to create + pkgs := []struct { + dir string + files []file + }{ + // pkg 'foo', 'bar' and 'baz' + { + dir: filepath.Join(tmpDir, "foo"), + files: []file{ + { + name: "foo.gno", + data: `package foo`, + }, + }, + }, + { + dir: filepath.Join(tmpDir, "bar"), + files: []file{ + { + name: "bar.gno", + data: `package bar`, + }, + }, + }, + { + dir: filepath.Join(tmpDir, "baz"), + files: []file{ + { + name: "baz.gno", + data: `package baz`, + }, + }, + }, + + // pkg inside 'foo' pkg + { + dir: filepath.Join(tmpDir, "foo", "qux"), + files: []file{ + { + name: "qux.gno", + data: `package qux`, + }, + }, + }, + + // pkg inside 'bar' pkg + { + dir: filepath.Join(tmpDir, "bar", "quux"), + files: []file{ + { + name: "quux.gno", + data: `package quux`, + }, + }, + }, + + // pkg inside 'foo/qux' pkg + { + dir: filepath.Join(tmpDir, "foo", "qux", "corge"), + files: []file{ + { + name: "corge.gno", + data: `package corge`, + }, + }, + }, + } + + // Create pkgs + for _, p := range pkgs { + err := os.MkdirAll(p.dir, 0o700) + require.NoError(t, err) + for _, f := range p.files { + err = os.WriteFile(filepath.Join(p.dir, f.name), []byte(f.data), 0o644) + require.NoError(t, err) + } + } +} diff --git a/gnovm/docs/go-gno-compatibility.md b/gnovm/docs/go-gno-compatibility.md deleted file mode 100644 index 98f42aa9f29..00000000000 --- a/gnovm/docs/go-gno-compatibility.md +++ /dev/null @@ -1,373 +0,0 @@ -# Go<>Gno compatibility - -**WIP: does not reflect the current state yet.** - -## Native keywords - -Legend: full, partial, missing, TBD. - -| keyword | support | -|-------------|------------------------| -| break | full | -| case | full | -| const | full | -| continue | full | -| default | full | -| defer | full | -| else | full | -| fallthrough | full | -| for | full | -| func | full | -| go | missing (after launch) | -| goto | full | -| if | full | -| import | full | -| interface | full | -| package | full | -| range | full | -| return | full | -| select | missing (after launch) | -| struct | full | -| switch | full | -| type | full | -| var | full | - -## Native types - -| type | usage | persistency | -|-----------------------------------------------|------------------------|------------------------------------------------------------| -| `bool` | full | full | -| `byte` | full | full | -| `float32`, `float64` | full | full | -| `int`, `int8`, `int16`, `int32`, `int64` | full | full | -| `uint`, `uint8`, `uint16`, `uint32`, `uint64` | full | full | -| `string` | full | full | -| `rune` | full | full | -| `interface{}` | full | full | -| `[]T` (slices) | full | full* | -| `map[T1]T2` | full | full* | -| `func (T1...) T2...` | full | full (needs more tests) | -| `*T` (pointers) | full | full* | -| `chan T` (channels) | missing (after launch) | missing (after launch) | - -**\*:** depends on `T`/`T1`/`T2` - -Additional native types: - -| type | comment | -|----------|--------------------------------------------------------------------------------------------| -| `bigint` | Based on `math/big.Int` | -| `bigdec` | Based on https://github.com/cockroachdb/apd, (see https://github.com/gnolang/gno/pull/306) | - - -## Stdlibs - - - -| package | status | -|---------------------------------------------|----------| -| archive/tar | TBD | -| archive/zip | TBD | -| arena | TBD | -| bufio | TBD | -| builtin | TBD | -| bytes | TBD | -| cmd/addr2line | TBD | -| cmd/api | TBD | -| cmd/api/testdata/src/issue21181/dep | TBD | -| cmd/api/testdata/src/issue21181/indirect | TBD | -| cmd/api/testdata/src/issue21181/p | TBD | -| cmd/api/testdata/src/pkg/p1 | TBD | -| cmd/api/testdata/src/pkg/p2 | TBD | -| cmd/api/testdata/src/pkg/p3 | TBD | -| cmd/api/testdata/src/pkg/p4 | TBD | -| cmd/asm | TBD | -| cmd/buildid | TBD | -| cmd/cgo | TBD | -| cmd/compile | TBD | -| cmd/covdata | TBD | -| cmd/covdata/testdata | TBD | -| cmd/cover | TBD | -| cmd/cover/testdata | TBD | -| cmd/cover/testdata/html | TBD | -| cmd/cover/testdata/pkgcfg/a | TBD | -| cmd/cover/testdata/pkgcfg/b | TBD | -| cmd/cover/testdata/pkgcfg/main | TBD | -| cmd/dist | TBD | -| cmd/distpack | TBD | -| cmd/doc | TBD | -| cmd/doc/testdata | TBD | -| cmd/doc/testdata/merge | TBD | -| cmd/doc/testdata/nested | TBD | -| cmd/doc/testdata/nested/empty | TBD | -| cmd/doc/testdata/nested/nested | TBD | -| cmd/fix | TBD | -| cmd/go | TBD | -| cmd/gofmt | TBD | -| cmd/go/testdata | TBD | -| cmd/link | TBD | -| cmd/link/testdata/pe-binutils | TBD | -| cmd/link/testdata/pe-llvm | TBD | -| cmd/link/testdata/testBuildFortvOS | TBD | -| cmd/link/testdata/testHashedSyms | TBD | -| cmd/link/testdata/testIndexMismatch | TBD | -| cmd/link/testdata/testRO | TBD | -| cmd/nm | TBD | -| cmd/objdump | TBD | -| cmd/objdump/testdata | TBD | -| cmd/objdump/testdata/testfilenum | TBD | -| cmd/pack | TBD | -| cmd/pprof | TBD | -| cmd/pprof/testdata | TBD | -| cmd/test2json | TBD | -| cmd/trace | TBD | -| cmd/vet | TBD | -| cmd/vet/testdata/asm | TBD | -| cmd/vet/testdata/assign | TBD | -| cmd/vet/testdata/atomic | TBD | -| cmd/vet/testdata/bool | TBD | -| cmd/vet/testdata/buildtag | TBD | -| cmd/vet/testdata/cgo | TBD | -| cmd/vet/testdata/composite | TBD | -| cmd/vet/testdata/copylock | TBD | -| cmd/vet/testdata/deadcode | TBD | -| cmd/vet/testdata/directive | TBD | -| cmd/vet/testdata/httpresponse | TBD | -| cmd/vet/testdata/lostcancel | TBD | -| cmd/vet/testdata/method | TBD | -| cmd/vet/testdata/nilfunc | TBD | -| cmd/vet/testdata/print | TBD | -| cmd/vet/testdata/rangeloop | TBD | -| cmd/vet/testdata/shift | TBD | -| cmd/vet/testdata/structtag | TBD | -| cmd/vet/testdata/tagtest | TBD | -| cmd/vet/testdata/testingpkg | TBD | -| cmd/vet/testdata/unmarshal | TBD | -| cmd/vet/testdata/unsafeptr | TBD | -| cmd/vet/testdata/unused | TBD | -| compress/bzip2 | TBD | -| compress/flate | TBD | -| compress/gzip | TBD | -| compress/lzw | TBD | -| compress/zlib | TBD | -| container/heap | TBD | -| container/list | TBD | -| container/ring | TBD | -| context | TBD | -| crypto | TBD | -| crypto/aes | TBD | -| crypto/boring | TBD | -| crypto/cipher | TBD | -| crypto/des | TBD | -| crypto/dsa | TBD | -| crypto/ecdh | TBD | -| crypto/ecdsa | TBD | -| crypto/ed25519 | TBD | -| crypto/elliptic | TBD | -| crypto/hmac | TBD | -| crypto/md5 | TBD | -| crypto/rand | TBD | -| crypto/rc4 | TBD | -| crypto/rsa | TBD | -| crypto/sha1 | TBD | -| crypto/sha256 | TBD | -| crypto/sha512 | TBD | -| crypto/subtle | TBD | -| crypto/tls | TBD | -| crypto/tls/fipsonly | TBD | -| crypto/x509 | TBD | -| crypto/x509/pkix | TBD | -| database/sql | TBD | -| database/sql/driver | TBD | -| debug/buildinfo | TBD | -| debug/dwarf | TBD | -| debug/elf | TBD | -| debug/gosym | TBD | -| debug/gosym/testdata | TBD | -| debug/macho | TBD | -| debug/pe | TBD | -| debug/plan9obj | TBD | -| embed | TBD | -| encoding | TBD | -| encoding/ascii85 | TBD | -| encoding/asn1 | TBD | -| encoding/base32 | TBD | -| encoding/base64 | TBD | -| encoding/binary | partial | -| encoding/csv | TBD | -| encoding/gob | TBD | -| encoding/hex | TBD | -| encoding/json | TBD | -| encoding/pem | TBD | -| encoding/xml | TBD | -| errors | TBD | -| expvar | TBD | -| flag | TBD | -| fmt | TBD | -| go/ast | TBD | -| go/build | TBD | -| go/build/constraint | TBD | -| go/build/testdata/alltags | TBD | -| go/build/testdata/cgo_disabled | TBD | -| go/build/testdata/directives | TBD | -| go/build/testdata/doc | TBD | -| go/build/testdata/multi | TBD | -| go/build/testdata/non_source_tags | TBD | -| go/build/testdata/other | TBD | -| go/build/testdata/other/file | TBD | -| go/constant | TBD | -| go/doc | TBD | -| go/doc/comment | TBD | -| go/doc/testdata | TBD | -| go/doc/testdata/examples | TBD | -| go/doc/testdata/pkgdoc | TBD | -| go/format | TBD | -| go/importer | TBD | -| go/parser | TBD | -| go/parser/testdata/goversion | TBD | -| go/parser/testdata/issue42951 | TBD | -| go/parser/testdata/issue42951/not_a_file.go | TBD | -| go/printer | TBD | -| go/printer/testdata | TBD | -| go/scanner | TBD | -| go/token | TBD | -| go/types | TBD | -| go/types/testdata | TBD | -| go/types/testdata/local | TBD | -| hash | TBD | -| hash/adler32 | TBD | -| hash/crc32 | TBD | -| hash/crc64 | TBD | -| hash/fnv | TBD | -| hash/maphash | TBD | -| html | TBD | -| html/template | TBD | -| image | TBD | -| image/color | TBD | -| image/color/palette | TBD | -| image/draw | TBD | -| image/gif | TBD | -| image/jpeg | TBD | -| image/png | TBD | -| index/suffixarray | TBD | -| io | TBD | -| io/fs | TBD | -| io/ioutil | TBD | -| log | TBD | -| log/internal | TBD | -| log/slog | TBD | -| log/slog/internal | TBD | -| log/syslog | TBD | -| maps | TBD | -| math | partial | -| math/big | TBD | -| math/bits | TBD | -| math/cmplx | TBD | -| math/rand | TBD | -| mime | TBD | -| mime/multipart | TBD | -| mime/quotedprintable | TBD | -| net | TBD | -| net/http | TBD | -| net/http/cgi | TBD | -| net/http/cookiejar | TBD | -| net/http/fcgi | TBD | -| net/http/httptest | TBD | -| net/http/httptrace | TBD | -| net/http/httputil | TBD | -| net/http/internal | TBD | -| net/http/pprof | TBD | -| net/mail | TBD | -| net/netip | TBD | -| net/rpc | TBD | -| net/rpc/jsonrpc | TBD | -| net/smtp | TBD | -| net/textproto | TBD | -| net/url | full | -| os | TBD | -| os/exec | TBD | -| os/signal | TBD | -| os/user | TBD | -| path | full | -| path/filepath | TBD | -| plugin | TBD | -| reflect | TBD | -| regexp | TBD | -| regexp/syntax | TBD | -| runtime | TBD | -| runtime/asan | TBD | -| runtime/cgo | TBD | -| runtime/coverage | TBD | -| runtime/coverage/testdata | TBD | -| runtime/coverage/testdata/issue56006 | TBD | -| runtime/debug | TBD | -| runtime/metrics | TBD | -| runtime/msan | TBD | -| runtime/pprof | TBD | -| runtime/pprof/testdata/mappingtest | TBD | -| runtime/race | TBD | -| runtime/race/testdata | TBD | -| runtime/testdata/testexithooks | TBD | -| runtime/testdata/testfaketime | TBD | -| runtime/testdata/testprog | TBD | -| runtime/testdata/testprogcgo | TBD | -| runtime/testdata/testprogcgo/windows | TBD | -| runtime/testdata/testprognet | TBD | -| runtime/testdata/testwinlib | TBD | -| runtime/testdata/testwinlibsignal | TBD | -| runtime/testdata/testwinlibthrow | TBD | -| runtime/testdata/testwinsignal | TBD | -| runtime/trace | TBD | -| slices | TBD | -| sort | TBD | -| strconv | TBD | -| strings | TBD | -| sync | TBD | -| sync/atomic | TBD | -| syscall | TBD | -| syscall/js | TBD | -| testing | TBD | -| testing/fstest | TBD | -| testing/iotest | TBD | -| testing/quick | TBD | -| text/scanner | TBD | -| text/tabwriter | TBD | -| text/template | TBD | -| text/template/parse | TBD | -| time | TBD | -| time/tzdata | TBD | -| unicode | TBD | -| unicode/utf16 | TBD | -| unicode/utf8 | TBD | -| unsafe | TBD | - - - -## Tooling (`gno` binary) - -| go command | gno command | comment | -|-------------------|------------------|-----------------------------------------------------------------------| -| go bug | | see https://github.com/gnolang/gno/issues/733 | -| go build | gno build | same intention, limited compatibility | -| go clean | gno clean | same intention, limited compatibility | -| go doc | gno doc | limited compatibility; see https://github.com/gnolang/gno/issues/522 | -| go env | | | -| go fix | | | -| go fmt | | | -| go generate | | | -| go get | | | -| go help | | | -| go install | | | -| go list | | | -| go mod | | | -| + go mod download | gno mod download | same behavior | -| + go mod init | gno mod init | same behavior | -| | gno precompile | | -| go work | | | -| | gno repl | | -| go run | gno run | | -| go test | gno test | limited compatibility | -| go tool | | | -| go version | | | -| go vet | | | -| golint | gno lint | same intention | diff --git a/gnovm/pkg/doc/dirs.go b/gnovm/pkg/doc/dirs.go index 21216828ce4..19d312f6826 100644 --- a/gnovm/pkg/doc/dirs.go +++ b/gnovm/pkg/doc/dirs.go @@ -5,7 +5,6 @@ package doc import ( - "fmt" "log" "os" "path" @@ -52,7 +51,7 @@ func newDirs(dirs []string, modDirs []string) *bfsDirs { } for _, mdir := range modDirs { - gm, err := parseGnoMod(filepath.Join(mdir, "gno.mod")) + gm, err := gnomod.ParseGnoMod(filepath.Join(mdir, "gno.mod")) if err != nil { log.Printf("%v", err) continue @@ -68,34 +67,6 @@ func newDirs(dirs []string, modDirs []string) *bfsDirs { return d } -// tries to parse gno mod file given the filename, using Parse and Validate from -// the gnomod package -// -// TODO(tb): replace by `gnomod.ParseAt` ? The key difference is the latter -// looks for gno.mod in parent directories, while this function doesn't. -func parseGnoMod(fname string) (*gnomod.File, error) { - file, err := os.Stat(fname) - if err != nil { - return nil, fmt.Errorf("could not read gno.mod file: %w", err) - } - if file.IsDir() { - return nil, fmt.Errorf("invalid gno.mod at %q: is a directory", fname) - } - - b, err := os.ReadFile(fname) - if err != nil { - return nil, fmt.Errorf("could not read gno.mod file: %w", err) - } - gm, err := gnomod.Parse(fname, b) - if err != nil { - return nil, fmt.Errorf("error parsing gno.mod file at %q: %w", fname, err) - } - if err := gm.Validate(); err != nil { - return nil, fmt.Errorf("error validating gno.mod file at %q: %w", fname, err) - } - return gm, nil -} - func getGnoModDirs(gm *gnomod.File) []bfsDir { // cmd/go makes use of the go list command, we don't have that here. diff --git a/gnovm/pkg/doc/doc.go b/gnovm/pkg/doc/doc.go index 3e5ce48985a..989188a946a 100644 --- a/gnovm/pkg/doc/doc.go +++ b/gnovm/pkg/doc/doc.go @@ -155,7 +155,7 @@ func (d *documentable) output(pp *pkgPrinter) (err error) { var fpAbs = filepath.Abs // ResolveDocumentable returns a Documentable from the given arguments. -// Refer to the documentation of gnodev doc for the formats accepted (in general +// Refer to the documentation of gno doc for the formats accepted (in general // the same as the go doc command). // An error may be returned even if documentation was resolved in case some // packages in dirs could not be parsed correctly. diff --git a/gnovm/pkg/doc/print.go b/gnovm/pkg/doc/print.go index 7ac1742c62f..644eba9ad13 100644 --- a/gnovm/pkg/doc/print.go +++ b/gnovm/pkg/doc/print.go @@ -430,7 +430,7 @@ func (pkg *pkgPrinter) packageClause() { // If we did a file system scan, we knew the import path when we found the directory. // But if we started with a directory name, we never knew the import path. // Either way, we don't know it now, and it's cheap to (re)compute it. - /* TODO: add when supporting gnodev doc on local directories + /* TODO: add when supporting gno doc on local directories if usingModules { for _, root := range codeRoots() { if pkg.build.Dir == root.dir { diff --git a/gnovm/pkg/gnoenv/gnohome.go b/gnovm/pkg/gnoenv/gnohome.go new file mode 100644 index 00000000000..52dd5e6adb4 --- /dev/null +++ b/gnovm/pkg/gnoenv/gnohome.go @@ -0,0 +1,35 @@ +package gnoenv + +import ( + "fmt" + "os" + "path/filepath" +) + +func HomeDir() string { + // if environment variable is set, always use that. + // otherwise, use config dir (varies depending on OS) + "gno" + + dir := os.Getenv("GNOHOME") + if dir != "" { + return dir + } + + // XXX: `GNO_HOME` is deprecated and should be replaced by `GNOHOME` + // keeping for compatibility support + dir = os.Getenv("GNO_HOME") + if dir != "" { + return dir + } + + var err error + dir, err = os.UserConfigDir() + if err != nil { + panic(fmt.Errorf("couldn't get user config dir: %w", err)) + } + gnoHome := filepath.Join(dir, "gno") + + // XXX: added april 2023 as a transitory measure - remove after test4 + fixOldDefaultGnoHome(gnoHome) + return gnoHome +} diff --git a/gnovm/pkg/gnoenv/gnohome_test.go b/gnovm/pkg/gnoenv/gnohome_test.go new file mode 100644 index 00000000000..827d87774bb --- /dev/null +++ b/gnovm/pkg/gnoenv/gnohome_test.go @@ -0,0 +1,43 @@ +package gnoenv + +import ( + "os" + "path/filepath" + "testing" + + "github.com/jaekwon/testify/require" +) + +func TestHomeDir(t *testing.T) { + t.Run("use GNOHOME if set", func(t *testing.T) { + // Backup any related environment variables + t.Setenv("GNOHOME", "") + t.Setenv("GNO_HOME", "") + + expected := "/test/gno_home" + os.Setenv("GNOHOME", expected) + require.Equal(t, expected, HomeDir()) + }) + + t.Run("fallback to GNO_HOME if set", func(t *testing.T) { + // Backup any related environment variables + t.Setenv("GNOHOME", "") + t.Setenv("GNO_HOME", "") + t.Log("`GNO_HOME` is deprecated, use `GNOHOME` instead") + + expected := "/test/gnohome" + os.Setenv("GNO_HOME", expected) + require.Equal(t, expected, HomeDir()) + }) + + t.Run("use UserConfigDir with gno", func(t *testing.T) { + // Backup any related environment variables + t.Setenv("GNOHOME", "") + t.Setenv("GNO_HOME", "") + + dir, err := os.UserConfigDir() + require.NoError(t, err) + expected := filepath.Join(dir, "gno") + require.Equal(t, expected, HomeDir()) + }) +} diff --git a/gnovm/pkg/gnoenv/gnoroot.go b/gnovm/pkg/gnoenv/gnoroot.go new file mode 100644 index 00000000000..ad66cd93b57 --- /dev/null +++ b/gnovm/pkg/gnoenv/gnoroot.go @@ -0,0 +1,92 @@ +package gnoenv + +import ( + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "sync" +) + +var ErrUnableToGuessGnoRoot = errors.New("gno was unable to determine GNOROOT. Please set the GNOROOT environment variable") + +// Can be set manually at build time using: +// -ldflags="-X github.com/gnolang/gno/gnovm/pkg/gnoenv._GNOROOT" +var _GNOROOT string + +// RootDir guesses the Gno root directory and panics if it fails. +func RootDir() string { + root, err := GuessRootDir() + if err != nil { + panic(err) + } + + return root +} + +var muGnoRoot sync.Mutex + +// GuessRootDir attempts to determine the Gno root directory using various strategies: +// 1. First, It tries to obtain it from the `GNOROOT` environment variable. +// 2. If the env variable isn't set, It checks if `_GNOROOT` has been previously determined or set with -ldflags. +// 3. If not, it uses the `go list` command to infer from go.mod. +// 4. As a last resort, it determines `GNOROOT` based on the caller stack's file path. +func GuessRootDir() (string, error) { + muGnoRoot.Lock() + defer muGnoRoot.Unlock() + + // First try to get the root directory from the `GNOROOT` environment variable. + if rootdir := os.Getenv("GNOROOT"); rootdir != "" { + return strings.TrimSpace(rootdir), nil + } + + var err error + if _GNOROOT == "" { + // Try to guess `GNOROOT` using various strategies + _GNOROOT, err = guessRootDir() + } + + return _GNOROOT, err +} + +func guessRootDir() (string, error) { + // Attempt to guess `GNOROOT` from go.mod by using the `go list` command. + if rootdir, err := inferRootFromGoMod(); err == nil { + return filepath.Clean(rootdir), nil + } + + // If the above method fails, ultimately try to determine `GNOROOT` based + // on the caller stack's file path. + // Path need to be absolute here, that mostly mean that if `-trimpath` + // as been passed this method will not works. + if _, filename, _, ok := runtime.Caller(1); ok && filepath.IsAbs(filename) { + if currentDir := filepath.Dir(filename); currentDir != "" { + // Deduce Gno root directory relative from the current file's path. + // gno/ .. /gnovm/ .. /pkg/ .. /gnoenv/gnoenv.go + rootdir, err := filepath.Abs(filepath.Join(currentDir, "..", "..", "..")) + if err == nil { + return rootdir, nil + } + } + } + + return "", ErrUnableToGuessGnoRoot +} + +func inferRootFromGoMod() (string, error) { + gobin, err := exec.LookPath("go") + if err != nil { + return "", fmt.Errorf("unable to find `go` binary: %w", err) + } + + cmd := exec.Command(gobin, "list", "-m", "-mod=mod", "-f", "{{.Dir}}", "github.com/gnolang/gno") + out, err := cmd.CombinedOutput() + if err != nil { + return "", fmt.Errorf("unable to infer GnoRoot from go.mod: %w", err) + } + + return strings.TrimSpace(string(out)), nil +} diff --git a/gnovm/pkg/gnoenv/gnoroot_test.go b/gnovm/pkg/gnoenv/gnoroot_test.go new file mode 100644 index 00000000000..300ed8727b3 --- /dev/null +++ b/gnovm/pkg/gnoenv/gnoroot_test.go @@ -0,0 +1,75 @@ +package gnoenv + +import ( + "os/exec" + "path/filepath" + "testing" + + "github.com/jaekwon/testify/require" +) + +func TestGuessGnoRootDir_WithSetGnoRoot(t *testing.T) { + originalGnoRoot := _GNOROOT + defer func() { _GNOROOT = originalGnoRoot }() // Restore after test + + t.Setenv("GNOROOT", "") + + const testPath = "/path/to/gnoRoot" + + _GNOROOT = testPath + root, err := GuessRootDir() + require.NoError(t, err) + require.Equal(t, root, testPath) +} + +func TestGuessGnoRootDir_UsingCallerStack(t *testing.T) { + originalGnoRoot := _GNOROOT + defer func() { _GNOROOT = originalGnoRoot }() + + // Unset PATH should prevent InferGnoRootFromGoMod to works + t.Setenv("GNOROOT", "") + t.Setenv("PATH", "") + + _, err := exec.LookPath("go") + require.Error(t, err) + + // gno/ .. /gnovm/ .. /pkg/ .. /gnoenv/gnoroot.go + testPath, _ := filepath.Abs(filepath.Join(".", "..", "..", "..")) + root, err := GuessRootDir() + require.NoError(t, err) + require.Equal(t, root, testPath) +} + +func TestGuessGnoRootDir_Error(t *testing.T) { + // XXX: Determine a method to test the GuessGnoRoot final error. + // One approach might be to use `txtar` to build a test binary with -trimpath, + // avoiding absolute paths in the call stack. + t.Skip("not implemented; refer to the inline comment for more details.") +} + +func TestGuessGnoRootDir_WithGoModList(t *testing.T) { + // XXX: find a way to test `go mod list` phase. + // One solution is to use txtar with embed go.mod file. + // For now only `inferGnoRootFromGoMod` is tested. + t.Skip("not implemented; refer to the inline comment for more details.") +} + +func TestInferGnoRootFromGoMod(t *testing.T) { + // gno/ .. /gnovm/ .. /pkg/ .. /gnoenv/gnoroot.go + testPath, _ := filepath.Abs(filepath.Join(".", "..", "..", "..")) + + t.Run("go is present", func(t *testing.T) { + root, err := inferRootFromGoMod() + require.NoError(t, err) + require.Equal(t, root, testPath) + }) + + t.Run("go is not present", func(t *testing.T) { + // Unset PATH should prevent `inferGnoRootFromGoMod` to works + t.Setenv("PATH", "") + + root, err := inferRootFromGoMod() + require.Error(t, err) + require.Empty(t, root) + }) +} diff --git a/tm2/pkg/crypto/keys/client/migration.go b/gnovm/pkg/gnoenv/migration.go similarity index 98% rename from tm2/pkg/crypto/keys/client/migration.go rename to gnovm/pkg/gnoenv/migration.go index a3b1f029ba2..5b1d1fd1fa0 100644 --- a/tm2/pkg/crypto/keys/client/migration.go +++ b/gnovm/pkg/gnoenv/migration.go @@ -1,4 +1,4 @@ -package client +package gnoenv import ( "log" diff --git a/gnovm/pkg/gnoenv/migration_test.go b/gnovm/pkg/gnoenv/migration_test.go new file mode 100644 index 00000000000..7ab12b59b67 --- /dev/null +++ b/gnovm/pkg/gnoenv/migration_test.go @@ -0,0 +1,29 @@ +package gnoenv + +import ( + "os" + "path/filepath" + "testing" + + "github.com/jaekwon/testify/require" +) + +func TestFixOldDefaultGnoHome(t *testing.T) { + tempHomeDir := t.TempDir() + t.Setenv("HOME", tempHomeDir) + + oldGnoHome := filepath.Join(tempHomeDir, ".gno") + newGnoHome := filepath.Join(tempHomeDir, "gno") + + // Create a dummy old GNO_HOME + os.Mkdir(oldGnoHome, 0o755) + + // Test migration + fixOldDefaultGnoHome(newGnoHome) + + _, errOld := os.Stat(oldGnoHome) + require.NotNil(t, errOld) + _, errNew := os.Stat(newGnoHome) + require.True(t, os.IsNotExist(errOld), "invalid errors", errOld) + require.NoError(t, errNew) +} diff --git a/gnovm/pkg/gnolang/alloc_test.go b/gnovm/pkg/gnolang/alloc_test.go index e3b9f1cb069..dbb3903f862 100644 --- a/gnovm/pkg/gnolang/alloc_test.go +++ b/gnovm/pkg/gnolang/alloc_test.go @@ -6,6 +6,8 @@ import ( ) func TestAllocSizes(t *testing.T) { + t.Parallel() + // go elemental println("_allocPointer", unsafe.Sizeof(&StructValue{})) println("_allocSlice", unsafe.Sizeof([]byte("12345678901234567890123456789012345678901234567890"))) diff --git a/gnovm/pkg/gnolang/benchdata/fib.gno b/gnovm/pkg/gnolang/benchdata/fib.gno new file mode 100644 index 00000000000..75f2b0b15d6 --- /dev/null +++ b/gnovm/pkg/gnolang/benchdata/fib.gno @@ -0,0 +1,16 @@ +// param: 4 8 16 + +package main + +func main() { + for i := 0; i < {{ .N }}; i++ { + fib({{ .Param }}) + } +} + +func fib(n int) int { + if n < 2 { + return 1 + } + return fib(n-1) + fib(n-2) +} diff --git a/gnovm/pkg/gnolang/benchdata/loop.gno b/gnovm/pkg/gnolang/benchdata/loop.gno new file mode 100644 index 00000000000..0effa823f0d --- /dev/null +++ b/gnovm/pkg/gnolang/benchdata/loop.gno @@ -0,0 +1,5 @@ +package main + +func main() { + for i := 0; i < {{ .N }}; i++ {} +} diff --git a/gnovm/pkg/gnolang/benchdata/matrix.gno b/gnovm/pkg/gnolang/benchdata/matrix.gno new file mode 100644 index 00000000000..429323df65d --- /dev/null +++ b/gnovm/pkg/gnolang/benchdata/matrix.gno @@ -0,0 +1,90 @@ +// param: 3 4 5 6 + +package main + +func main() { + const p = {{ .Param }} + for i := 0; i < {{ .N }}; i++ { + _ = det(mul(dist(p), mul(id(p), dist(p)))) + } +} + +// identity matrix +func id(sz int) [][]int { + r := make([][]int, sz) + for i := range r { + r[i] = make([]int, sz) + r[i][i] = 1 + } + return r +} + +// distance from corner +func dist(sz int) [][]int { + half := sz / 2 + r := make([][]int, sz) + for i := range r { + r[i] = make([]int, sz) + vdist := i + if vdist >= half { + vdist = sz - 1 - i + } + for j := range r[i] { + hdist := j + if hdist >= half { + hdist = sz - 1 - j + } + r[i][j] = vdist + hdist + } + } + return r +} + +func det(m [][]int) int { + size := len(m) + if size == 2 { + return m[0][0]*m[1][1] - m[0][1]*m[1][0] + } + subMatrix := make([][]int, size-1) + for j := range subMatrix { + subMatrix[j] = make([]int, size-1) + } + + determinant := 0 + for i := 0; i < size; i++ { + for j := 1; j < size; j++ { + t := 0 + for k := 0; k < size; k++ { + if k == i { + continue + } + subMatrix[j-1][t] = m[j][k] + t++ + } + } + sign := 1 + if i % 2 == 1 { + sign = -1 + } + determinant += m[0][i] * det(subMatrix) * sign + } + return determinant +} + +func mul(m1, m2 [][]int) [][]int { + size := len(m1) + result := make([][]int, size) + for i := range result { + result[i] = make([]int, size) + } + + for i := 0; i < size; i++ { + for j := 0; j < size; j++ { + for k := 0; k < size; k++ { + result[i][j] += m1[i][k] * m2[k][j] + } + } + } + + return result +} diff --git a/gnovm/pkg/gnolang/debug.go b/gnovm/pkg/gnolang/debug.go index cb21da12ef2..c6e39dfaa5a 100644 --- a/gnovm/pkg/gnolang/debug.go +++ b/gnovm/pkg/gnolang/debug.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "os" + "strings" "time" // Ignore pprof import, as the server does not @@ -76,6 +77,40 @@ func (d debugging) Errorf(format string, args ...interface{}) { } } +// PreprocessError wraps a processing error along with its associated +// preprocessing stack for enhanced error reporting. +type PreprocessError struct { + err error + stack []BlockNode +} + +// Unwrap returns the encapsulated error message. +func (p *PreprocessError) Unwrap() error { + return p.err +} + +// Stack produces a string representation of the preprocessing stack +// trace that was associated with the error occurrence. +func (p *PreprocessError) Stack() string { + var stacktrace strings.Builder + for i := len(p.stack) - 1; i >= 0; i-- { + sbn := p.stack[i] + fmt.Fprintf(&stacktrace, "stack %d: %s\n", i, sbn.String()) + } + return stacktrace.String() +} + +// Error consolidates and returns the full error message, including +// the actual error followed by its associated preprocessing stack. +func (p *PreprocessError) Error() string { + var err strings.Builder + fmt.Fprintf(&err, "%s:\n", p.Unwrap()) + fmt.Fprintln(&err, "--- preprocess stack ---") + fmt.Fprint(&err, p.Stack()) + fmt.Fprintf(&err, "------------------------") + return err.String() +} + // ---------------------------------------- // Exposed errors accessors // File tests may access debug errors. diff --git a/gnovm/pkg/gnolang/gno_test.go b/gnovm/pkg/gnolang/gno_test.go index 5ed5971c836..d8bdcdfd4a0 100644 --- a/gnovm/pkg/gnolang/gno_test.go +++ b/gnovm/pkg/gnolang/gno_test.go @@ -3,24 +3,36 @@ package gnolang import ( "bytes" "fmt" + "io" + "os" + "path/filepath" "reflect" + "strings" "testing" + "text/template" "unsafe" // "github.com/davecgh/go-spew/spew" "github.com/jaekwon/testify/assert" + "github.com/jaekwon/testify/require" ) // run empty main(). func TestRunEmptyMain(t *testing.T) { + t.Parallel() + m := NewMachine("test", nil) - main := FuncD("main", nil, nil, nil) + // []Stmt{} != nil, as nil means that in the source code not even the + // brackets are present and is reserved for external (ie. native) functions. + main := FuncD("main", nil, nil, []Stmt{}) m.RunDeclaration(main) m.RunMain() } // run main() with a for loop. func TestRunLoopyMain(t *testing.T) { + t.Parallel() + m := NewMachine("test", nil) c := `package test func main() { @@ -35,7 +47,78 @@ func main() { m.RunMain() } +func TestDoOpEvalBaseConversion(t *testing.T) { + m := NewMachine("test", nil) + + type testCase struct { + input string + expect string + expectErr bool + } + + testCases := []testCase{ + // binary + {input: "0b101010", expect: "42", expectErr: false}, + {input: "0B101010", expect: "42", expectErr: false}, + {input: "0b111111111111111111111111111111111111111111111111111111111111111", expect: "9223372036854775807", expectErr: false}, + {input: "0b0", expect: "0", expectErr: false}, + {input: "0b000000101010", expect: "42", expectErr: false}, + {input: " 0b101010", expectErr: true}, + {input: "0b", expectErr: true}, + {input: "0bXXXX", expectErr: true}, + {input: "42b0", expectErr: true}, + // octal + {input: "0o42", expect: "34", expectErr: false}, + {input: "0o0", expect: "0", expectErr: false}, + {input: "042", expect: "34", expectErr: false}, + {input: "0777", expect: "511", expectErr: false}, + {input: "0O0000042", expect: "34", expectErr: false}, + {input: "0777777777777777777777", expect: "9223372036854775807", expectErr: false}, + {input: "0o777777777777777777777", expect: "9223372036854775807", expectErr: false}, + {input: "048", expectErr: true}, + {input: "0o", expectErr: true}, + {input: "0oXXXX", expectErr: true}, + {input: "0OXXXX", expectErr: true}, + {input: "0o42x42", expectErr: true}, + {input: "0O42x42", expectErr: true}, + {input: "0420x42", expectErr: true}, + {input: "0o420o42", expectErr: true}, + // hex + {input: "0x2a", expect: "42", expectErr: false}, + {input: "0X2A", expect: "42", expectErr: false}, + {input: "0x7FFFFFFFFFFFFFFF", expect: "9223372036854775807", expectErr: false}, + {input: "0x2a ", expectErr: true}, + {input: "0x", expectErr: true}, + {input: "0xXXXX", expectErr: true}, + {input: "0xGHIJ", expectErr: true}, + {input: "0x42o42", expectErr: true}, + {input: "0x2ax42", expectErr: true}, + // decimal + {input: "42", expect: "42", expectErr: false}, + {input: "0", expect: "0", expectErr: false}, + {input: "0000000000", expect: "0", expectErr: false}, + {input: "9223372036854775807", expect: "9223372036854775807", expectErr: false}, + } + + for _, tc := range testCases { + m.PushExpr(&BasicLitExpr{ + Kind: INT, + Value: tc.input, + }) + + if tc.expectErr { + assert.Panics(t, func() { m.doOpEval() }) + } else { + m.doOpEval() + v := m.PopValue() + assert.Equal(t, v.V.String(), tc.expect) + } + } +} + func TestEval(t *testing.T) { + t.Parallel() + m := NewMachine("test", nil) c := `package test func next(i int) int { @@ -64,6 +147,8 @@ func assertOutput(t *testing.T, input string, output string) { } func TestRunMakeStruct(t *testing.T) { + t.Parallel() + assertOutput(t, `package test type Outfit struct { Scarf string @@ -90,6 +175,8 @@ func main() { } func TestRunReturnStruct(t *testing.T) { + t.Parallel() + assertOutput(t, `package test type MyStruct struct { FieldA string @@ -111,43 +198,84 @@ func main() { // Benchmarks func BenchmarkPreprocess(b *testing.B) { - for i := 0; i < b.N; i++ { - // stop timer - b.StopTimer() - pkg := &PackageNode{ - PkgName: "main", - PkgPath: ".main", - FileSet: nil, - } - pkg.InitStaticBlock(pkg, nil) - main := FuncD("main", nil, nil, Ss( - A("mx", ":=", "1000000"), - For( - A("i", ":=", "0"), - X("i < mx"), - Inc("i"), - ), - )) - b.StartTimer() - // timer started - main = Preprocess(nil, pkg, main).(*FuncDecl) + pkg := &PackageNode{ + PkgName: "main", + PkgPath: ".main", + FileSet: nil, } -} - -func BenchmarkLoopyMain(b *testing.B) { - m := NewMachine("test", nil) + pkg.InitStaticBlock(pkg, nil) main := FuncD("main", nil, nil, Ss( - A("mx", ":=", "10000000"), + A("mx", ":=", "1000000"), For( A("i", ":=", "0"), - // X("i < 10000000"), X("i < mx"), Inc("i"), ), )) - m.RunDeclaration(main) + copies := make([]*FuncDecl, b.N) for i := 0; i < b.N; i++ { - m.RunMain() + copies[i] = main.Copy().(*FuncDecl) + } + b.ResetTimer() + + for i := 0; i < b.N; i++ { + main = Preprocess(nil, pkg, copies[i]).(*FuncDecl) + } +} + +type bdataParams struct { + N int + Param string +} + +func BenchmarkBenchdata(b *testing.B) { + const bdDir = "./benchdata" + files, err := os.ReadDir(bdDir) + require.NoError(b, err) + for _, file := range files { + // Read file and parse template. + bcont, err := os.ReadFile(filepath.Join(bdDir, file.Name())) + cont := string(bcont) + require.NoError(b, err) + tpl, err := template.New("").Parse(cont) + require.NoError(b, err) + + // Determine parameters. + const paramString = "// param: " + var params []string + pos := strings.Index(cont, paramString) + if pos >= 0 { + paramsRaw := strings.SplitN(cont[pos+len(paramString):], "\n", 2)[0] + params = strings.Fields(paramsRaw) + } else { + params = []string{""} + } + + for _, param := range params { + name := file.Name() + if param != "" { + name += "_param:" + param + } + b.Run(name, func(b *testing.B) { + // Gen template with N and param. + var buf bytes.Buffer + require.NoError(b, tpl.Execute(&buf, bdataParams{ + N: b.N, + Param: param, + })) + + // Set up machine. + m := NewMachineWithOptions(MachineOptions{ + PkgPath: "main", + Output: io.Discard, + }) + n := MustParseFile("main.go", buf.String()) + m.RunFiles(n) + + b.ResetTimer() + m.RunMain() + }) + } } } @@ -160,6 +288,8 @@ type Struct1 struct { } func TestModifyTypeAsserted(t *testing.T) { + t.Parallel() + x := Struct1{1, 1} var v interface{} = x x2 := v.(Struct1) @@ -176,6 +306,8 @@ type Interface1 interface { } func TestTypeConversion(t *testing.T) { + t.Parallel() + x := 1 var v interface{} = x if _, ok := v.(Interface1); ok { @@ -193,6 +325,8 @@ func TestTypeConversion(t *testing.T) { } func TestSomething(t *testing.T) { + t.Parallel() + type Foo struct { X interface{} } @@ -210,6 +344,8 @@ func TestSomething(t *testing.T) { // XXX is there a way to test in Go as well as Gno? func TestDeferOrder(t *testing.T) { + t.Parallel() + a := func() func(int, int) int { fmt.Println("a constructed") return func(x int, y int) int { @@ -238,6 +374,8 @@ func TestDeferOrder(t *testing.T) { // XXX is there a way to test in Go as well as Gno? func TestCallOrder(t *testing.T) { + t.Parallel() + a := func() func(int, int) int { fmt.Println("a constructed") return func(x int, y int) int { @@ -264,6 +402,8 @@ func TestCallOrder(t *testing.T) { // XXX is there a way to test in Go as well as Gno? func TestBinaryShortCircuit(t *testing.T) { + t.Parallel() + tr := func() bool { fmt.Println("t called") return true @@ -281,6 +421,8 @@ func TestBinaryShortCircuit(t *testing.T) { // XXX is there a way to test in Go as well as Gno? func TestSwitchDefine(t *testing.T) { + t.Parallel() + var x interface{} = 1 switch y := x.(type) { case int: @@ -292,6 +434,8 @@ func TestSwitchDefine(t *testing.T) { // XXX is there a way to test in Go as well as Gno? func TestBinaryCircuit(t *testing.T) { + t.Parallel() + tr := func() bool { fmt.Println("tr() called") return true @@ -316,6 +460,8 @@ func TestBinaryCircuit(t *testing.T) { } func TestMultiAssignment(t *testing.T) { + t.Parallel() + buf := make([]int, 4) ref := func(i int) *int { fmt.Printf("ref(%v) called\n", i) @@ -342,6 +488,8 @@ func TestMultiAssignment(t *testing.T) { // XXX is there a way to test in Go as well as Gno? func TestCallLHS(t *testing.T) { + t.Parallel() + x := 1 xptr := func() *int { return &x @@ -352,6 +500,8 @@ func TestCallLHS(t *testing.T) { // XXX is there a way to test in Go as well as Gno? func TestCallFieldLHS(t *testing.T) { + t.Parallel() + type str struct { X int } diff --git a/gnovm/pkg/gnolang/gnolang.proto b/gnovm/pkg/gnolang/gnolang.proto index 30508bb41a6..f7eaa907ec5 100644 --- a/gnovm/pkg/gnolang/gnolang.proto +++ b/gnovm/pkg/gnolang/gnolang.proto @@ -8,596 +8,596 @@ import "google/protobuf/any.proto"; // messages message TypedValue { - google.protobuf.Any T = 1; - google.protobuf.Any V = 2; - bytes N = 3; + google.protobuf.Any t = 1 [json_name = "T"]; + google.protobuf.Any v = 2 [json_name = "V"]; + bytes n = 3 [json_name = "N"]; } message StringValue { - string Value = 1; + string value = 1; } message BigintValue { - string Value = 1; + string value = 1; } message BigdecValue { - string Value = 1; + string value = 1; } message PointerValue { - TypedValue TV = 1; - google.protobuf.Any Base = 2; - sint64 Index = 3; - TypedValue Key = 4; + TypedValue tv = 1 [json_name = "TV"]; + google.protobuf.Any base = 2 [json_name = "Base"]; + sint64 index = 3 [json_name = "Index"]; + TypedValue key = 4 [json_name = "Key"]; } message ArrayValue { - ObjectInfo ObjectInfo = 1; - repeated TypedValue List = 2; - bytes Data = 3; + ObjectInfo object_info = 1 [json_name = "ObjectInfo"]; + repeated TypedValue list = 2 [json_name = "List"]; + bytes data = 3 [json_name = "Data"]; } message SliceValue { - google.protobuf.Any Base = 1; - sint64 Offset = 2; - sint64 Length = 3; - sint64 Maxcap = 4; + google.protobuf.Any base = 1 [json_name = "Base"]; + sint64 offset = 2 [json_name = "Offset"]; + sint64 length = 3 [json_name = "Length"]; + sint64 maxcap = 4 [json_name = "Maxcap"]; } message StructValue { - ObjectInfo ObjectInfo = 1; - repeated TypedValue Fields = 2; + ObjectInfo object_info = 1 [json_name = "ObjectInfo"]; + repeated TypedValue fields = 2 [json_name = "Fields"]; } message FuncValue { - google.protobuf.Any Type = 1; - bool IsMethod = 2; - google.protobuf.Any Source = 3; - string Name = 4; - google.protobuf.Any Closure = 5; - string FileName = 6; - string PkgPath = 7; + google.protobuf.Any type = 1 [json_name = "Type"]; + bool is_method = 2 [json_name = "IsMethod"]; + google.protobuf.Any source = 3 [json_name = "Source"]; + string name = 4 [json_name = "Name"]; + google.protobuf.Any closure = 5 [json_name = "Closure"]; + string file_name = 6 [json_name = "FileName"]; + string pkg_path = 7 [json_name = "PkgPath"]; } message MapValue { - ObjectInfo ObjectInfo = 1; - MapList List = 2; + ObjectInfo object_info = 1 [json_name = "ObjectInfo"]; + MapList list = 2 [json_name = "List"]; } message MapList { - repeated MapListItem List = 1; + repeated MapListItem list = 1 [json_name = "List"]; } message MapListItem { - TypedValue Key = 1; - TypedValue Value = 2; + TypedValue key = 1 [json_name = "Key"]; + TypedValue value = 2 [json_name = "Value"]; } message BoundMethodValue { - ObjectInfo ObjectInfo = 1; - FuncValue Func = 2; - TypedValue Receiver = 3; + ObjectInfo object_info = 1 [json_name = "ObjectInfo"]; + FuncValue func = 2 [json_name = "Func"]; + TypedValue receiver = 3 [json_name = "Receiver"]; } message TypeValue { - google.protobuf.Any Type = 1; + google.protobuf.Any type = 1 [json_name = "Type"]; } message PackageValue { - ObjectInfo ObjectInfo = 1; - google.protobuf.Any Block = 2; - string PkgName = 3; - string PkgPath = 4; - repeated string FNames = 5; - repeated google.protobuf.Any FBlocks = 6; + ObjectInfo object_info = 1 [json_name = "ObjectInfo"]; + google.protobuf.Any block = 2 [json_name = "Block"]; + string pkg_name = 3 [json_name = "PkgName"]; + string pkg_path = 4 [json_name = "PkgPath"]; + repeated string f_names = 5 [json_name = "FNames"]; + repeated google.protobuf.Any f_blocks = 6 [json_name = "FBlocks"]; } message Block { - ObjectInfo ObjectInfo = 1; - google.protobuf.Any Source = 2; - repeated TypedValue Values = 3; - google.protobuf.Any Parent = 4; - TypedValue Blank = 5; + ObjectInfo object_info = 1 [json_name = "ObjectInfo"]; + google.protobuf.Any source = 2 [json_name = "Source"]; + repeated TypedValue values = 3 [json_name = "Values"]; + google.protobuf.Any parent = 4 [json_name = "Parent"]; + TypedValue blank = 5 [json_name = "Blank"]; } message RefValue { - string ObjectID = 1; - bool Escaped = 2; - string PkgPath = 3; - string Hash = 4; + string object_id = 1 [json_name = "ObjectID"]; + bool escaped = 2 [json_name = "Escaped"]; + string pkg_path = 3 [json_name = "PkgPath"]; + string hash = 4 [json_name = "Hash"]; } message ObjectID { - string Value = 1; + string value = 1; } message ObjectInfo { - string ID = 1; - string Hash = 2; - string OwnerID = 3; - uint64 ModTime = 4; - sint64 RefCount = 5; - bool IsEscaped = 6; + string id = 1 [json_name = "ID"]; + string hash = 2 [json_name = "Hash"]; + string owner_id = 3 [json_name = "OwnerID"]; + uint64 mod_time = 4 [json_name = "ModTime"]; + sint64 ref_count = 5 [json_name = "RefCount"]; + bool is_escaped = 6 [json_name = "IsEscaped"]; } message ValueHash { - string Value = 1; + string value = 1; } message Hashlet { - bytes Value = 1; + bytes value = 1; } message ValuePath { - uint32 Type = 1; - uint32 Depth = 2; - uint32 Index = 3; - string Name = 4; + uint32 type = 1 [json_name = "Type"]; + uint32 depth = 2 [json_name = "Depth"]; + uint32 index = 3 [json_name = "Index"]; + string name = 4 [json_name = "Name"]; } message Location { - string PkgPath = 1; - string File = 2; - sint64 Line = 3; - sint64 Nonce = 4; + string pkg_path = 1 [json_name = "PkgPath"]; + string file = 2 [json_name = "File"]; + sint64 line = 3 [json_name = "Line"]; + sint64 nonce = 4 [json_name = "Nonce"]; } message Attributes { - sint64 Line = 1; - string Label = 2; + sint64 line = 1 [json_name = "Line"]; + string label = 2 [json_name = "Label"]; } message NameExpr { - Attributes Attributes = 1; - ValuePath Path = 2; - string Name = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + ValuePath path = 2 [json_name = "Path"]; + string name = 3 [json_name = "Name"]; } message BasicLitExpr { - Attributes Attributes = 1; - sint64 Kind = 2; - string Value = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + sint64 kind = 2 [json_name = "Kind"]; + string value = 3 [json_name = "Value"]; } message BinaryExpr { - Attributes Attributes = 1; - google.protobuf.Any Left = 2; - sint64 Op = 3; - google.protobuf.Any Right = 4; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any left = 2 [json_name = "Left"]; + sint64 op = 3 [json_name = "Op"]; + google.protobuf.Any right = 4 [json_name = "Right"]; } message CallExpr { - Attributes Attributes = 1; - google.protobuf.Any Func = 2; - repeated google.protobuf.Any Args = 3; - bool Varg = 4; - sint64 NumArgs = 5; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any func = 2 [json_name = "Func"]; + repeated google.protobuf.Any args = 3 [json_name = "Args"]; + bool varg = 4 [json_name = "Varg"]; + sint64 num_args = 5 [json_name = "NumArgs"]; } message IndexExpr { - Attributes Attributes = 1; - google.protobuf.Any X = 2; - google.protobuf.Any Index = 3; - bool HasOK = 4; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any x = 2 [json_name = "X"]; + google.protobuf.Any index = 3 [json_name = "Index"]; + bool has_ok = 4 [json_name = "HasOK"]; } message SelectorExpr { - Attributes Attributes = 1; - google.protobuf.Any X = 2; - ValuePath Path = 3; - string Sel = 4; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any x = 2 [json_name = "X"]; + ValuePath path = 3 [json_name = "Path"]; + string sel = 4 [json_name = "Sel"]; } message SliceExpr { - Attributes Attributes = 1; - google.protobuf.Any X = 2; - google.protobuf.Any Low = 3; - google.protobuf.Any High = 4; - google.protobuf.Any Max = 5; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any x = 2 [json_name = "X"]; + google.protobuf.Any low = 3 [json_name = "Low"]; + google.protobuf.Any high = 4 [json_name = "High"]; + google.protobuf.Any max = 5 [json_name = "Max"]; } message StarExpr { - Attributes Attributes = 1; - google.protobuf.Any X = 2; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any x = 2 [json_name = "X"]; } message RefExpr { - Attributes Attributes = 1; - google.protobuf.Any X = 2; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any x = 2 [json_name = "X"]; } message TypeAssertExpr { - Attributes Attributes = 1; - google.protobuf.Any X = 2; - google.protobuf.Any Type = 3; - bool HasOK = 4; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any x = 2 [json_name = "X"]; + google.protobuf.Any type = 3 [json_name = "Type"]; + bool has_ok = 4 [json_name = "HasOK"]; } message UnaryExpr { - Attributes Attributes = 1; - google.protobuf.Any X = 2; - sint64 Op = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any x = 2 [json_name = "X"]; + sint64 op = 3 [json_name = "Op"]; } message CompositeLitExpr { - Attributes Attributes = 1; - google.protobuf.Any Type = 2; - repeated KeyValueExpr Elts = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any type = 2 [json_name = "Type"]; + repeated KeyValueExpr elts = 3 [json_name = "Elts"]; } message KeyValueExpr { - Attributes Attributes = 1; - google.protobuf.Any Key = 2; - google.protobuf.Any Value = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any key = 2 [json_name = "Key"]; + google.protobuf.Any value = 3 [json_name = "Value"]; } message FuncLitExpr { - Attributes Attributes = 1; - StaticBlock StaticBlock = 2; - FuncTypeExpr Type = 3; - repeated google.protobuf.Any Body = 4; + Attributes attributes = 1 [json_name = "Attributes"]; + StaticBlock static_block = 2 [json_name = "StaticBlock"]; + FuncTypeExpr type = 3 [json_name = "Type"]; + repeated google.protobuf.Any body = 4 [json_name = "Body"]; } message ConstExpr { - Attributes Attributes = 1; - google.protobuf.Any Source = 2; - TypedValue TypedValue = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any source = 2 [json_name = "Source"]; + TypedValue typed_value = 3 [json_name = "TypedValue"]; } message FieldTypeExpr { - Attributes Attributes = 1; - string Name = 2; - google.protobuf.Any Type = 3; - google.protobuf.Any Tag = 4; + Attributes attributes = 1 [json_name = "Attributes"]; + string name = 2 [json_name = "Name"]; + google.protobuf.Any type = 3 [json_name = "Type"]; + google.protobuf.Any tag = 4 [json_name = "Tag"]; } message ArrayTypeExpr { - Attributes Attributes = 1; - google.protobuf.Any Len = 2; - google.protobuf.Any Elt = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any len = 2 [json_name = "Len"]; + google.protobuf.Any elt = 3 [json_name = "Elt"]; } message SliceTypeExpr { - Attributes Attributes = 1; - google.protobuf.Any Elt = 2; - bool Vrd = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any elt = 2 [json_name = "Elt"]; + bool vrd = 3 [json_name = "Vrd"]; } message InterfaceTypeExpr { - Attributes Attributes = 1; - repeated FieldTypeExpr Methods = 2; - string Generic = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + repeated FieldTypeExpr methods = 2 [json_name = "Methods"]; + string generic = 3 [json_name = "Generic"]; } message ChanTypeExpr { - Attributes Attributes = 1; - sint64 Dir = 2; - google.protobuf.Any Value = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + sint64 dir = 2 [json_name = "Dir"]; + google.protobuf.Any value = 3 [json_name = "Value"]; } message FuncTypeExpr { - Attributes Attributes = 1; - repeated FieldTypeExpr Params = 2; - repeated FieldTypeExpr Results = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + repeated FieldTypeExpr params = 2 [json_name = "Params"]; + repeated FieldTypeExpr results = 3 [json_name = "Results"]; } message MapTypeExpr { - Attributes Attributes = 1; - google.protobuf.Any Key = 2; - google.protobuf.Any Value = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any key = 2 [json_name = "Key"]; + google.protobuf.Any value = 3 [json_name = "Value"]; } message StructTypeExpr { - Attributes Attributes = 1; - repeated FieldTypeExpr Fields = 2; + Attributes attributes = 1 [json_name = "Attributes"]; + repeated FieldTypeExpr fields = 2 [json_name = "Fields"]; } message constTypeExpr { - Attributes Attributes = 1; - google.protobuf.Any Source = 2; - google.protobuf.Any Type = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any source = 2 [json_name = "Source"]; + google.protobuf.Any type = 3 [json_name = "Type"]; } message MaybeNativeTypeExpr { - Attributes Attributes = 1; - google.protobuf.Any Type = 2; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any type = 2 [json_name = "Type"]; } message AssignStmt { - Attributes Attributes = 1; - repeated google.protobuf.Any Lhs = 2; - sint64 Op = 3; - repeated google.protobuf.Any Rhs = 4; + Attributes attributes = 1 [json_name = "Attributes"]; + repeated google.protobuf.Any lhs = 2 [json_name = "Lhs"]; + sint64 op = 3 [json_name = "Op"]; + repeated google.protobuf.Any rhs = 4 [json_name = "Rhs"]; } message BlockStmt { - Attributes Attributes = 1; - StaticBlock StaticBlock = 2; - repeated google.protobuf.Any Body = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + StaticBlock static_block = 2 [json_name = "StaticBlock"]; + repeated google.protobuf.Any body = 3 [json_name = "Body"]; } message BranchStmt { - Attributes Attributes = 1; - sint64 Op = 2; - string Label = 3; - uint32 Depth = 4; - sint64 BodyIndex = 5; + Attributes attributes = 1 [json_name = "Attributes"]; + sint64 op = 2 [json_name = "Op"]; + string label = 3 [json_name = "Label"]; + uint32 depth = 4 [json_name = "Depth"]; + sint64 body_index = 5 [json_name = "BodyIndex"]; } message DeclStmt { - Attributes Attributes = 1; - repeated google.protobuf.Any Body = 2; + Attributes attributes = 1 [json_name = "Attributes"]; + repeated google.protobuf.Any body = 2 [json_name = "Body"]; } message DeferStmt { - Attributes Attributes = 1; - CallExpr Call = 2; + Attributes attributes = 1 [json_name = "Attributes"]; + CallExpr call = 2 [json_name = "Call"]; } message ExprStmt { - Attributes Attributes = 1; - google.protobuf.Any X = 2; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any x = 2 [json_name = "X"]; } message ForStmt { - Attributes Attributes = 1; - StaticBlock StaticBlock = 2; - google.protobuf.Any Init = 3; - google.protobuf.Any Cond = 4; - google.protobuf.Any Post = 5; - repeated google.protobuf.Any Body = 6; + Attributes attributes = 1 [json_name = "Attributes"]; + StaticBlock static_block = 2 [json_name = "StaticBlock"]; + google.protobuf.Any init = 3 [json_name = "Init"]; + google.protobuf.Any cond = 4 [json_name = "Cond"]; + google.protobuf.Any post = 5 [json_name = "Post"]; + repeated google.protobuf.Any body = 6 [json_name = "Body"]; } message GoStmt { - Attributes Attributes = 1; - CallExpr Call = 2; + Attributes attributes = 1 [json_name = "Attributes"]; + CallExpr call = 2 [json_name = "Call"]; } message IfStmt { - Attributes Attributes = 1; - StaticBlock StaticBlock = 2; - google.protobuf.Any Init = 3; - google.protobuf.Any Cond = 4; - IfCaseStmt Then = 5; - IfCaseStmt Else = 6; + Attributes attributes = 1 [json_name = "Attributes"]; + StaticBlock static_block = 2 [json_name = "StaticBlock"]; + google.protobuf.Any init = 3 [json_name = "Init"]; + google.protobuf.Any cond = 4 [json_name = "Cond"]; + IfCaseStmt then = 5 [json_name = "Then"]; + IfCaseStmt else = 6 [json_name = "Else"]; } message IfCaseStmt { - Attributes Attributes = 1; - StaticBlock StaticBlock = 2; - repeated google.protobuf.Any Body = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + StaticBlock static_block = 2 [json_name = "StaticBlock"]; + repeated google.protobuf.Any body = 3 [json_name = "Body"]; } message IncDecStmt { - Attributes Attributes = 1; - google.protobuf.Any X = 2; - sint64 Op = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any x = 2 [json_name = "X"]; + sint64 op = 3 [json_name = "Op"]; } message RangeStmt { - Attributes Attributes = 1; - StaticBlock StaticBlock = 2; - google.protobuf.Any X = 3; - google.protobuf.Any Key = 4; - google.protobuf.Any Value = 5; - sint64 Op = 6; - repeated google.protobuf.Any Body = 7; - bool IsMap = 8; - bool IsString = 9; - bool IsArrayPtr = 10; + Attributes attributes = 1 [json_name = "Attributes"]; + StaticBlock static_block = 2 [json_name = "StaticBlock"]; + google.protobuf.Any x = 3 [json_name = "X"]; + google.protobuf.Any key = 4 [json_name = "Key"]; + google.protobuf.Any value = 5 [json_name = "Value"]; + sint64 op = 6 [json_name = "Op"]; + repeated google.protobuf.Any body = 7 [json_name = "Body"]; + bool is_map = 8 [json_name = "IsMap"]; + bool is_string = 9 [json_name = "IsString"]; + bool is_array_ptr = 10 [json_name = "IsArrayPtr"]; } message ReturnStmt { - Attributes Attributes = 1; - repeated google.protobuf.Any Results = 2; + Attributes attributes = 1 [json_name = "Attributes"]; + repeated google.protobuf.Any results = 2 [json_name = "Results"]; } message PanicStmt { - Attributes Attributes = 1; - google.protobuf.Any Exception = 2; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any exception = 2 [json_name = "Exception"]; } message SelectStmt { - Attributes Attributes = 1; - repeated SelectCaseStmt Cases = 2; + Attributes attributes = 1 [json_name = "Attributes"]; + repeated SelectCaseStmt cases = 2 [json_name = "Cases"]; } message SelectCaseStmt { - Attributes Attributes = 1; - StaticBlock StaticBlock = 2; - google.protobuf.Any Comm = 3; - repeated google.protobuf.Any Body = 4; + Attributes attributes = 1 [json_name = "Attributes"]; + StaticBlock static_block = 2 [json_name = "StaticBlock"]; + google.protobuf.Any comm = 3 [json_name = "Comm"]; + repeated google.protobuf.Any body = 4 [json_name = "Body"]; } message SendStmt { - Attributes Attributes = 1; - google.protobuf.Any Chan = 2; - google.protobuf.Any Value = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + google.protobuf.Any chan = 2 [json_name = "Chan"]; + google.protobuf.Any value = 3 [json_name = "Value"]; } message SwitchStmt { - Attributes Attributes = 1; - StaticBlock StaticBlock = 2; - google.protobuf.Any Init = 3; - google.protobuf.Any X = 4; - bool IsTypeSwitch = 5; - repeated SwitchClauseStmt Clauses = 6; - string VarName = 7; + Attributes attributes = 1 [json_name = "Attributes"]; + StaticBlock static_block = 2 [json_name = "StaticBlock"]; + google.protobuf.Any init = 3 [json_name = "Init"]; + google.protobuf.Any x = 4 [json_name = "X"]; + bool is_type_switch = 5 [json_name = "IsTypeSwitch"]; + repeated SwitchClauseStmt clauses = 6 [json_name = "Clauses"]; + string var_name = 7 [json_name = "VarName"]; } message SwitchClauseStmt { - Attributes Attributes = 1; - StaticBlock StaticBlock = 2; - repeated google.protobuf.Any Cases = 3; - repeated google.protobuf.Any Body = 4; + Attributes attributes = 1 [json_name = "Attributes"]; + StaticBlock static_block = 2 [json_name = "StaticBlock"]; + repeated google.protobuf.Any cases = 3 [json_name = "Cases"]; + repeated google.protobuf.Any body = 4 [json_name = "Body"]; } message EmptyStmt { - Attributes Attributes = 1; + Attributes attributes = 1 [json_name = "Attributes"]; } message bodyStmt { - Attributes Attributes = 1; - repeated google.protobuf.Any Body = 2; - sint64 BodyLen = 3; - sint64 NextBodyIndex = 4; - sint64 NumOps = 5; - sint64 NumValues = 6; - sint64 NumExprs = 7; - sint64 NumStmts = 8; - google.protobuf.Any Cond = 9; - google.protobuf.Any Post = 10; - google.protobuf.Any Active = 11; - google.protobuf.Any Key = 12; - google.protobuf.Any Value = 13; - sint64 Op = 14; - sint64 ListLen = 15; - sint64 ListIndex = 16; - MapListItem NextItem = 17; - sint64 StrLen = 18; - sint64 StrIndex = 19; - sint32 NextRune = 20; + Attributes attributes = 1 [json_name = "Attributes"]; + repeated google.protobuf.Any body = 2 [json_name = "Body"]; + sint64 body_len = 3 [json_name = "BodyLen"]; + sint64 next_body_index = 4 [json_name = "NextBodyIndex"]; + sint64 num_ops = 5 [json_name = "NumOps"]; + sint64 num_values = 6 [json_name = "NumValues"]; + sint64 num_exprs = 7 [json_name = "NumExprs"]; + sint64 num_stmts = 8 [json_name = "NumStmts"]; + google.protobuf.Any cond = 9 [json_name = "Cond"]; + google.protobuf.Any post = 10 [json_name = "Post"]; + google.protobuf.Any active = 11 [json_name = "Active"]; + google.protobuf.Any key = 12 [json_name = "Key"]; + google.protobuf.Any value = 13 [json_name = "Value"]; + sint64 op = 14 [json_name = "Op"]; + sint64 list_len = 15 [json_name = "ListLen"]; + sint64 list_index = 16 [json_name = "ListIndex"]; + MapListItem next_item = 17 [json_name = "NextItem"]; + sint64 str_len = 18 [json_name = "StrLen"]; + sint64 str_index = 19 [json_name = "StrIndex"]; + sint32 next_rune = 20 [json_name = "NextRune"]; } message FuncDecl { - Attributes Attributes = 1; - StaticBlock StaticBlock = 2; - NameExpr NameExpr = 3; - bool IsMethod = 4; - FieldTypeExpr Recv = 5; - FuncTypeExpr Type = 6; - repeated google.protobuf.Any Body = 7; + Attributes attributes = 1 [json_name = "Attributes"]; + StaticBlock static_block = 2 [json_name = "StaticBlock"]; + NameExpr name_expr = 3 [json_name = "NameExpr"]; + bool is_method = 4 [json_name = "IsMethod"]; + FieldTypeExpr recv = 5 [json_name = "Recv"]; + FuncTypeExpr type = 6 [json_name = "Type"]; + repeated google.protobuf.Any body = 7 [json_name = "Body"]; } message ImportDecl { - Attributes Attributes = 1; - NameExpr NameExpr = 2; - string PkgPath = 3; + Attributes attributes = 1 [json_name = "Attributes"]; + NameExpr name_expr = 2 [json_name = "NameExpr"]; + string pkg_path = 3 [json_name = "PkgPath"]; } message ValueDecl { - Attributes Attributes = 1; - repeated NameExpr NameExprs = 2; - google.protobuf.Any Type = 3; - repeated google.protobuf.Any Values = 4; - bool Const = 5; + Attributes attributes = 1 [json_name = "Attributes"]; + repeated NameExpr name_exprs = 2 [json_name = "NameExprs"]; + google.protobuf.Any type = 3 [json_name = "Type"]; + repeated google.protobuf.Any values = 4 [json_name = "Values"]; + bool const = 5 [json_name = "Const"]; } message TypeDecl { - Attributes Attributes = 1; - NameExpr NameExpr = 2; - google.protobuf.Any Type = 3; - bool IsAlias = 4; + Attributes attributes = 1 [json_name = "Attributes"]; + NameExpr name_expr = 2 [json_name = "NameExpr"]; + google.protobuf.Any type = 3 [json_name = "Type"]; + bool is_alias = 4 [json_name = "IsAlias"]; } message StaticBlock { - Block Block = 1; - repeated google.protobuf.Any Types = 2; - uint32 NumNames = 3; - repeated string Names = 4; - repeated string Consts = 5; - repeated string Externs = 6; - Location Loc = 7; + Block block = 1 [json_name = "Block"]; + repeated google.protobuf.Any types = 2 [json_name = "Types"]; + uint32 num_names = 3 [json_name = "NumNames"]; + repeated string names = 4 [json_name = "Names"]; + repeated string consts = 5 [json_name = "Consts"]; + repeated string externs = 6 [json_name = "Externs"]; + Location loc = 7 [json_name = "Loc"]; } message FileSet { - repeated FileNode Files = 1; + repeated FileNode files = 1 [json_name = "Files"]; } message FileNode { - Attributes Attributes = 1; - StaticBlock StaticBlock = 2; - string Name = 3; - string PkgName = 4; - repeated google.protobuf.Any Decls = 5; + Attributes attributes = 1 [json_name = "Attributes"]; + StaticBlock static_block = 2 [json_name = "StaticBlock"]; + string name = 3 [json_name = "Name"]; + string pkg_name = 4 [json_name = "PkgName"]; + repeated google.protobuf.Any decls = 5 [json_name = "Decls"]; } message PackageNode { - Attributes Attributes = 1; - StaticBlock StaticBlock = 2; - string PkgPath = 3; - string PkgName = 4; - FileSet FileSet = 5; + Attributes attributes = 1 [json_name = "Attributes"]; + StaticBlock static_block = 2 [json_name = "StaticBlock"]; + string pkg_path = 3 [json_name = "PkgPath"]; + string pkg_name = 4 [json_name = "PkgName"]; + FileSet file_set = 5 [json_name = "FileSet"]; } message RefNode { - Location Location = 1; - google.protobuf.Any BlockNode = 2; + Location location = 1 [json_name = "Location"]; + google.protobuf.Any block_node = 2 [json_name = "BlockNode"]; } message PrimitiveType { - sint64 Value = 1; + sint64 value = 1; } message PointerType { - google.protobuf.Any Elt = 1; + google.protobuf.Any elt = 1 [json_name = "Elt"]; } message ArrayType { - sint64 Len = 1; - google.protobuf.Any Elt = 2; - bool Vrd = 3; + sint64 len = 1 [json_name = "Len"]; + google.protobuf.Any elt = 2 [json_name = "Elt"]; + bool vrd = 3 [json_name = "Vrd"]; } message SliceType { - google.protobuf.Any Elt = 1; - bool Vrd = 2; + google.protobuf.Any elt = 1 [json_name = "Elt"]; + bool vrd = 2 [json_name = "Vrd"]; } message StructType { - string PkgPath = 1; - repeated FieldType Fields = 2; + string pkg_path = 1 [json_name = "PkgPath"]; + repeated FieldType fields = 2 [json_name = "Fields"]; } message FieldType { - string Name = 1; - google.protobuf.Any Type = 2; - bool Embedded = 3; - string Tag = 4; + string name = 1 [json_name = "Name"]; + google.protobuf.Any type = 2 [json_name = "Type"]; + bool embedded = 3 [json_name = "Embedded"]; + string tag = 4 [json_name = "Tag"]; } message FuncType { - repeated FieldType Params = 1; - repeated FieldType Results = 2; + repeated FieldType params = 1 [json_name = "Params"]; + repeated FieldType results = 2 [json_name = "Results"]; } message MapType { - google.protobuf.Any Key = 1; - google.protobuf.Any Value = 2; + google.protobuf.Any key = 1 [json_name = "Key"]; + google.protobuf.Any value = 2 [json_name = "Value"]; } message InterfaceType { - string PkgPath = 1; - repeated FieldType Methods = 2; - string Generic = 3; + string pkg_path = 1 [json_name = "PkgPath"]; + repeated FieldType methods = 2 [json_name = "Methods"]; + string generic = 3 [json_name = "Generic"]; } message TypeType { } message DeclaredType { - string PkgPath = 1; - string Name = 2; - google.protobuf.Any Base = 3; - repeated TypedValue Methods = 4; + string pkg_path = 1 [json_name = "PkgPath"]; + string name = 2 [json_name = "Name"]; + google.protobuf.Any base = 3 [json_name = "Base"]; + repeated TypedValue methods = 4 [json_name = "Methods"]; } message PackageType { } message ChanType { - sint64 Dir = 1; - google.protobuf.Any Elt = 2; + sint64 dir = 1 [json_name = "Dir"]; + google.protobuf.Any elt = 2 [json_name = "Elt"]; } message blockType { } message tupleType { - repeated google.protobuf.Any Elts = 1; + repeated google.protobuf.Any elts = 1 [json_name = "Elts"]; } message RefType { - string ID = 1; + string id = 1 [json_name = "ID"]; } \ No newline at end of file diff --git a/gnovm/pkg/gnolang/go2gno.go b/gnovm/pkg/gnolang/go2gno.go index c3ab4e95b73..ee82cb39555 100644 --- a/gnovm/pkg/gnolang/go2gno.go +++ b/gnovm/pkg/gnolang/go2gno.go @@ -96,9 +96,11 @@ func MustParseExpr(expr string) Expr { return x } -// filename must not include the path. +// ParseFile uses the Go parser to parse body. It then runs [Go2Gno] on the +// resulting AST -- the resulting FileNode is returned, together with any other +// error (including panics, which are recovered) from [Go2Gno]. func ParseFile(filename string, body string) (fn *FileNode, err error) { - // Parse src but stop after processing the imports. + // Use go parser to parse the body. fs := token.NewFileSet() f, err := parser.ParseFile(fs, filename, body, parser.ParseComments|parser.DeclarationErrors) if err != nil { @@ -112,9 +114,9 @@ func ParseFile(filename string, body string) (fn *FileNode, err error) { defer func() { if r := recover(); r != nil { if rerr, ok := r.(error); ok { - err = rerr + err = errors.Wrap(rerr, "parsing file") } else { - err = errors.New(fmt.Sprintf("%v", r)) + err = errors.New(fmt.Sprintf("%v", r)).Stacktrace() } return } @@ -431,7 +433,10 @@ func Go2Gno(fs *token.FileSet, gon ast.Node) (n Node) { } name := toName(gon.Name) type_ := Go2Gno(fs, gon.Type).(*FuncTypeExpr) - body := Go2Gno(fs, gon.Body).(*BlockStmt).Body + var body []Stmt + if gon.Body != nil { + body = Go2Gno(fs, gon.Body).(*BlockStmt).Body + } return &FuncDecl{ IsMethod: isMethod, Recv: recv, diff --git a/gnovm/pkg/gnolang/go2gno_test.go b/gnovm/pkg/gnolang/go2gno_test.go index 6a1413a07e1..d5b94c618b0 100644 --- a/gnovm/pkg/gnolang/go2gno_test.go +++ b/gnovm/pkg/gnolang/go2gno_test.go @@ -8,6 +8,8 @@ import ( ) func TestParseForLoop(t *testing.T) { + t.Parallel() + gocode := `package main func main(){ for i:=0; i<10; i++ { diff --git a/gnovm/pkg/gnolang/go_bench_test.go b/gnovm/pkg/gnolang/go_bench_test.go deleted file mode 100644 index 837d7625f35..00000000000 --- a/gnovm/pkg/gnolang/go_bench_test.go +++ /dev/null @@ -1,420 +0,0 @@ -package gnolang - -import ( - "fmt" - "reflect" - "testing" -) - -type BenchValue interface { - Int32() int32 -} - -type ( - Int32 int32 - Int32a int32 - Int32b int32 - Int32c int32 - Int32d int32 - Int32e int32 - Int32f int32 - Int32g int32 - Int32h int32 - Int32i int32 - Int32j int32 - Int32k int32 -) - -func (i Int32) Int32() int32 { return int32(i) } -func (i Int32a) Int32() int32 { return int32(i) } -func (i Int32b) Int32() int32 { return int32(i) } -func (i Int32c) Int32() int32 { return int32(i) } -func (i Int32d) Int32() int32 { return int32(i) } -func (i Int32e) Int32() int32 { return int32(i) } -func (i Int32f) Int32() int32 { return int32(i) } -func (i Int32g) Int32() int32 { return int32(i) } -func (i Int32h) Int32() int32 { return int32(i) } -func (i Int32i) Int32() int32 { return int32(i) } -func (i Int32j) Int32() int32 { return int32(i) } -func (i Int32k) Int32() int32 { return int32(i) } - -func BenchmarkMapSet(b *testing.B) { - m := make(map[int32]int32) - for i := 0; i < b.N; i++ { - m[int32(i%20)] = int32(i) - } -} - -func BenchmarkMapCreateSet(b *testing.B) { - for i := 0; i < b.N; i++ { - m := make(map[int32]int32) - m[int32(i%20)] = int32(i) - } -} - -func BenchmarkMapCreateSetString(b *testing.B) { - for i := 0; i < b.N; i++ { - m := make(map[string]int32) - m["5"] += 1 - } -} - -// shows that it might be kinda worth it to not use maps but slices for struct -// fields, but for small structs. -func BenchmarkSliceIterate10(b *testing.B) { - fs := []TestField{} - for i := 0; i < 10; i++ { - fs = append(fs, TestField{fmt.Sprintf("%v", i%10), int32(i)}) - } - for i := 0; i < b.N; i++ { - i10 := i % 10 - for j := 0; j < 10; j++ { - if fs[i10].Name == "5" { - fs[i10].Value += 1 - } - } - } - b.Log(fs) -} - -type SomeStruct struct { - Field1 int32 - Field2 int32 -} - -func (s SomeStruct) it() int32 { - return s.Field1 + s.Field2 -} - -// seems to inline. -func BenchmarkStructStack(b *testing.B) { - x := int32(0) - for i := 0; i < b.N; i++ { - s := SomeStruct{Field1: int32(i) % 20, Field2: 1} - x = s.it() - } - b.Log(x) -} - -// this doesn't work -func BenchmarkStructGC(b *testing.B) { - x := int32(0) - gen := func(i int) *SomeStruct { return &SomeStruct{Field1: int32(i) % 20} } - for i := 0; i < b.N; i++ { - s := gen(i) - x = s.Field1 - } - b.Log(x) -} - -type TestField struct { - Name string - Value int32 -} - -func BenchmarkTypeAssertionMethodCall(b *testing.B) { - // This uses no interface. - b.Run("Int32().Int32() (no interface)", func(b *testing.B) { - var v Int32 = Int32(1) - x := int32(0) - for i := 0; i < b.N; i++ { - x += v.Int32() - } - }) - // This calls a method on the interface. - // It's surprising that this is slower than switch concrete assert method - // by an order of magnitude when the alternative enables inlining. - // Perhaps go could do better by first grouping each interface into a - // single giant switch statement. - b.Run("BenchValue().Int32() (interface method)", func(b *testing.B) { - var v BenchValue = Int32(1) - x := int32(0) - for i := 0; i < b.N; i++ { - x += v.Int32() - } - }) - // This type-asserts to a concrete type and calls its method. - b.Run("v.(Int32).Int32() (concrete assert method)", func(b *testing.B) { - var v interface{} = Int32(1) - x := int32(0) - for i := 0; i < b.N; i++ { - x += v.(Int32).Int32() - } - }) - // This switch-type-asserts to a concrete type and calls its method. - // This actually ends up being the best choice, and is even faster than - // calling a method on an interface. - b.Run("case v.(Int32).Int32() (type switch concrete assert method)", func(b *testing.B) { - var v interface{} = Int32(1) - x := int32(0) - for i := 0; i < b.N; i++ { - switch v := v.(type) { - case Int32: - x += v.Int32() - case Int32a: - x += v.Int32() + 1 - case Int32b: - x += v.Int32() + 2 - case Int32c: - x += v.Int32() + 3 - case Int32d: - x += v.Int32() + 4 - case Int32e: - x += v.Int32() + 5 - case Int32f: - x += v.Int32() + 6 - case Int32g: - x += v.Int32() + 7 - case Int32h: - x += v.Int32() + 8 - case Int32i: - x += v.Int32() + 9 - case Int32j: - x += v.Int32() + 10 - case Int32k: - x += v.Int32() + 11 - default: - panic("should not happen") - } - } - }) - // This appears to run fast, not sure what optimization is happening, - // but maybe the initial interface setting is fine as the itable - // info is known statically. - b.Run("MyStruct{Value:Int32(i)} (struct interface field init)", func(b *testing.B) { - type MyStruct struct { - Value BenchValue - } - x := int32(0) - for i := 0; i < b.N; i++ { - s := MyStruct{Value: Int32(i)} - x += s.Value.(Int32).Int32() - } - }) - // This type-asserts to an interface type and calls its method. - // v.(BenchValue) is super slow, see https://billglover.me/2018/09/17/how-expensive-is-a-go-function-call/ - // or use `go tool compile -S test.go` for more info. - b.Run("v.(BenchValue).Int32() (interface assert method)", func(b *testing.B) { - var v interface{} = Int32(1) - x := int32(0) - for i := 0; i < b.N; i++ { - x += v.(BenchValue).Int32() - } - }) -} - -// there is a choice between type-switching on a slice of interfaces, or to -// iterate over a slice of super-structs. -func BenchmarkTypeSwitchOrCreate(b *testing.B) { - type Object interface{} - type StructA struct { - Inner Object - A int - B int - } - type StructB struct { - C int - D int - } - x := make([]Object, 1000) - y := make([]StructA, 1000) - for i := 0; i < 1000; i++ { - x[i] = StructA{StructB{0, 0}, 0, 0} - y[i] = StructA{StructB{0, 0}, 0, 0} - } - c := 0 - b.Run("type-switch", func(b *testing.B) { - for j := 0; j < b.N; j++ { - for i := 0; i < 1000; i++ { - switch xi := x[i].(type) { - case StructA: - switch xi.Inner.(type) { - case StructA: - panic("shouldn't happen") - case StructB: - c++ - } - case StructB: - panic("shouldn't happen") - } - } - } - b.Log(c) - }) - b.Run("super-struct", func(b *testing.B) { - for j := 0; j < b.N; j++ { - for i := 0; i < 1000; i++ { - switch y[i].Inner.(type) { - case StructA: - panic("shouldn't happen") - case StructB: - c++ - } - } - } - b.Log(c) - }) -} - -func BenchmarkReflectValueOf(b *testing.B) { - things := []interface{}{ - int(0), - string(""), - struct{}{}, - } - var rv reflect.Value - for _, thing := range things { - b.Run(reflect.TypeOf(thing).String(), func(b *testing.B) { - for i := 0; i < b.N; i++ { - rv = reflect.ValueOf(thing) - } - }) - } - b.Log(rv) -} - -func BenchmarkReflectAddInt64(b *testing.B) { - var rv reflect.Value = reflect.ValueOf(int64(1)) - var x int64 - for i := 0; i < b.N; i++ { - x += rv.Int() - } - b.Log(x) -} - -func BenchmarkNativeAddInt64(b *testing.B) { - var x int64 - for i := 0; i < b.N; i++ { - x += 1 - } - b.Log(x) -} - -func BenchmarkReflectTypeOf(b *testing.B) { - var x int64 - var rt reflect.Type - for i := 0; i < b.N; i++ { - rt = reflect.TypeOf(x) - } - b.Log(x, rt) -} - -func BenchmarkInterfaceEquality(b *testing.B) { - ctr := 0 - var x interface{} - var y interface{} - for i := 0; i < b.N; i++ { - if x == y { - ctr++ - } - } - b.Log(ctr) -} - -func BenchmarkPointerEquality(b *testing.B) { - ctr := 0 - a := 1 - c := 2 - x := &a - y := &c - for i := 0; i < b.N; i++ { - if x == y { - ctr++ - } - } - b.Log(ctr) -} - -func BenchmarkPointerDerefEquality(b *testing.B) { - ctr := 0 - a := 1 - c := 2 - x := &a - y := &c - for i := 0; i < b.N; i++ { - if *x == *y { - ctr++ - } - } - b.Log(ctr) -} - -func BenchmarkArrayEquality(b *testing.B) { - b.Run("ArrayEquality[1]", func(b *testing.B) { - ctr := 0 - x := [1]byte{0x00} - y := [1]byte{0x00} - for i := 0; i < b.N; i++ { - if x == y { - ctr++ - } - } - b.Log(ctr) - }) - b.Run("ArrayEquality[8]", func(b *testing.B) { - ctr := 0 - x := [8]byte{} - y := [8]byte{} - for i := 0; i < b.N; i++ { - if x == y { - ctr++ - } - } - b.Log(ctr) - }) - b.Run("ArrayEquality[16]", func(b *testing.B) { - ctr := 0 - x := [16]byte{} - y := [16]byte{} - for i := 0; i < b.N; i++ { - if x == y { - ctr++ - } - } - b.Log(ctr) - }) - b.Run("ArrayEquality[20]", func(b *testing.B) { - ctr := 0 - x := [20]byte{} - y := [20]byte{} - for i := 0; i < b.N; i++ { - if x == y { - ctr++ - } - } - b.Log(ctr) - }) - b.Run("ArrayEquality[32]", func(b *testing.B) { - ctr := 0 - x := [32]byte{} - y := [32]byte{} - for i := 0; i < b.N; i++ { - if x == y { - ctr++ - } - } - b.Log(ctr) - }) - b.Run("ArrayEquality[64]", func(b *testing.B) { - ctr := 0 - x := [64]byte{} - y := [64]byte{} - for i := 0; i < b.N; i++ { - if x == y { - ctr++ - } - } - b.Log(ctr) - }) - b.Run("ArrayEquality[256]", func(b *testing.B) { - ctr := 0 - x := [256]byte{} - y := [256]byte{} - for i := 0; i < b.N; i++ { - if x == y { - ctr++ - } - } - b.Log(ctr) - }) -} diff --git a/gnovm/pkg/gnolang/gonative.go b/gnovm/pkg/gnolang/gonative.go index 69e468f755a..f3739dad0f3 100644 --- a/gnovm/pkg/gnolang/gonative.go +++ b/gnovm/pkg/gnolang/gonative.go @@ -676,6 +676,9 @@ func go2GnoValueUpdate(alloc *Allocator, rlm *Realm, lvl int, tv *TypedValue, rv return } +// used for direct comparison to error types +var tError = reflect.TypeOf(new(error)).Elem() + // If recursive is false, this function is like go2GnoValue() but less lazy // (but still not recursive/eager). When recursive is false, it is for // converting Go types to Gno types upon an explicit conversion (via @@ -757,6 +760,15 @@ func go2GnoValue2(alloc *Allocator, store Store, rv reflect.Value, recursive boo // regardless. tv.V = alloc.NewNative(rv) case reflect.Interface: + // special case for errors, which are very often used especially in + // native bindings + if rv.Type() == tError { + tv.T = gErrorType + if !rv.IsNil() { + tv.V = alloc.NewNative(rv.Elem()) + } + return + } panic("not yet implemented") case reflect.Map: panic("not yet implemented") diff --git a/gnovm/pkg/gnolang/gonative_test.go b/gnovm/pkg/gnolang/gonative_test.go index b9b659aae99..e348ffe9c22 100644 --- a/gnovm/pkg/gnolang/gonative_test.go +++ b/gnovm/pkg/gnolang/gonative_test.go @@ -98,6 +98,8 @@ D: } func TestGoNativeDefine3(t *testing.T) { + t.Parallel() + // Create package foo and define Foo. out := new(bytes.Buffer) pkg := NewPackageNode("foo", "test.foo", nil) @@ -135,6 +137,8 @@ D: } func TestCrypto(t *testing.T) { + t.Parallel() + addr := crypto.Address{} store := gonativeTestStore() tv := Go2GnoValue(nilAllocator, store, reflect.ValueOf(addr)) diff --git a/gnovm/pkg/gnolang/helpers.go b/gnovm/pkg/gnolang/helpers.go index c16ea795ea3..b163b6a52a7 100644 --- a/gnovm/pkg/gnolang/helpers.go +++ b/gnovm/pkg/gnolang/helpers.go @@ -105,6 +105,12 @@ func MaybeNativeT(tx interface{}) *MaybeNativeTypeExpr { } } +// FuncD creates a new function declaration. +// +// There is a difference between passing nil to body or passing []Stmt{}: +// nil means that the curly brackets are missing in the source code, indicating +// a declaration for an externally-defined function, while []Stmt{} is simply a +// functions with no statements (func() {}). func FuncD(name interface{}, params, results FieldTypeExprs, body []Stmt) *FuncDecl { return &FuncDecl{ NameExpr: *Nx(name), @@ -440,18 +446,6 @@ var ( precs = [][]string{prec1, prec2, prec3, prec4, prec5} ) -// 0 for prec1... -1 if no match. -func lowestMatch(op string) int { - for i, prec := range precs { - for _, op2 := range prec { - if op == op2 { - return i - } - } - } - return -1 -} - func Ss(b ...Stmt) []Stmt { return b } diff --git a/gnovm/pkg/gnolang/machine.go b/gnovm/pkg/gnolang/machine.go index 94232e014d2..e9e8eba8adc 100644 --- a/gnovm/pkg/gnolang/machine.go +++ b/gnovm/pkg/gnolang/machine.go @@ -46,14 +46,15 @@ type Machine struct { Context interface{} } -// machine.Release() must be called on objects -// created via this constructor -// Machine with new package of given path. -// Creates a new MemRealmer for any new realms. -// Looks in store for package of pkgPath; if not found, -// creates new instances as necessary. -// If pkgPath is zero, the machine has no active package -// and one must be set prior to usage. +// NewMachine initializes a new gno virtual machine, acting as a shorthand +// for [NewMachineWithOptions], setting the given options PkgPath and Store. +// +// The machine will run on the package at the given path, which will be +// retrieved through the given store. If it is not set, the machine has no +// active package, and one must be set prior to usage. +// +// Like for [NewMachineWithOptions], Machines initialized through this +// constructor must be finalized with [Machine.Release]. func NewMachine(pkgPath string, store Store) *Machine { return NewMachineWithOptions( MachineOptions{ @@ -62,12 +63,14 @@ func NewMachine(pkgPath string, store Store) *Machine { }) } +// MachineOptions is used to pass options to [NewMachineWithOptions]. type MachineOptions struct { + // Active package of the given machine; must be set before execution. PkgPath string CheckTypes bool // not yet used ReadOnly bool - Output io.Writer - Store Store + Output io.Writer // default os.Stdout + Store Store // default NewStore(Alloc, nil, nil) Context interface{} Alloc *Allocator // or see MaxAllocBytes. MaxAllocBytes int64 // or 0 for no limit. @@ -87,6 +90,11 @@ var machinePool = sync.Pool{ }, } +// NewMachineWithOptions initializes a new gno virtual machine with the given +// options. +// +// Machines initialized through this constructor must be finalized with +// [Machine.Release]. func NewMachineWithOptions(opts MachineOptions) *Machine { checkTypes := opts.CheckTypes readOnly := opts.ReadOnly @@ -141,11 +149,10 @@ var ( valueZeroed [VMSliceSize]TypedValue ) -// m should not be used after this call -// if m is nil, this will panic -// this is on purpose, to discourage misuse -// and prevent objects that were not taken from -// the pool, to call Release +// Release resets some of the values of *Machine and puts back m into the +// machine pool; for this reason, Release() should be called as a finalizer, +// and m should not be used after this call. Only Machines initialized with this +// package's constructors should be released. func (m *Machine) Release() { // here we zero in the values for the next user m.NumOps = 0 @@ -175,6 +182,9 @@ func (m *Machine) SetActivePackage(pv *PackageValue) { // Upon restart, preprocess all MemPackage and save blocknodes. // This is a temporary measure until we optimize/make-lazy. +// +// NOTE: package paths not beginning with gno.land will be allowed to override, +// to support cases of stdlibs processed through [RunMemPackagesWithOverrides]. func (m *Machine) PreprocessAllFilesAndSaveBlockNodes() { ch := m.Store.IterMemPackage() for memPkg := range ch { @@ -209,8 +219,23 @@ func (m *Machine) PreprocessAllFilesAndSaveBlockNodes() { // and corresponding package node, package value, and types to store. Save // is set to false for tests where package values may be native. func (m *Machine) RunMemPackage(memPkg *std.MemPackage, save bool) (*PackageNode, *PackageValue) { + return m.runMemPackage(memPkg, save, false) +} + +// RunMemPackageWithOverrides works as [RunMemPackage], however after parsing, +// declarations are filtered removing duplicate declarations. +// To control which declaration overrides which, use [ReadMemPackageFromList], +// putting the overrides at the top of the list. +func (m *Machine) RunMemPackageWithOverrides(memPkg *std.MemPackage, save bool) (*PackageNode, *PackageValue) { + return m.runMemPackage(memPkg, save, true) +} + +func (m *Machine) runMemPackage(memPkg *std.MemPackage, save, overrides bool) (*PackageNode, *PackageValue) { // parse files. files := ParseMemPackage(memPkg) + if !overrides && checkDuplicates(files) { + panic(fmt.Errorf("running package %q: duplicate declarations not allowed", memPkg.Path)) + } // make and set package if doesn't exist. pn := (*PackageNode)(nil) pv := (*PackageValue)(nil) @@ -237,6 +262,56 @@ func (m *Machine) RunMemPackage(memPkg *std.MemPackage, save bool) (*PackageNode return pn, pv } +// checkDuplicates returns true if there duplicate declarations in the fset. +func checkDuplicates(fset *FileSet) bool { + defined := make(map[Name]struct{}, 128) + for _, f := range fset.Files { + for _, d := range f.Decls { + var name Name + switch d := d.(type) { + case *FuncDecl: + if d.Name == "init" { //nolint:goconst + continue + } + name = d.Name + if d.IsMethod { + name = Name(destar(d.Recv.Type).String()) + "." + name + } + case *TypeDecl: + name = d.Name + case *ValueDecl: + for _, nx := range d.NameExprs { + if nx.Name == "_" { + continue + } + if _, ok := defined[nx.Name]; ok { + return true + } + defined[nx.Name] = struct{}{} + } + continue + default: + continue + } + if name == "_" { + continue + } + if _, ok := defined[name]; ok { + return true + } + defined[name] = struct{}{} + } + } + return false +} + +func destar(x Expr) Expr { + if x, ok := x.(*StarExpr); ok { + return x.X + } + return x +} + // Tests all test files in a mempackage. // Assumes that the importing of packages is handled elsewhere. // The resulting package value and node become injected with TestMethods and @@ -316,12 +391,8 @@ func (m *Machine) TestFunc(t *testing.T, tv TypedValue) { // mirror of stdlibs/testing.Report var report struct { - Name string - Verbose bool - Failed bool - Skipped bool - Filtered bool - Output string + Skipped bool + Failed bool } err := json.Unmarshal([]byte(ret), &report) if err != nil { @@ -330,17 +401,11 @@ func (m *Machine) TestFunc(t *testing.T, tv TypedValue) { } switch { - case report.Filtered: - // noop case report.Skipped: t.SkipNow() case report.Failed: t.Fail() } - - if report.Output != "" && (report.Verbose || report.Failed) { - t.Log(report.Output) - } }) } @@ -500,7 +565,7 @@ func (m *Machine) runFiles(fns ...*FileNode) { "loop in variable initialization: dependency trail %v circularly depends on %s", loopfindr, dep)) } } - // run dependecy declaration + // run dependency declaration loopfindr = append(loopfindr, dep) runDeclarationFor(fn, *depdecl) loopfindr = loopfindr[:len(loopfindr)-1] diff --git a/gnovm/pkg/gnolang/machine_test.go b/gnovm/pkg/gnolang/machine_test.go index e37fc508cb6..4268e3f3332 100644 --- a/gnovm/pkg/gnolang/machine_test.go +++ b/gnovm/pkg/gnolang/machine_test.go @@ -1,6 +1,16 @@ package gnolang -import "testing" +import ( + "fmt" + "testing" + + dbm "github.com/gnolang/gno/tm2/pkg/db" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/gnolang/gno/tm2/pkg/store/dbadapter" + "github.com/gnolang/gno/tm2/pkg/store/iavl" + stypes "github.com/gnolang/gno/tm2/pkg/store/types" + "github.com/jaekwon/testify/assert" +) func BenchmarkCreateNewMachine(b *testing.B) { for i := 0; i < b.N; i++ { @@ -8,3 +18,41 @@ func BenchmarkCreateNewMachine(b *testing.B) { m.Release() } } + +func TestRunMemPackageWithOverrides_revertToOld(t *testing.T) { + // A test to check revertToOld is correctly putting back an old value, + // after preprocessing fails. + db := dbm.NewMemDB() + baseStore := dbadapter.StoreConstructor(db, stypes.StoreOptions{}) + iavlStore := iavl.StoreConstructor(db, stypes.StoreOptions{}) + store := NewStore(nil, baseStore, iavlStore) + m := NewMachine("std", store) + m.RunMemPackageWithOverrides(&std.MemPackage{ + Name: "std", + Path: "std", + Files: []*std.MemFile{ + {Name: "a.gno", Body: `package std; func Redecl(x int) string { return "1" }`}, + }, + }, true) + result := func() (p string) { + defer func() { + p = fmt.Sprint(recover()) + }() + m.RunMemPackageWithOverrides(&std.MemPackage{ + Name: "std", + Path: "std", + Files: []*std.MemFile{ + {Name: "b.gno", Body: `package std; func Redecl(x int) string { var y string; _, _ = y; return "2" }`}, + }, + }, true) + return + }() + t.Log("panic trying to redeclare invalid func", result) + m.RunStatement(S(Call(X("Redecl"), 11))) + + // Check last value, assuming it is the result of Redecl. + v := m.Values[0] + assert.NotNil(t, v) + assert.Equal(t, v.T.Kind(), StringKind) + assert.Equal(t, v.V, StringValue("1")) +} diff --git a/gnovm/pkg/gnolang/nodes.go b/gnovm/pkg/gnolang/nodes.go index 46308fb3a02..8f2c5054a8a 100644 --- a/gnovm/pkg/gnolang/nodes.go +++ b/gnovm/pkg/gnolang/nodes.go @@ -4,7 +4,6 @@ import ( "fmt" "go/parser" "go/token" - "io/ioutil" "os" "path/filepath" "reflect" @@ -1093,13 +1092,21 @@ func PackageNameFromFileBody(name, body string) Name { return Name(astFile.Name.Name) } -// NOTE: panics if package name is invalid. +// ReadMemPackage initializes a new MemPackage by reading the OS directory +// at dir, and saving it with the given pkgPath (import path). +// The resulting MemPackage will contain the names and content of all *.gno files, +// and additionally README.md, LICENSE, and gno.mod. +// +// ReadMemPackage does not perform validation aside from the package's name; +// the files are not parsed but their contents are merely stored inside a MemFile. +// +// NOTE: panics if package name is invalid (characters must be alphanumeric or _, +// lowercase, and must start with a letter). func ReadMemPackage(dir string, pkgPath string) *std.MemPackage { - files, err := ioutil.ReadDir(dir) + files, err := os.ReadDir(dir) if err != nil { panic(err) } - memPkg := &std.MemPackage{Path: pkgPath} allowedFiles := []string{ // make case insensitive? "gno.mod", "LICENSE", @@ -1108,27 +1115,43 @@ func ReadMemPackage(dir string, pkgPath string) *std.MemPackage { allowedFileExtensions := []string{ ".gno", } - var pkgName Name + list := make([]string, 0, len(files)) for _, file := range files { if file.IsDir() || strings.HasPrefix(file.Name(), ".") || (!endsWith(file.Name(), allowedFileExtensions) && !contains(allowedFiles, file.Name())) { continue } - fpath := filepath.Join(dir, file.Name()) + list = append(list, filepath.Join(dir, file.Name())) + } + return ReadMemPackageFromList(list, pkgPath) +} + +// ReadMemPackageFromList creates a new [std.MemPackage] with the specified pkgPath, +// containing the contents of all the files provided in the list slice. +// No parsing or validation is done on the filenames. +// +// NOTE: panics if package name is invalid (characters must be alphanumeric or _, +// lowercase, and must start with a letter). +func ReadMemPackageFromList(list []string, pkgPath string) *std.MemPackage { + memPkg := &std.MemPackage{Path: pkgPath} + var pkgName Name + for _, fpath := range list { + fname := filepath.Base(fpath) bz, err := os.ReadFile(fpath) if err != nil { panic(err) } - if pkgName == "" && strings.HasSuffix(file.Name(), ".gno") { - pkgName = PackageNameFromFileBody(file.Name(), string(bz)) + // XXX: should check that all pkg names are the same (else package is invalid) + if pkgName == "" && strings.HasSuffix(fname, ".gno") { + pkgName = PackageNameFromFileBody(fname, string(bz)) if strings.HasSuffix(string(pkgName), "_test") { pkgName = pkgName[:len(pkgName)-len("_test")] } } memPkg.Files = append(memPkg.Files, &std.MemFile{ - Name: file.Name(), + Name: fname, Body: string(bz), }) } @@ -1142,33 +1165,29 @@ func ReadMemPackage(dir string, pkgPath string) *std.MemPackage { return memPkg } -func PrecompileMemPackage(memPkg *std.MemPackage) error { - return nil -} - -// Returns the code fileset minus any spurious or test files. +// ParseMemPackage executes [ParseFile] on each file of the memPkg, excluding +// test and spurious (non-gno) files. The resulting *FileSet is returned. +// +// If one of the files has a different package name than memPkg.Name, +// or [ParseFile] returns an error, ParseMemPackage panics. func ParseMemPackage(memPkg *std.MemPackage) (fset *FileSet) { fset = &FileSet{} for _, mfile := range memPkg.Files { - if !strings.HasSuffix(mfile.Name, ".gno") { - continue // skip spurious file. + if !strings.HasSuffix(mfile.Name, ".gno") || + endsWith(mfile.Name, []string{"_test.gno", "_filetest.gno"}) { + continue // skip spurious or test file. } n, err := ParseFile(mfile.Name, mfile.Body) if err != nil { panic(errors.Wrap(err, "parsing file "+mfile.Name)) } - if strings.HasSuffix(mfile.Name, "_test.gno") { - // skip test file. - } else if strings.HasSuffix(mfile.Name, "_filetest.gno") { - // skip test file. - } else if memPkg.Name == string(n.PkgName) { - // add package file. - fset.AddFiles(n) - } else { + if memPkg.Name != string(n.PkgName) { panic(fmt.Sprintf( "expected package name [%s] but got [%s]", memPkg.Name, n.PkgName)) } + // add package file. + fset.AddFiles(n) } return fset } @@ -1237,7 +1256,11 @@ func (fs *FileSet) GetDeclFor(n Name) (*FileNode, *Decl) { func (fs *FileSet) GetDeclForSafe(n Name) (*FileNode, *Decl, bool) { // XXX index to bound to linear time. - for _, fn := range fs.Files { + + // Iteration happens reversing fs.Files; this is because the LAST declaration + // of n is what we are looking for. + for i := len(fs.Files) - 1; i >= 0; i-- { + fn := fs.Files[i] for i, dn := range fn.Decls { if _, isImport := dn.(*ImportDecl); isImport { // imports in other files don't count. @@ -1378,6 +1401,7 @@ func (x *PackageNode) DefineNative(n Name, ps, rs FieldTypeExprs, native func(*M if native == nil { panic("DefineNative expects a function, but got nil") } + fd := FuncD(n, ps, rs, nil) fd = Preprocess(nil, x, fd).(*FuncDecl) ft := evalStaticType(nil, x, &fd.Type).(*FuncType) @@ -1458,6 +1482,22 @@ type StaticBlock struct { Consts []Name // TODO consider merging with Names. Externs []Name Loc Location + + // temporary storage for rolling back redefinitions. + oldValues []oldValue +} + +type oldValue struct { + idx uint16 + value Value +} + +// revert values upon failure of redefinitions. +func (sb *StaticBlock) revertToOld() { + for _, ov := range sb.oldValues { + sb.Block.Values[ov.idx].V = ov.value + } + sb.oldValues = nil } // Implements BlockNode @@ -1657,7 +1697,6 @@ func (sb *StaticBlock) GetStaticTypeOfAt(store Store, path ValuePath) Type { path.Depth -= 1 } } - panic("should not happen") } // Implements BlockNode. @@ -1707,12 +1746,11 @@ func (sb *StaticBlock) GetValueRef(store Store, n Name) *TypedValue { // values, which are pre-computeed in the preprocessor. // Once a typed value is defined, it cannot be changed. // -// NOTE: Currently tv.V is only set when the value -// represents a Type(Value). The purpose of tv is to describe -// the invariant of a named value, at the minimum its type, -// but also sometimes the typeval value; but we could go -// further and store preprocessed constant results here -// too. See "anyValue()" and "asValue()" for usage. +// NOTE: Currently tv.V is only set when the value represents a Type(Value) or +// a FuncValue. The purpose of tv is to describe the invariant of a named +// value, at the minimum its type, but also sometimes the typeval value; but we +// could go further and store preprocessed constant results here too. See +// "anyValue()" and "asValue()" for usage. func (sb *StaticBlock) Define(n Name, tv TypedValue) { sb.Define2(false, n, tv.T, tv) } @@ -1755,16 +1793,30 @@ func (sb *StaticBlock) Define2(isConst bool, n Name, st Type, tv TypedValue) { } old := sb.Block.Values[idx] if !old.IsUndefined() { - if tv.T.TypeID() != old.T.TypeID() { - panic(fmt.Sprintf( - "StaticBlock.Define2(%s) cannot change .T; was %v, new %v", - n, old.T, tv.T)) - } - if tv.V != old.V { - panic(fmt.Sprintf( - "StaticBlock.Define2(%s) cannot change .V", - n)) + if tv.T.Kind() == FuncKind && tv.T.(*FuncType).IsZero() { + // special case, + // allow re-predefining for func upgrades. + // keep the old type so we can check it at preprocessor. + tv.T = old.T + fv := tv.V.(*FuncValue) + fv.Type = old.T + st = old.T + sb.oldValues = append(sb.oldValues, + oldValue{idx, old.V}) + } else { + if tv.T.TypeID() != old.T.TypeID() { + panic(fmt.Sprintf( + "StaticBlock.Define2(%s) cannot change .T; was %v, new %v", + n, old.T, tv.T)) + } + if tv.V != old.V { + panic(fmt.Sprintf( + "StaticBlock.Define2(%s) cannot change .V", + n)) + } } + // Allow re-definitions if they have the same type. + // (In normal scenarios, duplicate declarations are "caught" by RunMemPackage.) } sb.Block.Values[idx] = tv sb.Types[idx] = st @@ -2024,10 +2076,12 @@ const ( ATTR_INJECTED GnoAttribute = "ATTR_INJECTED" ) +var rePkgName = regexp.MustCompile(`^[a-z][a-z0-9_]+$`) + // TODO: consider length restrictions. +// If this function is changed, ReadMemPackage's documentation should be updated accordingly. func validatePkgName(name string) { - if nameOK, _ := regexp.MatchString( - `^[a-z][a-z0-9_]+$`, name); !nameOK { + if !rePkgName.MatchString(name) { panic(fmt.Sprintf("cannot create package with invalid name %q", name)) } } diff --git a/gnovm/pkg/gnolang/op_binary.go b/gnovm/pkg/gnolang/op_binary.go index 13156133e38..9162a710d7d 100644 --- a/gnovm/pkg/gnolang/op_binary.go +++ b/gnovm/pkg/gnolang/op_binary.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/cockroachdb/apd" + "github.com/cockroachdb/apd/v3" ) // ---------------------------------------- @@ -489,7 +489,7 @@ func isEql(store Store, lv, rv *TypedValue) bool { func isLss(lv, rv *TypedValue) bool { switch lv.T.Kind() { case StringKind: - return (lv.V.(StringValue) < rv.V.(StringValue)) + return (lv.GetString() < rv.GetString()) case IntKind: return (lv.GetInt() < rv.GetInt()) case Int8Kind: @@ -533,7 +533,7 @@ func isLss(lv, rv *TypedValue) bool { func isLeq(lv, rv *TypedValue) bool { switch lv.T.Kind() { case StringKind: - return (lv.V.(StringValue) <= rv.V.(StringValue)) + return (lv.GetString() <= rv.GetString()) case IntKind: return (lv.GetInt() <= rv.GetInt()) case Int8Kind: @@ -577,7 +577,7 @@ func isLeq(lv, rv *TypedValue) bool { func isGtr(lv, rv *TypedValue) bool { switch lv.T.Kind() { case StringKind: - return (lv.V.(StringValue) > rv.V.(StringValue)) + return (lv.GetString() > rv.GetString()) case IntKind: return (lv.GetInt() > rv.GetInt()) case Int8Kind: @@ -621,7 +621,7 @@ func isGtr(lv, rv *TypedValue) bool { func isGeq(lv, rv *TypedValue) bool { switch lv.T.Kind() { case StringKind: - return (lv.V.(StringValue) >= rv.V.(StringValue)) + return (lv.GetString() >= rv.GetString()) case IntKind: return (lv.GetInt() >= rv.GetInt()) case Int8Kind: diff --git a/gnovm/pkg/gnolang/op_call.go b/gnovm/pkg/gnolang/op_call.go index f43a593c50e..c7274dd73af 100644 --- a/gnovm/pkg/gnolang/op_call.go +++ b/gnovm/pkg/gnolang/op_call.go @@ -58,6 +58,13 @@ func (m *Machine) doOpCall() { clo := fr.Func.GetClosure(m.Store) b := m.Alloc.NewBlock(fr.Func.GetSource(m.Store), clo) m.PushBlock(b) + if fv.nativeBody == nil && fv.NativePkg != "" { + // native function, unmarshaled so doesn't have nativeBody yet + fv.nativeBody = m.Store.GetNative(fv.NativePkg, fv.NativeName) + if fv.nativeBody == nil { + panic(fmt.Sprintf("natively defined function (%q).%s could not be resolved", fv.NativePkg, fv.NativeName)) + } + } if fv.nativeBody == nil { fbody := fv.GetBodyFromSource(m.Store) if len(ft.Results) == 0 { @@ -154,7 +161,11 @@ func (m *Machine) doOpCall() { } // TODO: some more pt <> pv.Type // reconciliations/conversions necessary. - b.Values[i] = pv + + // Make a copy so that a reference to the argument isn't used + // in cases where the non-primitive value type is represented + // as a pointer, *StructValue, for example. + b.Values[i] = pv.Copy(m.Alloc) } } diff --git a/gnovm/pkg/gnolang/op_eval.go b/gnovm/pkg/gnolang/op_eval.go index 83dd3737b6b..bf4ac88aa25 100644 --- a/gnovm/pkg/gnolang/op_eval.go +++ b/gnovm/pkg/gnolang/op_eval.go @@ -9,13 +9,18 @@ import ( "strconv" "strings" - "github.com/cockroachdb/apd" + "github.com/cockroachdb/apd/v3" +) + +var ( + reFloat = regexp.MustCompile(`^[0-9\.]+([eE][\-\+]?[0-9]+)?$`) + reHexFloat = regexp.MustCompile(`^0[xX][0-9a-fA-F\.]+([pP][\-\+]?[0-9a-fA-F]+)?$`) ) func (m *Machine) doOpEval() { x := m.PeekExpr(1) if debug { - debug.Printf("EVAL: %v\n", x) + debug.Printf("EVAL: (%T) %v\n", x, x) // fmt.Println(m.String()) } // This case moved out of switch for performance. @@ -47,8 +52,8 @@ func (m *Machine) doOpEval() { bi := big.NewInt(0) // TODO optimize. // TODO deal with base. - if len(x.Value) > 2 && x.Value[0] == '0' { - var ok bool = false + var ok bool + if len(x.Value) >= 2 && x.Value[0] == '0' { switch x.Value[1] { case 'b', 'B': _, ok = bi.SetString(x.Value[2:], 2) @@ -56,6 +61,8 @@ func (m *Machine) doOpEval() { _, ok = bi.SetString(x.Value[2:], 8) case 'x', 'X': _, ok = bi.SetString(x.Value[2:], 16) + case '0', '1', '2', '3', '4', '5', '6', '7': + _, ok = bi.SetString(x.Value, 8) default: ok = false } @@ -79,7 +86,7 @@ func (m *Machine) doOpEval() { case FLOAT: x.Value = strings.ReplaceAll(x.Value, "_", "") - if matched, _ := regexp.MatchString(`^[0-9\.]+([eE][\-\+]?[0-9]+)?$`, x.Value); matched { + if reFloat.MatchString(x.Value) { value := x.Value bd, c, err := apd.NewFromString(value) if err != nil { @@ -97,7 +104,7 @@ func (m *Machine) doOpEval() { V: BigdecValue{V: bd}, }) return - } else if matched, _ := regexp.MatchString(`^0[xX][0-9a-fA-F\.]+([pP][\-\+]?[0-9a-fA-F]+)?$`, x.Value); matched { + } else if reHexFloat.MatchString(x.Value) { originalInput := x.Value value := x.Value[2:] var hexString string @@ -160,8 +167,7 @@ func (m *Machine) doOpEval() { panic(fmt.Sprintf("error computing exponent: %v", err)) } // Step 3 make Decimal from mantissa and exp. - var dValue *big.Int - dValue = new(big.Int) + dValue := new(apd.BigInt) _, ok := dValue.SetString(hexString, 16) if !ok { panic(fmt.Sprintf("can't convert %s to decimal", value)) diff --git a/gnovm/pkg/gnolang/op_expressions.go b/gnovm/pkg/gnolang/op_expressions.go index b46e343b00e..b3bf240aea1 100644 --- a/gnovm/pkg/gnolang/op_expressions.go +++ b/gnovm/pkg/gnolang/op_expressions.go @@ -78,8 +78,12 @@ func (m *Machine) doOpIndex2() { func (m *Machine) doOpSelector() { sx := m.PopExpr().(*SelectorExpr) xv := m.PeekValue(1) - res := xv.GetPointerTo(m.Alloc, m.Store, sx.Path) - *xv = res.Deref() // reuse as result + res := xv.GetPointerTo(m.Alloc, m.Store, sx.Path).Deref() + if debug { + m.Printf("-v[S] %v\n", xv) + m.Printf("+v[S] %v\n", res) + } + *xv = res // reuse as result } func (m *Machine) doOpSlice() { diff --git a/gnovm/pkg/gnolang/op_inc_dec.go b/gnovm/pkg/gnolang/op_inc_dec.go index 80987707035..84c39716eec 100644 --- a/gnovm/pkg/gnolang/op_inc_dec.go +++ b/gnovm/pkg/gnolang/op_inc_dec.go @@ -1,5 +1,12 @@ package gnolang +import ( + "fmt" + "math/big" + + "github.com/cockroachdb/apd/v3" +) + func (m *Machine) doOpInc() { s := m.PopStmt().(*IncDecStmt) @@ -42,8 +49,26 @@ func (m *Machine) doOpInc() { lv.SetUint32(lv.GetUint32() + 1) case Uint64Type: lv.SetUint64(lv.GetUint64() + 1) + case Float32Type: + lv.SetFloat32(lv.GetFloat32() + 1) + case Float64Type: + lv.SetFloat64(lv.GetFloat64() + 1) + case BigintType, UntypedBigintType: + lb := lv.GetBigInt() + lb = big.NewInt(0).Add(lb, big.NewInt(1)) + lv.V = BigintValue{V: lb} + case BigdecType, UntypedBigdecType: + lb := lv.GetBigDec() + sum := apd.New(0, 0) + cond, err := apd.BaseContext.WithPrecision(0).Add(sum, lb, apd.New(1, 0)) + if err != nil { + panic(fmt.Sprintf("bigdec addition error: %v", err)) + } else if cond.Inexact() { + panic(fmt.Sprintf("bigdec addition inexact: %v + 1", lb)) + } + lv.V = BigdecValue{V: sum} default: - panic("unexpected type in in operation") + panic(fmt.Sprintf("unexpected type %s in inc/dec operation", lv.T)) } // Mark dirty in realm. @@ -94,8 +119,26 @@ func (m *Machine) doOpDec() { lv.SetUint32(lv.GetUint32() - 1) case Uint64Type: lv.SetUint64(lv.GetUint64() - 1) + case Float32Type: + lv.SetFloat32(lv.GetFloat32() - 1) + case Float64Type: + lv.SetFloat64(lv.GetFloat64() - 1) + case BigintType, UntypedBigintType: + lb := lv.GetBigInt() + lb = big.NewInt(0).Sub(lb, big.NewInt(1)) + lv.V = BigintValue{V: lb} + case BigdecType, UntypedBigdecType: + lb := lv.GetBigDec() + sum := apd.New(0, 0) + cond, err := apd.BaseContext.WithPrecision(0).Sub(sum, lb, apd.New(1, 0)) + if err != nil { + panic(fmt.Sprintf("bigdec addition error: %v", err)) + } else if cond.Inexact() { + panic(fmt.Sprintf("bigdec addition inexact: %v + 1", lb)) + } + lv.V = BigdecValue{V: sum} default: - panic("unexpected type in in operation") + panic(fmt.Sprintf("unexpected type %s in inc/dec operation", lv.T)) } // Mark dirty in realm. diff --git a/gnovm/pkg/gnolang/op_unary.go b/gnovm/pkg/gnolang/op_unary.go index 331bea63ff9..9c330c7f8f1 100644 --- a/gnovm/pkg/gnolang/op_unary.go +++ b/gnovm/pkg/gnolang/op_unary.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/cockroachdb/apd" + "github.com/cockroachdb/apd/v3" ) func (m *Machine) doOpUpos() { diff --git a/gnovm/pkg/gnolang/package_test.go b/gnovm/pkg/gnolang/package_test.go index 47efd736bac..f219ba9b23f 100644 --- a/gnovm/pkg/gnolang/package_test.go +++ b/gnovm/pkg/gnolang/package_test.go @@ -12,6 +12,8 @@ import ( // Tries to reproduce the bug #1036 on all registered types func TestAminoJSONRegisteredTypes(t *testing.T) { + t.Parallel() + for _, typ := range Package.Types { // Instantiate registered type x := reflect.New(typ.Type).Interface() diff --git a/gnovm/pkg/gnolang/precompile.go b/gnovm/pkg/gnolang/precompile.go index f7a10d5589a..42748145de8 100644 --- a/gnovm/pkg/gnolang/precompile.go +++ b/gnovm/pkg/gnolang/precompile.go @@ -7,16 +7,16 @@ import ( "go/format" "go/parser" "go/token" - "io/ioutil" "os" "os/exec" "path/filepath" "sort" "strings" - "github.com/gnolang/gno/tm2/pkg/std" "go.uber.org/multierr" "golang.org/x/tools/go/ast/astutil" + + "github.com/gnolang/gno/tm2/pkg/std" ) const ( @@ -37,10 +37,10 @@ var stdlibWhitelist = []string{ "crypto/md5", "crypto/sha1", "crypto/sha256", - "encoding/json", "encoding/base64", "encoding/binary", "encoding/hex", + "encoding/json", "encoding/xml", "errors", "flag", @@ -112,7 +112,7 @@ func GetPrecompileFilenameAndTags(gnoFilePath string) (targetFilename, tags stri func PrecompileAndCheckMempkg(mempkg *std.MemPackage) error { gofmt := "gofmt" - tmpDir, err := ioutil.TempDir("", mempkg.Name) + tmpDir, err := os.MkdirTemp("", mempkg.Name) if err != nil { return err } @@ -151,7 +151,7 @@ func Precompile(source string, tags string, filename string) (*precompileResult, var out bytes.Buffer fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "tmp.gno", source, parser.ParseComments) + f, err := parser.ParseFile(fset, filename, source, parser.ParseComments) if err != nil { return nil, fmt.Errorf("parse: %w", err) } diff --git a/gnovm/pkg/gnolang/precompile_test.go b/gnovm/pkg/gnolang/precompile_test.go index 3d50353505f..e2cb5e92d86 100644 --- a/gnovm/pkg/gnolang/precompile_test.go +++ b/gnovm/pkg/gnolang/precompile_test.go @@ -12,6 +12,8 @@ import ( ) func TestPrecompile(t *testing.T) { + t.Parallel() + cases := []struct { name string source string @@ -56,6 +58,8 @@ func TestPrecompile(t *testing.T) { for _, c := range cases { c := c // scopelint t.Run(c.name, func(t *testing.T) { + t.Parallel() + // parse gno fset := token.NewFileSet() f, err := parser.ParseFile(fset, "foo.go", c.source, parser.ParseComments) diff --git a/gnovm/pkg/gnolang/preprocess.go b/gnovm/pkg/gnolang/preprocess.go index e02a158fcf1..1d215e4d94b 100644 --- a/gnovm/pkg/gnolang/preprocess.go +++ b/gnovm/pkg/gnolang/preprocess.go @@ -154,24 +154,27 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { defer func() { if r := recover(); r != nil { - fmt.Println("--- preprocess stack ---") - for i := len(stack) - 1; i >= 0; i-- { - sbn := stack[i] - fmt.Printf("stack %d: %s\n", i, sbn.String()) - } - fmt.Println("------------------------") // before re-throwing the error, append location information to message. loc := last.GetLocation() if nline := n.GetLine(); nline > 0 { loc.Line = nline } - if rerr, ok := r.(error); ok { + + var err error + rerr, ok := r.(error) + if ok { // NOTE: gotuna/gorilla expects error exceptions. - panic(errors.Wrap(rerr, loc.String())) + err = errors.Wrap(rerr, loc.String()) } else { // NOTE: gotuna/gorilla expects error exceptions. - panic(errors.New(fmt.Sprintf("%s: %v", loc.String(), r))) + err = errors.New(fmt.Sprintf("%s: %v", loc.String(), r)) } + + // Re-throw the error after wrapping it with the preprocessing stack information. + panic(&PreprocessError{ + err: err, + stack: stack, + }) } }() if debug { @@ -755,6 +758,13 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { resn := Preprocess(store, last, n2) return resn, TRANS_CONTINUE } + + // Left and right hand expressions must evaluate to a boolean typed value if + // the operation is a logical AND or OR. + if (n.Op == LAND || n.Op == LOR) && (lt.Kind() != BoolKind || rt.Kind() != BoolKind) { + panic("operands of boolean operators must evaluate to boolean typed values") + } + // General case. lcx, lic := n.Left.(*ConstExpr) rcx, ric := n.Right.(*ConstExpr) @@ -951,6 +961,7 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { } n.NumArgs = 1 if arg0, ok := n.Args[0].(*ConstExpr); ok { + var constConverted bool ct := evalStaticType(store, last, n.Func) // As a special case, if a decimal cannot // be represented as an integer, it cannot be converted to one, @@ -967,10 +978,20 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { arg0)) } } + convertConst(store, last, arg0, ct) + constConverted = true + case SliceKind: + if ct.Elem().Kind() == Uint8Kind { // bypass []byte("xxx") + n.SetAttribute(ATTR_TYPEOF_VALUE, ct) + return n, TRANS_CONTINUE + } } // (const) untyped decimal -> float64. // (const) untyped bigint -> int. - convertConst(store, last, arg0, nil) + if !constConverted { + convertConst(store, last, arg0, nil) + } + // evaluate the new expression. cx := evalConst(store, last, n) // Though cx may be undefined if ct is interface, @@ -1005,6 +1026,29 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { args1 = Preprocess(nil, last, args1).(Expr) n.Args[1] = args1 } + } else { + var tx *constTypeExpr // array type expr, lazily initialized + // Another special case for append: adding untyped constants. + // They must be converted to the array type for consistency. + for i, arg := range n.Args[1:] { + if _, ok := arg.(*ConstExpr); !ok { + // Consider only constant expressions. + continue + } + if t1 := evalStaticTypeOf(store, last, arg); t1 != nil && !isUntyped(t1) { + // Consider only untyped values (including nil). + continue + } + + if tx == nil { + // Get the array type from the first argument. + s0 := evalStaticTypeOf(store, last, n.Args[0]) + tx = constType(arg, s0.Elem()) + } + // Convert to the array type. + arg1 := Call(tx, arg) + n.Args[i+1] = Preprocess(nil, last, arg1).(Expr) + } } } else if fv.PkgPath == uversePkgPath && fv.Name == "copy" { if len(n.Args) == 2 { @@ -1817,8 +1861,13 @@ func Preprocess(store Store, ctx BlockNode, n Node) Node { for i, vx := range n.Values { if cx, ok := vx.(*ConstExpr); ok && !cx.TypedValue.IsUndefined() { - // if value is non-nil const expr: - tvs[i] = cx.TypedValue + if n.Const { + // const _ = : static block should contain value + tvs[i] = cx.TypedValue + } else { + // var _ = : static block should NOT contain value + tvs[i] = anyValue(cx.TypedValue.T) + } } else { // for var decls of non-const expr. st := sts[i] @@ -2909,7 +2958,7 @@ func predefineNow2(store Store, last BlockNode, d Decl, m map[Name]struct{}) (De } else { dt = rt.(*DeclaredType) } - dt.DefineMethod(&FuncValue{ + if !dt.TryDefineMethod(&FuncValue{ Type: ft, IsMethod: true, Source: cd, @@ -2919,16 +2968,34 @@ func predefineNow2(store Store, last BlockNode, d Decl, m map[Name]struct{}) (De PkgPath: pkg.PkgPath, body: cd.Body, nativeBody: nil, - }) + }) { + // Revert to old function declarations in the package we're preprocessing. + pkg := packageOf(last) + pkg.StaticBlock.revertToOld() + panic(fmt.Sprintf("redeclaration of method %s.%s", + dt.Name, cd.Name)) + } } else { ftv := pkg.GetValueRef(store, cd.Name) ft := ftv.T.(*FuncType) cd.Type = *Preprocess(store, last, &cd.Type).(*FuncTypeExpr) ft2 := evalStaticType(store, last, &cd.Type).(*FuncType) - *ft = *ft2 + if !ft.IsZero() { + // redefining function. + // make sure the type is the same. + if ft.TypeID() != ft2.TypeID() { + panic(fmt.Sprintf( + "Redefinition (%s) cannot change .T; was %v, new %v", + cd, ft, ft2)) + } + // keep the orig type. + } else { + *ft = *ft2 + } // XXX replace attr w/ ft? // return Preprocess(store, last, cd).(Decl), true } + // Full type declaration/preprocessing already done in tryPredefine return d, false case *ValueDecl: return Preprocess(store, last, cd).(Decl), true @@ -3122,19 +3189,30 @@ func tryPredefine(store Store, last BlockNode, d Decl) (un Name) { } // define a FuncValue w/ above type as d.Name. // fill in later during *FuncDecl:BLOCK. + fv := &FuncValue{ + Type: ft, + IsMethod: false, + Source: d, + Name: d.Name, + Closure: nil, // set lazily. + FileName: fileNameOf(last), + PkgPath: pkg.PkgPath, + body: d.Body, + nativeBody: nil, + } + // NOTE: fv.body == nil means no body (ie. not even curly braces) + // len(fv.body) == 0 could mean also {} (ie. no statements inside) + if fv.body == nil && store != nil { + fv.nativeBody = store.GetNative(pkg.PkgPath, d.Name) + if fv.nativeBody == nil { + panic(fmt.Sprintf("function %s does not have a body but is not natively defined", d.Name)) + } + fv.NativePkg = pkg.PkgPath + fv.NativeName = d.Name + } pkg.Define(d.Name, TypedValue{ T: ft, - V: &FuncValue{ - Type: ft, - IsMethod: false, - Source: d, - Name: d.Name, - Closure: nil, // set lazily. - FileName: fileNameOf(last), - PkgPath: pkg.PkgPath, - body: d.Body, - nativeBody: nil, - }, + V: fv, }) if d.Name == "init" { // init functions can't be referenced. @@ -3338,23 +3416,6 @@ func elideCompositeExpr(vx *Expr, vt Type) { } } -// returns true of x is exactly `nil`. -func isNilExpr(x Expr) bool { - if nx, ok := x.(*NameExpr); ok { - return nx.Name == nilStr - } - return false -} - -func isNilComparableKind(k Kind) bool { - switch k { - case SliceKind, MapKind, FuncKind: - return true - default: - return false - } -} - // returns number of args, or if arg is a call result, // the number of results of the return tuple type. func countNumArgs(store Store, last BlockNode, n *CallExpr) (numArgs int) { @@ -3374,13 +3435,6 @@ func countNumArgs(store Store, last BlockNode, n *CallExpr) (numArgs int) { } } -func mergeNames(a, b []Name) []Name { - c := make([]Name, len(a)+len(b)) - copy(c, a) - copy(c[len(a):], b) - return c -} - // This is to be run *after* preprocessing is done, // to determine the order of var decl execution // (which may include functions which may refer to package vars). diff --git a/gnovm/pkg/gnolang/realm.go b/gnovm/pkg/gnolang/realm.go index 567aea58284..519b183ad3a 100644 --- a/gnovm/pkg/gnolang/realm.go +++ b/gnovm/pkg/gnolang/realm.go @@ -151,6 +151,7 @@ func (rlm *Realm) DidUpdate(po, xo, co Object) { // Updates to .newCreated/.newEscaped /.newDeleted made here. (first gen) // More appends happen during FinalizeRealmTransactions(). (second+ gen) rlm.MarkDirty(po) + if co != nil { co.IncRefCount() if co.GetRefCount() > 1 { @@ -166,6 +167,7 @@ func (rlm *Realm) DidUpdate(po, xo, co Object) { rlm.MarkNewReal(co) } } + if xo != nil { xo.DecRefCount() if xo.GetRefCount() == 0 { @@ -1129,18 +1131,23 @@ func copyValueWithRefs(parent Object, val Value) Value { if cv.Closure != nil { closure = toRefValue(parent, cv.Closure) } - if cv.nativeBody != nil { + // nativeBody funcs which don't come from NativeStore (and thus don't + // have NativePkg/Name) can't be persisted, and should not be able + // to get here anyway. + if cv.nativeBody != nil && cv.NativePkg == "" { panic("should not happen") } ft := copyTypeWithRefs(cv.Type) return &FuncValue{ - Type: ft, - IsMethod: cv.IsMethod, - Source: source, - Name: cv.Name, - Closure: closure, - FileName: cv.FileName, - PkgPath: cv.PkgPath, + Type: ft, + IsMethod: cv.IsMethod, + Source: source, + Name: cv.Name, + Closure: closure, + FileName: cv.FileName, + PkgPath: cv.PkgPath, + NativePkg: cv.NativePkg, + NativeName: cv.NativeName, } case *BoundMethodValue: fnc := copyValueWithRefs(cv, cv.Func).(*FuncValue) diff --git a/gnovm/pkg/gnolang/scanner.go b/gnovm/pkg/gnolang/scanner.go index 87695c0f346..f0b3f34c9ad 100644 --- a/gnovm/pkg/gnolang/scanner.go +++ b/gnovm/pkg/gnolang/scanner.go @@ -192,21 +192,3 @@ func (ss *scanner) advanceEscapeSequence() bool { return ss.done() } } - -// pops the next monoid term. -// The result is a string enclosed in balanced parentheses, -// brackets, or quotes; or what comes before such things. -// scanner doesn't understand operators, so a polynomial -// expression could be a single monoid as far as this scanner -// is concerned. TODO Chop functions should maybe use this. -func (ss *scanner) popMonoid() string { - startOut := ss.out() - start := ss.idx - for !ss.advance() { - if ss.out() != startOut { - end := ss.idx - return string(ss.rnz[start:end]) - } - } - panic("no monoid") -} diff --git a/gnovm/pkg/gnolang/store.go b/gnovm/pkg/gnolang/store.go index d3628edf216..be4a854e7ce 100644 --- a/gnovm/pkg/gnolang/store.go +++ b/gnovm/pkg/gnolang/store.go @@ -11,14 +11,15 @@ import ( "github.com/gnolang/gno/tm2/pkg/store" ) -const iavlCacheSize = 1024 * 1024 // TODO increase and parameterize. - // return nil if package doesn't exist. type PackageGetter func(pkgPath string) (*PackageNode, *PackageValue) // inject natives into a new or loaded package (value and node) type PackageInjector func(store Store, pn *PackageNode) +// NativeStore is a function which can retrieve native bodies of native functions. +type NativeStore func(pkgName string, name Name) func(m *Machine) + type Store interface { // STABLE SetPackageGetter(PackageGetter) @@ -50,10 +51,12 @@ type Store interface { GetMemPackage(path string) *std.MemPackage GetMemFile(path string, name string) *std.MemFile IterMemPackage() <-chan *std.MemPackage - ClearObjectCache() // for each delivertx. - Fork() Store // for checktx, simulate, and queries. - SwapStores(baseStore, iavlStore store.Store) // for gas wrappers. - SetPackageInjector(PackageInjector) // for natives + ClearObjectCache() // for each delivertx. + Fork() Store // for checktx, simulate, and queries. + SwapStores(baseStore, iavlStore store.Store) // for gas wrappers. + SetPackageInjector(PackageInjector) // for natives + SetNativeStore(NativeStore) // for "new" natives XXX + GetNative(pkgPath string, name Name) func(m *Machine) // for "new" natives XXX SetLogStoreOps(enabled bool) SprintStoreOps() string LogSwitchRealm(rlmpath string) // to mark change of realm boundaries @@ -72,6 +75,7 @@ type defaultStore struct { baseStore store.Store // for objects, types, nodes iavlStore store.Store // for escaped object hashes pkgInjector PackageInjector // for injecting natives + nativeStore NativeStore // for injecting natives go2gnoMap map[string]string // go pkgpath.name -> gno pkgpath.name go2gnoStrict bool // if true, native->gno type conversion must be registered. @@ -603,6 +607,7 @@ func (ds *defaultStore) Fork() Store { baseStore: ds.baseStore, iavlStore: ds.iavlStore, pkgInjector: ds.pkgInjector, + nativeStore: ds.nativeStore, go2gnoMap: ds.go2gnoMap, go2gnoStrict: ds.go2gnoStrict, opslog: nil, // new ops log. @@ -622,11 +627,22 @@ func (ds *defaultStore) SetPackageInjector(inj PackageInjector) { ds.pkgInjector = inj } +func (ds *defaultStore) SetNativeStore(ns NativeStore) { + ds.nativeStore = ns +} + +func (ds *defaultStore) GetNative(pkgPath string, name Name) func(m *Machine) { + if ds.nativeStore != nil { + return ds.nativeStore(pkgPath, name) + } + return nil +} + func (ds *defaultStore) Flush() { // XXX } -//---------------------------------------- +// ---------------------------------------- // StoreOp type StoreOpType uint8 @@ -723,7 +739,7 @@ func (ds *defaultStore) Print() { } } -//---------------------------------------- +// ---------------------------------------- // backend keys func backendObjectKey(oid ObjectID) string { @@ -755,7 +771,7 @@ func backendPackagePathKey(path string) string { return fmt.Sprintf("pkg:" + path) } -//---------------------------------------- +// ---------------------------------------- // builtin types and packages func InitStoreCaches(store Store) { diff --git a/gnovm/pkg/gnolang/types.go b/gnovm/pkg/gnolang/types.go index c3e439e9427..34565b7a1b6 100644 --- a/gnovm/pkg/gnolang/types.go +++ b/gnovm/pkg/gnolang/types.go @@ -879,7 +879,6 @@ type InterfaceType struct { } // General empty interface. -var gEmptyInterfaceType *InterfaceType = &InterfaceType{} func (it *InterfaceType) IsEmptyInterface() bool { return len(it.Methods) == 0 @@ -1085,6 +1084,12 @@ type FuncType struct { bound *FuncType } +// true for predefined func types that are not filled in yet. +func (ft *FuncType) IsZero() bool { + // XXX be explicit. + return ft.Params == nil && ft.Results == nil && ft.typeid.IsZero() && ft.bound == nil +} + // if ft is a method, returns whether method takes a pointer receiver. func (ft *FuncType) HasPointerReceiver() bool { if debug { @@ -1446,10 +1451,61 @@ func (dt *DeclaredType) GetPkgPath() string { } func (dt *DeclaredType) DefineMethod(fv *FuncValue) { + if !dt.TryDefineMethod(fv) { + panic(fmt.Sprintf("redeclaration of method %s.%s", + dt.Name, fv.Name)) + } +} + +// TryDefineMethod attempts to define the method fv on type dt. +// It returns false if this does not succeeds, as a result of a re-declaration. +func (dt *DeclaredType) TryDefineMethod(fv *FuncValue) bool { + name := fv.Name + + // Handle redeclarations. + for i, tv := range dt.Methods { + ofv := tv.V.(*FuncValue) + if ofv.Name != name { + continue + } + + // Do not allow redeclaring (override) a method. + // In the future we may allow this, just like we + // allow package-level function overrides. + + // Special case: if the type and location are the same, + // ignore and do not redefine. + // This is due to PreprocessAllFilesAndSaveBlocknodes, + // and because the preprocessor fills some of the + // method's FuncValue. Since the method was already + // filled in prior to PreprocessAllFilesAndSaveBlocks, + // there is no need to re-set it. + // Keep this or move this check outside. + if fv.Type.TypeID() == ofv.Type.TypeID() && + fv.Source.GetLocation() == ofv.Source.GetLocation() { + return true + } + + // Special case: allow defining a native body. + if fv.Type.TypeID() == ofv.Type.TypeID() && + !ofv.IsNative() && fv.IsNative() { + dt.Methods[i] = TypedValue{ + T: fv.Type, // keep old type. + V: fv, + } + return true + } + + // Otherwise fail and return false. + return false + } + + // If not redeclaring, just append. dt.Methods = append(dt.Methods, TypedValue{ T: fv.Type, V: fv, }) + return true } func (dt *DeclaredType) GetPathForName(n Name) ValuePath { diff --git a/gnovm/pkg/gnolang/uverse.go b/gnovm/pkg/gnolang/uverse.go index 80f8a751e57..a0e9913eaad 100644 --- a/gnovm/pkg/gnolang/uverse.go +++ b/gnovm/pkg/gnolang/uverse.go @@ -147,32 +147,32 @@ func UverseNode() *PackageNode { // As a special case, if arg1 is a string type, first convert it into // a data slice type. if arg1.TV.T != nil && arg1.TV.T.Kind() == StringKind { - arg1s := arg1.TV.GetString() + arg1String := arg1.TV.GetString() // NOTE: this hack works because // arg1 PointerValue is not a pointer, // so the modification here is only local. - av := m.Alloc.NewDataArray(len(arg1s)) - copy(av.Data, []byte(arg1s)) + newArrayValue := m.Alloc.NewDataArray(len(arg1String)) + copy(newArrayValue.Data, []byte(arg1String)) arg1.TV = &TypedValue{ T: m.Alloc.NewType(&SliceType{ // TODO: reuse Elt: Uint8Type, Vrd: true, }), - V: m.Alloc.NewSlice(av, 0, len(arg1s), len(arg1s)), // TODO: pool? + V: m.Alloc.NewSlice(newArrayValue, 0, len(arg1String), len(arg1String)), // TODO: pool? } } - xt := arg0.TV.T - argt := arg1.TV.T - switch xv := arg0.TV.V.(type) { + arg0Type := arg0.TV.T + arg1Type := arg1.TV.T + switch arg0Value := arg0.TV.V.(type) { // ---------------------------------------------------------------- // append(nil, ???) case nil: - switch args := arg1.TV.V.(type) { + switch arg1Value := arg1.TV.V.(type) { // ------------------------------------------------------------ // append(nil, nil) case nil: // no change m.PushValue(TypedValue{ - T: xt, + T: arg0Type, V: nil, }) return @@ -180,42 +180,44 @@ func UverseNode() *PackageNode { // ------------------------------------------------------------ // append(nil, *SliceValue) case *SliceValue: - argsl := args.Length - argso := args.Offset - argsb := args.GetBase(m.Store) - if argsl == 0 { // no change + arg1Length := arg1Value.Length + arg1Offset := arg1Value.Offset + arg1Base := arg1Value.GetBase(m.Store) + arg1EndIndex := arg1Offset + arg1Length + + if arg1Length == 0 { // no change m.PushValue(TypedValue{ - T: xt, + T: arg0Type, V: nil, }) return - } else if xt.Elem().Kind() == Uint8Kind { + } else if arg0Type.Elem().Kind() == Uint8Kind { // append(nil, *SliceValue) new data bytes --- - data := make([]byte, argsl) - if argsb.Data == nil { + data := make([]byte, arg1Length) + if arg1Base.Data == nil { copyListToData( - data[:argsl], - argsb.List[argso:argso+argsl]) + data[:arg1Length], + arg1Base.List[arg1Offset:arg1EndIndex]) } else { copy( - data[:argsl], - argsb.Data[argso:argso+argsl]) + data[:arg1Length], + arg1Base.Data[arg1Offset:arg1EndIndex]) } m.PushValue(TypedValue{ - T: xt, + T: arg0Type, V: m.Alloc.NewSliceFromData(data), }) return } else { // append(nil, *SliceValue) new list --------- - list := make([]TypedValue, argsl) - if 0 < argsl { - copy( - list[:argsl], - argsb.List[argso:argso+argsl]) + list := make([]TypedValue, arg1Length) + if 0 < arg1Length { + for i := 0; i < arg1Length; i++ { + list[i] = arg1Base.List[arg1Offset+i].unrefCopy(m.Alloc, m.Store) + } } m.PushValue(TypedValue{ - T: xt, + T: arg0Type, V: m.Alloc.NewSliceFromList(list), }) return @@ -224,36 +226,36 @@ func UverseNode() *PackageNode { // ------------------------------------------------------------ // append(nil, *NativeValue) case *NativeValue: - argsrv := args.Value - argsl := argsrv.Len() - if argsl == 0 { // no change + arg1NativeValue := arg1Value.Value + arg1NativeValueLength := arg1NativeValue.Len() + if arg1NativeValueLength == 0 { // no change m.PushValue(TypedValue{ - T: xt, + T: arg0Type, V: nil, }) return - } else if xt.Elem().Kind() == Uint8Kind { + } else if arg0Type.Elem().Kind() == Uint8Kind { // append(nil, *NativeValue) new data bytes -- - data := make([]byte, argsl) + data := make([]byte, arg1NativeValueLength) copyNativeToData( - data[:argsl], - argsrv, argsl) + data[:arg1NativeValueLength], + arg1NativeValue, arg1NativeValueLength) m.PushValue(TypedValue{ - T: xt, + T: arg0Type, V: m.Alloc.NewSliceFromData(data), }) return } else { // append(nil, *NativeValue) new list -------- - list := make([]TypedValue, argsl) - if 0 < argsl { + list := make([]TypedValue, arg1NativeValueLength) + if 0 < arg1NativeValueLength { copyNativeToList( m.Alloc, - list[:argsl], - argsrv, argsl) + list[:arg1NativeValueLength], + arg1NativeValue, arg1NativeValueLength) } m.PushValue(TypedValue{ - T: xt, + T: arg0Type, V: m.Alloc.NewSliceFromList(list), }) return @@ -267,131 +269,137 @@ func UverseNode() *PackageNode { // ---------------------------------------------------------------- // append(*SliceValue, ???) case *SliceValue: - xvl := xv.Length - xvo := xv.Offset - xvc := xv.Maxcap - xvb := xv.GetBase(m.Store) - switch args := arg1.TV.V.(type) { + arg0Length := arg0Value.Length + arg0Offset := arg0Value.Offset + arg0Capacity := arg0Value.Maxcap + arg0Base := arg0Value.GetBase(m.Store) + switch arg1Value := arg1.TV.V.(type) { // ------------------------------------------------------------ // append(*SliceValue, nil) case nil: // no change m.PushValue(TypedValue{ - T: xt, - V: xv, + T: arg0Type, + V: arg0Value, }) return // ------------------------------------------------------------ // append(*SliceValue, *SliceValue) case *SliceValue: - argsl := args.Length - argso := args.Offset - argsb := args.GetBase(m.Store) - if xvl+argsl <= xvc { + arg1Length := arg1Value.Length + arg1Offset := arg1Value.Offset + arg1Base := arg1Value.GetBase(m.Store) + if arg0Length+arg1Length <= arg0Capacity { // append(*SliceValue, *SliceValue) w/i capacity ----- - if 0 < argsl { // implies 0 < xvc - if xvb.Data == nil { + if 0 < arg1Length { // implies 0 < xvc + if arg0Base.Data == nil { // append(*SliceValue.List, *SliceValue) --------- - list := xvb.List - if argsb.Data == nil { - copy( - list[xvo+xvl:xvo+xvl+argsl], - argsb.List[argso:argso+argsl]) + list := arg0Base.List + if arg1Base.Data == nil { + for i := 0; i < arg1Length; i++ { + oldElem := list[arg0Offset+arg0Length+i] + // unrefCopy will resolve references and copy their values + // to copy by value rather than by reference. + newElem := arg1Base.List[arg1Offset+i].unrefCopy(m.Alloc, m.Store) + list[arg0Offset+arg0Length+i] = newElem + + m.Realm.DidUpdate( + arg0Base, + oldElem.GetFirstObject(m.Store), + newElem.GetFirstObject(m.Store), + ) + } } else { copyDataToList( - list[xvo+xvl:xvo+xvl+argsl], - argsb.Data[argso:argso+argsl], - xt.Elem()) + list[arg0Offset+arg0Length:arg0Offset+arg0Length+arg1Length], + arg1Base.Data[arg1Offset:arg1Offset+arg1Length], + arg0Type.Elem()) + m.Realm.DidUpdate(arg1Base, nil, nil) } } else { // append(*SliceValue.Data, *SliceValue) --------- - data := xvb.Data - if argsb.Data == nil { + data := arg0Base.Data + if arg1Base.Data == nil { copyListToData( - data[xvo+xvl:xvo+xvl+argsl], - argsb.List[argso:argso+argsl]) + data[arg0Offset+arg0Length:arg0Offset+arg0Length+arg1Length], + arg1Base.List[arg1Offset:arg1Offset+arg1Length]) + m.Realm.DidUpdate(arg0Base, nil, nil) } else { copy( - data[xvo+xvl:xvo+xvl+argsl], - argsb.Data[argso:argso+argsl]) + data[arg0Offset+arg0Length:arg0Offset+arg0Length+arg1Length], + arg1Base.Data[arg1Offset:arg1Offset+arg1Length]) } } m.PushValue(TypedValue{ - T: xt, - V: m.Alloc.NewSlice(xvb, xvo, xvl+argsl, xvc), + T: arg0Type, + V: m.Alloc.NewSlice(arg0Base, arg0Offset, arg0Length+arg1Length, arg0Capacity), }) return } else { // no change m.PushValue(TypedValue{ - T: xt, - V: xv, + T: arg0Type, + V: arg0Value, }) return } - } else if xt.Elem().Kind() == Uint8Kind { + } else if arg0Type.Elem().Kind() == Uint8Kind { // append(*SliceValue, *SliceValue) new data bytes --- - data := make([]byte, xvl+argsl) - if 0 < xvl { - if xvb.Data == nil { + data := make([]byte, arg0Length+arg1Length) + if 0 < arg0Length { + if arg0Base.Data == nil { copyListToData( - data[:xvl], - xvb.List[xvo:xvo+xvl]) + data[:arg0Length], + arg0Base.List[arg0Offset:arg0Offset+arg0Length]) } else { copy( - data[:xvl], - xvb.Data[xvo:xvo+xvl]) + data[:arg0Length], + arg0Base.Data[arg0Offset:arg0Offset+arg0Length]) } } - if 0 < argsl { - if argsb.Data == nil { + if 0 < arg1Length { + if arg1Base.Data == nil { copyListToData( - data[xvl:xvl+argsl], - argsb.List[argso:argso+argsl]) + data[arg0Length:arg0Length+arg1Length], + arg1Base.List[arg1Offset:arg1Offset+arg1Length]) } else { copy( - data[xvl:xvl+argsl], - argsb.Data[argso:argso+argsl]) + data[arg0Length:arg0Length+arg1Length], + arg1Base.Data[arg1Offset:arg1Offset+arg1Length]) } } m.PushValue(TypedValue{ - T: xt, + T: arg0Type, V: m.Alloc.NewSliceFromData(data), }) return } else { // append(*SliceValue, *SliceValue) new list --------- - list := make([]TypedValue, xvl+argsl) - if 0 < xvl { - if xvb.Data == nil { - copy( - list[:xvl], - xvb.List[xvo:xvo+xvl]) + list := make([]TypedValue, arg0Length+arg1Length) + if 0 < arg0Length { + if arg0Base.Data == nil { + for i := 0; i < arg0Length; i++ { + list[i] = arg0Base.List[arg0Offset+i].unrefCopy(m.Alloc, m.Store) + } } else { panic("should not happen") - /* - copyDataToList( - list[:xvl], - xvb.Data[xvo:xvo+xvl], - xt.Elem(), - ) - */ } } - if 0 < argsl { - if argsb.Data == nil { - copy( - list[xvl:xvl+argsl], - argsb.List[argso:argso+argsl]) + + if 0 < arg1Length { + if arg1Base.Data == nil { + for i := 0; i < arg1Length; i++ { + list[arg0Length+i] = arg1Base.List[arg1Offset+i].unrefCopy(m.Alloc, m.Store) + } } else { copyDataToList( - list[xvl:xvl+argsl], - argsb.Data[argso:argso+argsl], - argt.Elem(), + list[arg0Length:arg0Length+arg1Length], + arg1Base.Data[arg1Offset:arg1Offset+arg1Length], + arg1Type.Elem(), ) } } m.PushValue(TypedValue{ - T: xt, + T: arg0Type, V: m.Alloc.NewSliceFromList(list), }) return @@ -400,77 +408,78 @@ func UverseNode() *PackageNode { // ------------------------------------------------------------ // append(*SliceValue, *NativeValue) case *NativeValue: - argsrv := args.Value - argsl := argsrv.Len() - if xvl+argsl <= xvc { + arg1NativeValue := arg1Value.Value + arg1NativeValueLength := arg1NativeValue.Len() + if arg0Length+arg1NativeValueLength <= arg0Capacity { // append(*SliceValue, *NativeValue) w/i capacity ---- - if 0 < argsl { // implies 0 < xvc - if xvb.Data == nil { + if 0 < arg1NativeValueLength { // implies 0 < xvc + if arg0Base.Data == nil { // append(*SliceValue.List, *NativeValue) -------- - list := xvb.List + list := arg0Base.List copyNativeToList( m.Alloc, - list[xvo:xvo+argsl], - argsrv, argsl) + list[arg0Offset:arg0Offset+arg1NativeValueLength], + arg1NativeValue, arg1NativeValueLength) } else { // append(*SliceValue.Data, *NativeValue) -------- - data := xvb.Data + data := arg0Base.Data copyNativeToData( - data[xvo:xvo+argsl], - argsrv, argsl) + data[arg0Offset:arg0Offset+arg1NativeValueLength], + arg1NativeValue, arg1NativeValueLength) } m.PushValue(TypedValue{ - T: xt, - V: m.Alloc.NewSlice(xvb, xvo, xvl+argsl, xvc), + T: arg0Type, + V: m.Alloc.NewSlice(arg0Base, arg0Offset, arg0Length+arg1NativeValueLength, arg0Capacity), }) return } else { // no change m.PushValue(TypedValue{ - T: xt, - V: xv, + T: arg0Type, + V: arg0Value, }) return } - } else if xt.Elem().Kind() == Uint8Kind { + } else if arg0Type.Elem().Kind() == Uint8Kind { // append(*SliceValue, *NativeValue) new data bytes -- - data := make([]byte, xvl+argsl) - if 0 < xvl { - if xvb.Data == nil { + data := make([]byte, arg0Length+arg1NativeValueLength) + if 0 < arg0Length { + if arg0Base.Data == nil { copyListToData( - data[:xvl], - xvb.List[xvo:xvo+xvl]) + data[:arg0Length], + arg0Base.List[arg0Offset:arg0Offset+arg0Length]) } else { copy( - data[:xvl], - xvb.Data[xvo:xvo+xvl]) + data[:arg0Length], + arg0Base.Data[arg0Offset:arg0Offset+arg0Length]) } } - if 0 < argsl { + if 0 < arg1NativeValueLength { copyNativeToData( - data[xvl:xvl+argsl], - argsrv, argsl) + data[arg0Length:arg0Length+arg1NativeValueLength], + arg1NativeValue, arg1NativeValueLength) } m.PushValue(TypedValue{ - T: xt, + T: arg0Type, V: m.Alloc.NewSliceFromData(data), }) return } else { // append(*SliceValue, *NativeValue) new list -------- - list := make([]TypedValue, xvl+argsl) - if 0 < xvl { - copy( - list[:xvl], - xvb.List[xvo:xvo+xvl]) + listLen := arg0Length + arg1NativeValueLength + list := make([]TypedValue, listLen) + if 0 < arg0Length { + for i := 0; i < listLen; i++ { + list[i] = arg0Base.List[arg0Offset+i].unrefCopy(m.Alloc, m.Store) + } } - if 0 < argsl { + if 0 < arg1NativeValueLength { copyNativeToList( m.Alloc, - list[xvl:xvl+argsl], - argsrv, argsl) + list[arg0Length:listLen], + arg1NativeValue, arg1NativeValueLength) } m.PushValue(TypedValue{ - T: xt, + T: arg0Type, V: m.Alloc.NewSliceFromList(list), }) return @@ -484,51 +493,51 @@ func UverseNode() *PackageNode { // ---------------------------------------------------------------- // append(*NativeValue, ???) case *NativeValue: - sv := xv.Value - switch args := arg1.TV.V.(type) { + arg0NativeValue := arg0Value.Value + switch arg1Value := arg1.TV.V.(type) { // ------------------------------------------------------------ // append(*NativeValue, nil) case nil: // no change m.PushValue(TypedValue{ - T: xt, - V: xv, + T: arg0Type, + V: arg0Value, }) return // ------------------------------------------------------------ // append(*NativeValue, *SliceValue) case *SliceValue: - st := sv.Type() - argso := args.Offset - argsl := args.Length - argsb := args.GetBase(m.Store) - if 0 < argsl { - argsrv := reflect.MakeSlice(st, argsl, argsl) - if argsb.Data == nil { - for i := 0; i < argsl; i++ { - etv := &(argsb.List[argso+i]) + arg0NativeValueType := arg0NativeValue.Type() + arg1Offset := arg1Value.Offset + arg1Length := arg1Value.Length + arg1Base := arg1Value.GetBase(m.Store) + if 0 < arg1Length { + newNativeArg1Slice := reflect.MakeSlice(arg0NativeValueType, arg1Length, arg1Length) + if arg1Base.Data == nil { + for i := 0; i < arg1Length; i++ { + etv := &(arg1Base.List[arg1Offset+i]) if etv.IsUndefined() { continue } erv := gno2GoValue(etv, reflect.Value{}) - argsrv.Index(i).Set(erv) + newNativeArg1Slice.Index(i).Set(erv) } } else { - for i := 0; i < argsl; i++ { - erv := argsrv.Index(i) - erv.SetUint(uint64(argsb.Data[argso+i])) + for i := 0; i < arg1Length; i++ { + erv := newNativeArg1Slice.Index(i) + erv.SetUint(uint64(arg1Base.Data[arg1Offset+i])) } } - resrv := reflect.AppendSlice(sv, argsrv) + modifiedNativeSlice := reflect.AppendSlice(arg0NativeValue, newNativeArg1Slice) m.PushValue(TypedValue{ - T: xt, - V: m.Alloc.NewNative(resrv), + T: arg0Type, + V: m.Alloc.NewNative(modifiedNativeSlice), }) return } else { // no change m.PushValue(TypedValue{ - T: xt, - V: xv, + T: arg0Type, + V: arg0Value, }) return } @@ -536,31 +545,31 @@ func UverseNode() *PackageNode { // ------------------------------------------------------------ // append(*NativeValue, *NativeValue) case *NativeValue: - argsrv := args.Value - resrv := reflect.AppendSlice(sv, argsrv) + arg1ReflectValue := arg1Value.Value + modifiedNativeSlice := reflect.AppendSlice(arg0NativeValue, arg1ReflectValue) m.PushValue(TypedValue{ - T: xt, - V: m.Alloc.NewNative(resrv), + T: arg0Type, + V: m.Alloc.NewNative(modifiedNativeSlice), }) return // ------------------------------------------------------------ // append(*NativeValue, StringValue) case StringValue: - if xt.Elem().Kind() == Uint8Kind { + if arg0Type.Elem().Kind() == Uint8Kind { // TODO this might be faster if reflect supports // appending this way without first converting to a slice. - argrv := reflect.ValueOf([]byte(arg1.TV.V.(StringValue))) - resrv := reflect.AppendSlice(sv, argrv) + arg1ReflectValue := reflect.ValueOf([]byte(arg1.TV.GetString())) + modifiedNativeSlice := reflect.AppendSlice(arg0NativeValue, arg1ReflectValue) m.PushValue(TypedValue{ - T: xt, - V: m.Alloc.NewNative(resrv), + T: arg0Type, + V: m.Alloc.NewNative(modifiedNativeSlice), }) return } else { panic(fmt.Sprintf( "cannot append %s to %s", - arg1.TV.T.String(), xt.String())) + arg1.TV.T.String(), arg0Type.String())) } // ------------------------------------------------------------ @@ -568,7 +577,7 @@ func UverseNode() *PackageNode { default: panic(fmt.Sprintf( "cannot append %s to %s", - arg1.TV.T.String(), xt.String())) + arg1.TV.T.String(), arg0Type.String())) } // ---------------------------------------------------------------- diff --git a/gnovm/pkg/gnolang/uverse_test.go b/gnovm/pkg/gnolang/uverse_test.go new file mode 100644 index 00000000000..7280d131ec5 --- /dev/null +++ b/gnovm/pkg/gnolang/uverse_test.go @@ -0,0 +1,160 @@ +package gnolang + +import ( + "testing" +) + +type printlnTestCases struct { + name string + code string + expected string +} + +func TestIssue1337PrintNilSliceAsUndefined(t *testing.T) { + test := []printlnTestCases{ + { + name: "print empty slice", + code: `package test + func main() { + emptySlice1 := make([]int, 0) + emptySlice2 := []int{} + + println(emptySlice1) + println(emptySlice2) + }`, + expected: "slice[]\nslice[]\n", + }, + { + name: "nil slice", + code: `package test + func main() { + println(nil) + }`, + expected: "undefined\n", + }, + { + name: "print empty string slice", + code: `package test + func main() { + var a []string + println(a) + }`, + expected: "nil []string\n", + }, + { + name: "print non-empty slice", + code: `package test + func main() { + a := []string{"a", "b"} + println(a) + }`, + expected: "slice[(\"a\" string),(\"b\" string)]\n", + }, + { + name: "print empty map", + code: `package test + func main() { + var a map[string]string + println(a) + }`, + expected: "nil map[string]string\n", + }, + { + name: "print non-empty map", + code: `package test + func main() { + a := map[string]string{"a": "b"} + println(a) + }`, + expected: "map{(\"a\" string):(\"b\" string)}\n", + }, + { + name: "print nil struct", + code: `package test + func main() { + var a struct{} + println(a) + }`, + expected: "struct{}\n", + }, + { + name: "print function", + code: `package test + func foo(a, b int) int { + return a + b + } + func main() { + println(foo(1, 3)) + }`, + expected: "4\n", + }, + { + name: "print composite slice", + code: `package test + func main() { + a, b, c, d := 1, 2, 3, 4 + x := []int{ + a: b, + c: d, + } + println(x) + }`, + expected: "slice[(0 int),(2 int),(0 int),(4 int)]\n", + }, + { + name: "simple recover case", + code: `package test + + func main() { + defer func() { println("recover", recover()) }() + println("simple panic") + }`, + expected: "simple panic\nrecover undefined\n", + }, + { + name: "nested recover", + code: `package test + + func main() { + defer func() { println("outer recover", recover()) }() + defer func() { println("nested panic") }() + println("simple panic") + }`, + expected: "simple panic\nnested panic\nouter recover undefined\n", + }, + { + name: "print non-nil function", + code: `package test + func f() int { + return 1 + } + + func main() { + g := f + println(g) + }`, + expected: "f\n", + }, + { + name: "print primitive types", + code: `package test + func main() { + println(1) + println(1.1) + println(true) + println("hello") + }`, + expected: "1\n1.1\ntrue\nhello\n", + }, + } + + for _, tc := range test { + t.Run(tc.name, func(t *testing.T) { + m := NewMachine("test", nil) + n := MustParseFile("main.go", tc.code) + m.RunFiles(n) + m.RunMain() + assertOutput(t, tc.code, tc.expected) + }) + } +} diff --git a/gnovm/pkg/gnolang/values.go b/gnovm/pkg/gnolang/values.go index 3de74ac0130..53d482613a1 100644 --- a/gnovm/pkg/gnolang/values.go +++ b/gnovm/pkg/gnolang/values.go @@ -10,7 +10,7 @@ import ( "strings" "unsafe" - "github.com/cockroachdb/apd" + "github.com/cockroachdb/apd/v3" "github.com/gnolang/gno/tm2/pkg/crypto" ) @@ -43,7 +43,8 @@ func (*Block) assertValue() {} func (RefValue) assertValue() {} const ( - nilStr = "nil" + nilStr = "nil" + undefinedStr = "undefined" ) var ( @@ -500,7 +501,15 @@ func (sv *StructValue) Copy(alloc *Allocator) *StructValue { } */ fields := alloc.NewStructFields(len(sv.Fields)) - copy(fields, sv.Fields) + + // Each field needs to be copied individually to ensure that + // value fields are copied as such, even though they may be represented + // as pointers. A good example of this would be a struct that has + // a field that is an array. The value array is represented as a pointer. + for i, field := range sv.Fields { + fields[i] = field.Copy(alloc) + } + return alloc.NewStruct(fields) } @@ -518,18 +527,32 @@ func (sv *StructValue) Copy(alloc *Allocator) *StructValue { // makes construction TypedValue{T:*FuncType{},V:*FuncValue{}} // faster. type FuncValue struct { - Type Type // includes unbound receiver(s) - IsMethod bool // is an (unbound) method - Source BlockNode // for block mem allocation - Name Name // name of function/method - Closure Value // *Block or RefValue to closure (may be nil for file blocks; lazy) - FileName Name // file name where declared - PkgPath string + Type Type // includes unbound receiver(s) + IsMethod bool // is an (unbound) method + Source BlockNode // for block mem allocation + Name Name // name of function/method + Closure Value // *Block or RefValue to closure (may be nil for file blocks; lazy) + FileName Name // file name where declared + PkgPath string + NativePkg string // for native bindings through NativeStore + NativeName Name // not redundant with Name; this cannot be changed in userspace body []Stmt // function body nativeBody func(*Machine) // alternative to Body } +func (fv *FuncValue) IsNative() bool { + if fv.NativePkg == "" && fv.NativeName == "" { + return false + } + if fv.NativePkg == "" || fv.NativeName == "" { + panic(fmt.Sprintf("function (%q).%s has invalid native pkg/name ((%q).%s)", + fv.Source.GetLocation().PkgPath, fv.Name, + fv.NativePkg, fv.NativeName)) + } + return true +} + func (fv *FuncValue) Copy(alloc *Allocator) *FuncValue { alloc.AllocateFunc() return &FuncValue{ @@ -540,6 +563,8 @@ func (fv *FuncValue) Copy(alloc *Allocator) *FuncValue { Closure: fv.Closure, FileName: fv.FileName, PkgPath: fv.PkgPath, + NativePkg: fv.NativePkg, + NativeName: fv.NativeName, body: fv.body, nativeBody: fv.nativeBody, } @@ -1002,6 +1027,28 @@ func (tv TypedValue) Copy(alloc *Allocator) (cp TypedValue) { return } +// unrefCopy makes a copy of the underlying value in the case of reference values. +// It copies other values as expected using the normal Copy method. +func (tv TypedValue) unrefCopy(alloc *Allocator, store Store) (cp TypedValue) { + switch tv.V.(type) { + case RefValue: + cp.T = tv.T + refObject := tv.GetFirstObject(store) + switch refObjectValue := refObject.(type) { + case *ArrayValue: + cp.V = refObjectValue.Copy(alloc) + case *StructValue: + cp.V = refObjectValue.Copy(alloc) + default: + cp = tv + } + default: + cp = tv.Copy(alloc) + } + + return +} + // Returns encoded bytes for primitive values. // These bytes are used for both value hashes as well // as hash key bytes. @@ -2312,12 +2359,8 @@ func (b *Block) GetPointerTo(store Store, path ValuePath) PointerValue { // the generation for uverse is 0. If path.Depth is // 0, it implies that b == uverse, and the condition // would fail as if it were 1. - i := uint8(1) -LOOP: - if i < path.Depth { + for i := uint8(1); i < path.Depth; i++ { b = b.GetParent(store) - i++ - goto LOOP } return b.GetPointerToInt(store, int(path.Index)) } diff --git a/gnovm/pkg/gnolang/values_conversions.go b/gnovm/pkg/gnolang/values_conversions.go index 9fc2ce4a567..66d8bcbf233 100644 --- a/gnovm/pkg/gnolang/values_conversions.go +++ b/gnovm/pkg/gnolang/values_conversions.go @@ -7,7 +7,7 @@ import ( "reflect" "strconv" - "github.com/cockroachdb/apd" + "github.com/cockroachdb/apd/v3" ) // t cannot be nil or untyped or DataByteType. @@ -77,6 +77,10 @@ GNO_CASE: } // special case for undefined/nil source if tv.IsUndefined() { + switch t.Kind() { + case BoolKind, StringKind, IntKind, Int8Kind, Int16Kind, Int32Kind, Int64Kind, UintKind, Uint8Kind, Uint16Kind, Uint32Kind, Uint64Kind, Float32Kind, Float64Kind, BigintKind, BigdecKind: + panic(fmt.Sprintf("cannot convert %v to %v", tv, t)) + } tv.T = t return } @@ -1113,7 +1117,7 @@ func ConvertUntypedBigintTo(dst *TypedValue, bv BigintValue, t Type) { return // done case BigdecKind: dst.T = t - dst.V = BigdecValue{V: apd.NewWithBigInt(bi, 0)} + dst.V = BigdecValue{V: apd.NewWithBigInt(new(apd.BigInt).SetMathBigInt(bi), 0)} return // done default: panic(fmt.Sprintf( @@ -1240,12 +1244,14 @@ func ConvertUntypedBigdecTo(dst *TypedValue, bv BigdecValue, t Type) { case Float32Kind: dst.T = t dst.V = nil - f64, _ := bd.Float64() + f64, err := bd.Float64() + if err != nil { + panic(fmt.Errorf("cannot convert untyped bigdec to float64: %w", err)) + } + bf := big.NewFloat(f64) - f32, acc := bf.Float32() - if f32 == 0 && (acc == big.Below || acc == big.Above) { - panic("cannot convert untyped bigdec to float32 -- too close to zero") - } else if math.IsInf(float64(f32), 0) { + f32, _ := bf.Float32() + if math.IsInf(float64(f32), 0) { panic("cannot convert untyped bigdec to float32 -- too close to +-Inf") } dst.SetFloat32(f32) @@ -1253,10 +1259,11 @@ func ConvertUntypedBigdecTo(dst *TypedValue, bv BigdecValue, t Type) { case Float64Kind: dst.T = t dst.V = nil - f64, _ := bd.Float64() - if f64 == 0 && !bd.IsZero() { - panic("cannot convert untyped bigdec to float64 -- too close to zero") - } else if math.IsInf(f64, 0) { + f64, err := bd.Float64() + if err != nil { + panic(fmt.Errorf("cannot convert untyped bigdec to float64: %w", err)) + } + if math.IsInf(f64, 0) { panic("cannot convert untyped bigdec to float64 -- too close to +-Inf") } dst.SetFloat64(f64) diff --git a/gnovm/pkg/gnolang/values_conversions_test.go b/gnovm/pkg/gnolang/values_conversions_test.go new file mode 100644 index 00000000000..5436347733f --- /dev/null +++ b/gnovm/pkg/gnolang/values_conversions_test.go @@ -0,0 +1,27 @@ +package gnolang + +import ( + "math" + "testing" + + "github.com/cockroachdb/apd/v3" + "github.com/stretchr/testify/require" +) + +func TestConvertUntypedBigdecToFloat(t *testing.T) { + t.Parallel() + + dst := &TypedValue{} + + dec, err := apd.New(-math.MaxInt64, -4).SetFloat64(math.SmallestNonzeroFloat64 / 2) + require.NoError(t, err) + bd := BigdecValue{ + V: dec, + } + + typ := Float64Type + + ConvertUntypedBigdecTo(dst, bd, typ) + + require.Equal(t, float64(0), dst.GetFloat64()) +} diff --git a/gnovm/pkg/gnolang/values_string.go b/gnovm/pkg/gnolang/values_string.go index f9a0128d7f9..34187e32879 100644 --- a/gnovm/pkg/gnolang/values_string.go +++ b/gnovm/pkg/gnolang/values_string.go @@ -7,6 +7,47 @@ import ( "strings" ) +type protectedStringer interface { + ProtectedString(*seenValues) string +} + +// This indicates the maximum ancticipated depth of the stack when printing a Value type. +const defaultSeenValuesSize = 32 + +type seenValues struct { + values []Value +} + +func (sv *seenValues) Put(v Value) { + sv.values = append(sv.values, v) +} + +func (sv *seenValues) Contains(v Value) bool { + for _, vv := range sv.values { + if vv == v { + return true + } + } + + return false +} + +// Pop should be called by using a defer after each Put. +// Consider why this is necessary: +// - we are printing an array of structs +// - each invocation of struct.ProtectedString adds the value to the seenValues +// - without calling Pop before exiting struct.ProtectedString, the next call to +// struct.ProtectedString in the array.ProtectedString loop will not result in the value +// being printed if the value has already been print +// - this is NOT recursion and SHOULD be printed +func (sv *seenValues) Pop() { + sv.values = sv.values[:len(sv.values)-1] +} + +func newSeenValues() *seenValues { + return &seenValues{values: make([]Value, 0, defaultSeenValuesSize)} +} + func (v StringValue) String() string { return strconv.Quote(string(v)) } @@ -24,10 +65,21 @@ func (dbv DataByteValue) String() string { } func (av *ArrayValue) String() string { + return av.ProtectedString(newSeenValues()) +} + +func (av *ArrayValue) ProtectedString(seen *seenValues) string { + if seen.Contains(av) { + return fmt.Sprintf("%p", av) + } + + seen.Put(av) + defer seen.Pop() + ss := make([]string, len(av.List)) if av.Data == nil { for i, e := range av.List { - ss[i] = e.String() + ss[i] = e.ProtectedString(seen) } // NOTE: we may want to unify the representation, // but for now tests expect this to be different. @@ -41,17 +93,30 @@ func (av *ArrayValue) String() string { } func (sv *SliceValue) String() string { + return sv.ProtectedString(newSeenValues()) +} + +func (sv *SliceValue) ProtectedString(seen *seenValues) string { if sv.Base == nil { return "nil-slice" } + + if seen.Contains(sv) { + return fmt.Sprintf("%p", sv) + } + if ref, ok := sv.Base.(RefValue); ok { return fmt.Sprintf("slice[%v]", ref) } + + seen.Put(sv) + defer seen.Pop() + vbase := sv.Base.(*ArrayValue) if vbase.Data == nil { ss := make([]string, sv.Length) for i, e := range vbase.List[sv.Offset : sv.Offset+sv.Length] { - ss[i] = e.String() + ss[i] = e.ProtectedString(seen) } return "slice[" + strings.Join(ss, ",") + "]" } @@ -62,16 +127,40 @@ func (sv *SliceValue) String() string { } func (pv PointerValue) String() string { - // NOTE: cannot do below, due to recursion problems. - // TODO: create a different String2(...) function. - // return fmt.Sprintf("&%s", v.TypedValue.String()) - return fmt.Sprintf("&%p.(*%s)", pv.TV, pv.TV.T.String()) + return pv.ProtectedString(newSeenValues()) +} + +func (pv PointerValue) ProtectedString(seen *seenValues) string { + if seen.Contains(pv) { + return fmt.Sprintf("%p", &pv) + } + + seen.Put(pv) + defer seen.Pop() + + // Handle nil TV's, avoiding a nil pointer deref below. + if pv.TV == nil { + return "&" + } + + return fmt.Sprintf("&%s", pv.TV.ProtectedString(seen)) } func (sv *StructValue) String() string { + return sv.ProtectedString(newSeenValues()) +} + +func (sv *StructValue) ProtectedString(seen *seenValues) string { + if seen.Contains(sv) { + return fmt.Sprintf("%p", sv) + } + + seen.Put(sv) + defer seen.Pop() + ss := make([]string, len(sv.Fields)) for i, f := range sv.Fields { - ss[i] = f.String() + ss[i] = f.ProtectedString(seen) } return "struct{" + strings.Join(ss, ",") + "}" } @@ -104,9 +193,21 @@ func (v *BoundMethodValue) String() string { } func (mv *MapValue) String() string { + return mv.ProtectedString(newSeenValues()) +} + +func (mv *MapValue) ProtectedString(seen *seenValues) string { if mv.List == nil { return "zero-map" } + + if seen.Contains(mv) { + return fmt.Sprintf("%p", mv) + } + + seen.Put(mv) + defer seen.Pop() + ss := make([]string, 0, mv.GetLength()) next := mv.List.Head for next != nil { @@ -164,8 +265,9 @@ func (v RefValue) String() string { func (tv *TypedValue) Sprint(m *Machine) string { // if undefined, just "undefined". if tv == nil || tv.T == nil { - return "undefined" + return undefinedStr } + // if implements .String(), return it. if IsImplementedBy(gStringerType, tv.T) { res := m.Eval(Call(Sel(&ConstExpr{TypedValue: *tv}, "String"))) @@ -176,10 +278,28 @@ func (tv *TypedValue) Sprint(m *Machine) string { res := m.Eval(Call(Sel(&ConstExpr{TypedValue: *tv}, "Error"))) return res[0].GetString() } + + return tv.ProtectedSprint(newSeenValues(), true) +} + +func (tv *TypedValue) ProtectedSprint(seen *seenValues, considerDeclaredType bool) string { + if seen.Contains(tv.V) { + return fmt.Sprintf("%p", tv) + } + // print declared type - if _, ok := tv.T.(*DeclaredType); ok { - return tv.String() + if _, ok := tv.T.(*DeclaredType); ok && considerDeclaredType { + return tv.ProtectedString(seen) } + + // This is a special case that became necessary after adding `ProtectedString()` methods to + // reliably prevent recursive print loops. + if tv.V != nil { + if v, ok := tv.V.(RefValue); ok { + return v.String() + } + } + // otherwise, default behavior. switch bt := baseOf(tv.T).(type) { case PrimitiveType: @@ -223,23 +343,13 @@ func (tv *TypedValue) Sprint(m *Machine) string { if tv.V == nil { return "invalid-pointer" } - return tv.V.(PointerValue).String() - case *ArrayType: - return tv.V.(*ArrayValue).String() - case *SliceType: - return tv.V.(*SliceValue).String() - case *StructType: - return tv.V.(*StructValue).String() - case *MapType: - return tv.V.(*MapValue).String() + return tv.V.(PointerValue).ProtectedString(seen) case *FuncType: switch fv := tv.V.(type) { case nil: ft := tv.T.String() - return "nil " + ft - case *FuncValue: - return fv.String() - case *BoundMethodValue: + return nilStr + " " + ft + case *FuncValue, *BoundMethodValue: return fv.String() default: panic(fmt.Sprintf( @@ -253,19 +363,28 @@ func (tv *TypedValue) Sprint(m *Machine) string { } } return nilStr - case *TypeType: - return tv.V.(TypeValue).String() case *DeclaredType: panic("should not happen") case *PackageType: return tv.V.(*PackageValue).String() case *ChanType: panic("not yet implemented") - // return tv.V.(*ChanValue).String() - case *NativeType: - return fmt.Sprintf("%v", - tv.V.(*NativeValue).Value.Interface()) + case *TypeType: + return tv.V.(TypeValue).String() default: + // The remaining types may have a nil value. + if tv.V == nil { + return nilStr + " " + tv.T.String() + } + + // *ArrayType, *SliceType, *StructType, *MapType + if ps, ok := tv.V.(protectedStringer); ok { + return ps.ProtectedString(seen) + } else if s, ok := tv.V.(fmt.Stringer); ok { + // *NativeType + return s.String() + } + if debug { panic(fmt.Sprintf( "unexpected type %s", @@ -281,6 +400,10 @@ func (tv *TypedValue) Sprint(m *Machine) string { // For gno debugging/testing. func (tv TypedValue) String() string { + return tv.ProtectedString(newSeenValues()) +} + +func (tv TypedValue) ProtectedString(seen *seenValues) string { if tv.IsUndefined() { return "(undefined)" } @@ -317,12 +440,17 @@ func (tv TypedValue) String() string { vs = fmt.Sprintf("%v", tv.GetFloat32()) case Float64Type: vs = fmt.Sprintf("%v", tv.GetFloat64()) + // Complex types that require recusion protection. default: vs = nilStr } } else { - vs = fmt.Sprintf("%v", tv.V) + vs = tv.ProtectedSprint(seen, false) + if base := baseOf(tv.T); base == StringType || base == UntypedStringType { + vs = strconv.Quote(vs) + } } + ts := tv.T.String() return fmt.Sprintf("(%s %s)", vs, ts) // TODO improve } diff --git a/gnovm/pkg/gnomod/file.go b/gnovm/pkg/gnomod/file.go index 71d98b2d14b..25189ebc71d 100644 --- a/gnovm/pkg/gnomod/file.go +++ b/gnovm/pkg/gnomod/file.go @@ -1,3 +1,12 @@ +// Some part of file is copied and modified from +// golang.org/x/mod/modfile/read.go +// +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in here[1]. +// +// [1]: https://cs.opensource.google/go/x/mod/+/master:LICENSE + package gnomod import ( @@ -24,6 +33,100 @@ type File struct { Syntax *modfile.FileSyntax } +// AddRequire sets the first require line for path to version vers, +// preserving any existing comments for that line and removing all +// other lines for path. +// +// If no line currently exists for path, AddRequire adds a new line +// at the end of the last require block. +func (f *File) AddRequire(path, vers string) error { + need := true + for _, r := range f.Require { + if r.Mod.Path == path { + if need { + r.Mod.Version = vers + updateLine(r.Syntax, "require", modfile.AutoQuote(path), vers) + need = false + } else { + markLineAsRemoved(r.Syntax) + *r = modfile.Require{} + } + } + } + + if need { + f.AddNewRequire(path, vers, false) + } + return nil +} + +// AddNewRequire adds a new require line for path at version vers at the end of +// the last require block, regardless of any existing require lines for path. +func (f *File) AddNewRequire(path, vers string, indirect bool) { + line := addLine(f.Syntax, nil, "require", modfile.AutoQuote(path), vers) + r := &modfile.Require{ + Mod: module.Version{Path: path, Version: vers}, + Syntax: line, + } + setIndirect(r, indirect) + f.Require = append(f.Require, r) +} + +func (f *File) AddModuleStmt(path string) error { + if f.Syntax == nil { + f.Syntax = new(modfile.FileSyntax) + } + if f.Module == nil { + f.Module = &modfile.Module{ + Mod: module.Version{Path: path}, + Syntax: addLine(f.Syntax, nil, "module", modfile.AutoQuote(path)), + } + } else { + f.Module.Mod.Path = path + updateLine(f.Module.Syntax, "module", modfile.AutoQuote(path)) + } + return nil +} + +func (f *File) AddComment(text string) { + if f.Syntax == nil { + f.Syntax = new(modfile.FileSyntax) + } + f.Syntax.Stmt = append(f.Syntax.Stmt, &modfile.CommentBlock{ + Comments: modfile.Comments{ + Before: []modfile.Comment{ + { + Token: text, + }, + }, + }, + }) +} + +func (f *File) AddReplace(oldPath, oldVers, newPath, newVers string) error { + return addReplace(f.Syntax, &f.Replace, oldPath, oldVers, newPath, newVers) +} + +func (f *File) DropRequire(path string) error { + for _, r := range f.Require { + if r.Mod.Path == path { + markLineAsRemoved(r.Syntax) + *r = modfile.Require{} + } + } + return nil +} + +func (f *File) DropReplace(oldPath, oldVers string) error { + for _, r := range f.Replace { + if r.Old.Path == oldPath && r.Old.Version == oldVers { + markLineAsRemoved(r.Syntax) + *r = modfile.Replace{} + } + } + return nil +} + // Validate validates gno.mod func (f *File) Validate() error { if f.Module == nil { @@ -73,13 +176,8 @@ func (f *File) FetchDeps(path string, remote string, verbose bool) error { return fmt.Errorf("writepackage: %w", err) } - modFile := &File{ - Module: &modfile.Module{ - Mod: module.Version{ - Path: mod.Path, - }, - }, - } + modFile := new(File) + modFile.AddModuleStmt(mod.Path) for _, req := range requirements { path := req[1 : len(req)-1] // trim leading and trailing `"` if strings.HasSuffix(path, modFile.Module.Mod.Path) { @@ -92,13 +190,7 @@ func (f *File) FetchDeps(path string, remote string, verbose bool) error { if strings.HasPrefix(path, gnolang.ImportPrefix) { path = strings.TrimPrefix(path, gnolang.ImportPrefix+"/examples/") - modFile.Require = append(modFile.Require, &modfile.Require{ - Mod: module.Version{ - Path: path, - Version: "v0.0.0", // TODO: Use latest? - }, - Indirect: true, - }) + modFile.AddNewRequire(path, "v0.0.0-latest", true) } } @@ -112,7 +204,7 @@ func (f *File) FetchDeps(path string, remote string, verbose bool) error { } pkgPath := PackageDir(path, mod) goModFilePath := filepath.Join(pkgPath, "go.mod") - err = goMod.WriteToPath(goModFilePath) + err = goMod.Write(goModFilePath) if err != nil { return err } @@ -121,42 +213,14 @@ func (f *File) FetchDeps(path string, remote string, verbose bool) error { return nil } -// WriteToPath writes file to the given absolute file path -// TODO: Find better way to do this. Try to use `modfile` -// package to manage this. -func (f *File) WriteToPath(absFilePath string) error { - if f.Module == nil { - return errors.New("writing go.mod: module not found") - } - - data := "module " + f.Module.Mod.Path + "\n" - - if f.Go != nil { - data += "\ngo " + f.Go.Version + "\n" - } - - if f.Require != nil { - data += "\nrequire (" + "\n" - for _, req := range f.Require { - data += "\t" + req.Mod.Path + " " + req.Mod.Version + "\n" - } - data += ")\n" - } - - if f.Replace != nil { - data += "\nreplace (" + "\n" - for _, rep := range f.Replace { - data += "\t" + rep.Old.Path + " " + rep.Old.Version + - " => " + rep.New.Path + "\n" - } - data += ")\n" - } - - err := os.WriteFile(absFilePath, []byte(data), 0o644) +// writes file to the given absolute file path +func (f *File) Write(fname string) error { + f.Syntax.Cleanup() + data := modfile.Format(f.Syntax) + err := os.WriteFile(fname, data, 0o644) if err != nil { - return fmt.Errorf("writefile %q: %w", absFilePath, err) + return fmt.Errorf("writefile %q: %w", fname, err) } - return nil } diff --git a/gnovm/pkg/gnomod/file_test.go b/gnovm/pkg/gnomod/file_test.go index 7acea3a6096..0012960eb4f 100644 --- a/gnovm/pkg/gnomod/file_test.go +++ b/gnovm/pkg/gnomod/file_test.go @@ -20,11 +20,30 @@ func TestFetchDeps(t *testing.T) { for _, tc := range []struct { desc string modFile File + errorShouldContain string requirements []string stdOutContains []string cachedStdOutContains []string }{ { + desc: "not_exists", + modFile: File{ + Module: &modfile.Module{ + Mod: module.Version{ + Path: "testFetchDeps", + }, + }, + Require: []*modfile.Require{ + { + Mod: module.Version{ + Path: "gno.land/p/demo/does_not_exists", + Version: "v0.0.0", + }, + }, + }, + }, + errorShouldContain: "querychain (gno.land/p/demo/does_not_exists)", + }, { desc: "fetch_gno.land/p/demo/avl", modFile: File{ Module: &modfile.Module{ @@ -89,29 +108,34 @@ func TestFetchDeps(t *testing.T) { defer cleanUpFn() // Fetching dependencies - tc.modFile.FetchDeps(dirPath, testRemote, true) + err := tc.modFile.FetchDeps(dirPath, testRemote, true) + if tc.errorShouldContain != "" { + require.ErrorContains(t, err, tc.errorShouldContain) + } else { + require.Nil(t, err) - // Read dir - entries, err := os.ReadDir(filepath.Join(dirPath, "gno.land", "p", "demo")) - require.Nil(t, err) + // Read dir + entries, err := os.ReadDir(filepath.Join(dirPath, "gno.land", "p", "demo")) + require.Nil(t, err) - // Check dir entries - assert.Equal(t, len(tc.requirements), len(entries)) - for _, e := range entries { - assert.Contains(t, tc.requirements, e.Name()) - } + // Check dir entries + assert.Equal(t, len(tc.requirements), len(entries)) + for _, e := range entries { + assert.Contains(t, tc.requirements, e.Name()) + } - // Check logs - for _, c := range tc.stdOutContains { - assert.Contains(t, buf.String(), c) - } + // Check logs + for _, c := range tc.stdOutContains { + assert.Contains(t, buf.String(), c) + } - buf.Reset() + buf.Reset() - // Try fetching again. Should be cached - tc.modFile.FetchDeps(dirPath, testRemote, true) - for _, c := range tc.cachedStdOutContains { - assert.Contains(t, buf.String(), c) + // Try fetching again. Should be cached + tc.modFile.FetchDeps(dirPath, testRemote, true) + for _, c := range tc.cachedStdOutContains { + assert.Contains(t, buf.String(), c) + } } }) } diff --git a/gnovm/pkg/gnomod/gnomod.go b/gnovm/pkg/gnomod/gnomod.go index aa41c5aa00c..d750b7c9f29 100644 --- a/gnovm/pkg/gnomod/gnomod.go +++ b/gnovm/pkg/gnomod/gnomod.go @@ -3,13 +3,12 @@ package gnomod import ( "errors" "fmt" - "io/ioutil" "os" "path/filepath" "strings" + "github.com/gnolang/gno/gnovm/pkg/gnoenv" "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/tm2/pkg/crypto/keys/client" "github.com/gnolang/gno/tm2/pkg/std" "golang.org/x/mod/modfile" "golang.org/x/mod/module" @@ -19,7 +18,7 @@ const queryPathFile = "vm/qfile" // GetGnoModPath returns the path for gno modules func GetGnoModPath() string { - return filepath.Join(client.HomeDir(), "pkg", "mod") + return filepath.Join(gnoenv.HomeDir(), "pkg", "mod") } // PackageDir resolves a given module.Version to the path on the filesystem. @@ -100,7 +99,7 @@ func GnoToGoMod(f File) (*File, error) { if strings.HasPrefix(f.Module.Mod.Path, gnolang.GnoRealmPkgsPrefixBefore) || strings.HasPrefix(f.Module.Mod.Path, gnolang.GnoPackagePrefixBefore) { - f.Module.Mod.Path = gnolang.ImportPrefix + "/examples/" + f.Module.Mod.Path + f.AddModuleStmt(gnolang.ImportPrefix + "/examples/" + f.Module.Mod.Path) } for i := range f.Require { @@ -113,20 +112,17 @@ func GnoToGoMod(f File) (*File, error) { path := f.Require[i].Mod.Path if strings.HasPrefix(f.Require[i].Mod.Path, gnolang.GnoRealmPkgsPrefixBefore) || strings.HasPrefix(f.Require[i].Mod.Path, gnolang.GnoPackagePrefixBefore) { - f.Require[i].Mod.Path = gnolang.ImportPrefix + "/examples/" + f.Require[i].Mod.Path + // Add dependency with a modified import path + f.AddRequire(gnolang.ImportPrefix+"/examples/"+f.Require[i].Mod.Path, f.Require[i].Mod.Version) } - - f.Replace = append(f.Replace, &modfile.Replace{ - Old: module.Version{ - Path: f.Require[i].Mod.Path, - Version: f.Require[i].Mod.Version, - }, - New: module.Version{ - Path: filepath.Join(gnoModPath, path), - }, - }) + f.AddReplace(f.Require[i].Mod.Path, f.Require[i].Mod.Version, filepath.Join(gnoModPath, path), "") + // Remove the old require since the new dependency was added above + f.DropRequire(f.Require[i].Mod.Path) } + // Remove replacements that are not replaced by directories. + // + // Explanation: // By this stage every replacement should be replace by dir. // If not replaced by dir, remove it. // @@ -153,14 +149,11 @@ func GnoToGoMod(f File) (*File, error) { // ``` // // Remove `gno.land/p/demo/avl v1.2.3 => gno.land/p/demo/avl v3.2.1`. - repl := make([]*modfile.Replace, 0, len(f.Replace)) for _, r := range f.Replace { if !modfile.IsDirectoryPath(r.New.Path) { - continue + f.DropReplace(r.Old.Path, r.Old.Version) } - repl = append(repl, r) } - f.Replace = repl return &f, nil } @@ -178,9 +171,9 @@ func CreateGnoModFile(rootDir, modPath string) error { if modPath == "" { // Check .gno files for package name // and use it as modPath - files, err := ioutil.ReadDir(rootDir) + files, err := os.ReadDir(rootDir) if err != nil { - fmt.Errorf("read dir %q: %w", rootDir, err) + return fmt.Errorf("read dir %q: %w", rootDir, err) } var pkgName gnolang.Name @@ -215,14 +208,9 @@ func CreateGnoModFile(rootDir, modPath string) error { return err } - modFile := &File{ - Module: &modfile.Module{ - Mod: module.Version{ - Path: modPath, - }, - }, - } - modFile.WriteToPath(filepath.Join(rootDir, "gno.mod")) + modfile := new(File) + modfile.AddModuleStmt(modPath) + modfile.Write(filepath.Join(rootDir, "gno.mod")) return nil } diff --git a/gnovm/pkg/gnomod/parse.go b/gnovm/pkg/gnomod/parse.go index 5bda3c31f70..a6314d5729f 100644 --- a/gnovm/pkg/gnomod/parse.go +++ b/gnovm/pkg/gnomod/parse.go @@ -42,6 +42,34 @@ func ParseAt(dir string) (*File, error) { return gm, nil } +// tries to parse gno mod file given the filename, using Parse and Validate from +// the gnomod package +// +// TODO(tb): replace by `gnomod.ParseAt` ? The key difference is the latter +// looks for gno.mod in parent directories, while this function doesn't. +func ParseGnoMod(fname string) (*File, error) { + file, err := os.Stat(fname) + if err != nil { + return nil, fmt.Errorf("could not read gno.mod file: %w", err) + } + if file.IsDir() { + return nil, fmt.Errorf("invalid gno.mod at %q: is a directory", fname) + } + + b, err := os.ReadFile(fname) + if err != nil { + return nil, fmt.Errorf("could not read gno.mod file: %w", err) + } + gm, err := Parse(fname, b) + if err != nil { + return nil, fmt.Errorf("error parsing gno.mod file at %q: %w", fname, err) + } + if err := gm.Validate(); err != nil { + return nil, fmt.Errorf("error validating gno.mod file at %q: %w", fname, err) + } + return gm, nil +} + // Parse parses and returns a gno.mod file. // // - file is the name of the file, used in positions and errors. diff --git a/gnovm/pkg/gnomod/parse_test.go b/gnovm/pkg/gnomod/parse_test.go index 934531e69c7..61aaa83482b 100644 --- a/gnovm/pkg/gnomod/parse_test.go +++ b/gnovm/pkg/gnomod/parse_test.go @@ -1,9 +1,12 @@ package gnomod import ( + "path/filepath" "testing" + "github.com/gnolang/gno/tm2/pkg/testutils" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestModuleDeprecated(t *testing.T) { @@ -167,3 +170,57 @@ func TestParseDraft(t *testing.T) { }) } } + +func TestParseGnoMod(t *testing.T) { + pkgDir := "bar" + for _, tc := range []struct { + desc, modData, modPath, errShouldContain string + }{ + { + desc: "file not exists", + modData: `module foo`, + modPath: filepath.Join(pkgDir, "mod.gno"), + errShouldContain: "could not read gno.mod file:", + }, + { + desc: "file path is dir", + modData: `module foo`, + modPath: pkgDir, + errShouldContain: "is a directory", + }, + { + desc: "valid gno.mod file", + modData: `module foo`, + modPath: filepath.Join(pkgDir, "gno.mod"), + }, + { + desc: "error parsing gno.mod", + modData: `module foo v0.0.0`, + modPath: filepath.Join(pkgDir, "gno.mod"), + errShouldContain: "error parsing gno.mod file at", + }, + { + desc: "error validating gno.mod", + modData: `require bar v0.0.0`, + modPath: filepath.Join(pkgDir, "gno.mod"), + errShouldContain: "error validating gno.mod file at", + }, + } { + t.Run(tc.desc, func(t *testing.T) { + // Create test dir + tempDir, cleanUpFn := testutils.NewTestCaseDir(t) + require.NotNil(t, tempDir) + defer cleanUpFn() + + // Create gno package + createGnoModPkg(t, tempDir, pkgDir, tc.modData) + + _, err := ParseGnoMod(filepath.Join(tempDir, tc.modPath)) + if tc.errShouldContain != "" { + assert.ErrorContains(t, err, tc.errShouldContain) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/gnovm/pkg/gnomod/read.go b/gnovm/pkg/gnomod/read.go index 206c843f86a..a7a600f8826 100644 --- a/gnovm/pkg/gnomod/read.go +++ b/gnovm/pkg/gnomod/read.go @@ -3,9 +3,10 @@ // license that can be found in here[1]. // // [1]: https://cs.opensource.google/go/x/mod/+/master:LICENSE -// Original Filepath: golang.org/x/mod/modfile/read.go // -// Note: This file may contain some modifications. +// Mostly copied and modified from: +// - golang.org/x/mod/modfile/read.go +// - golang.org/x/mod/modfile/rule.go package gnomod @@ -773,7 +774,7 @@ func parseReplace(filename string, line *modfile.Line, verb string, args []strin if strings.Contains(ns, "@") { return nil, errorf("replacement module must match format 'path version', not 'path@version'") } - return nil, errorf("replacement module without version must be directory path (rooted or starting with ./ or ../)") + return nil, errorf("replacement module without version must be directory path (rooted or starting with . or ..)") } if filepath.Separator == '/' && strings.Contains(ns, `\`) { return nil, errorf("replacement directory appears to be Windows path (on a non-windows system)") @@ -795,6 +796,8 @@ func parseReplace(filename string, line *modfile.Line, verb string, args []strin }, nil } +var reDeprecation = regexp.MustCompile(`(?s)(?:^|\n\n)Deprecated: *(.*?)(?:$|\n\n)`) + // parseDeprecation extracts the text of comments on a "module" directive and // extracts a deprecation message from that. // @@ -805,8 +808,7 @@ func parseReplace(filename string, line *modfile.Line, verb string, args []strin // parseDeprecation returns the message from the first. func parseDeprecation(block *modfile.LineBlock, line *modfile.Line) string { text := parseDirectiveComment(block, line) - rx := regexp.MustCompile(`(?s)(?:^|\n\n)Deprecated: *(.*?)(?:$|\n\n)`) - m := rx.FindStringSubmatch(text) + m := reDeprecation.FindStringSubmatch(text) if m == nil { return "" } @@ -845,3 +847,207 @@ func parseDraft(block *modfile.CommentBlock) bool { } return true } + +// markLineAsRemoved modifies line so that it (and its end-of-line comment, if any) +// will be dropped by (*FileSyntax).Cleanup. +func markLineAsRemoved(line *modfile.Line) { + line.Token = nil + line.Comments.Suffix = nil +} + +func updateLine(line *modfile.Line, tokens ...string) { + if line.InBlock { + tokens = tokens[1:] + } + line.Token = tokens +} + +// setIndirect sets line to have (or not have) a "// indirect" comment. +func setIndirect(r *modfile.Require, indirect bool) { + r.Indirect = indirect + line := r.Syntax + if isIndirect(line) == indirect { + return + } + if indirect { + // Adding comment. + if len(line.Suffix) == 0 { + // New comment. + line.Suffix = []modfile.Comment{{Token: "// indirect", Suffix: true}} + return + } + + com := &line.Suffix[0] + text := strings.TrimSpace(strings.TrimPrefix(com.Token, string(slashSlash))) + if text == "" { + // Empty comment. + com.Token = "// indirect" + return + } + + // Insert at beginning of existing comment. + com.Token = "// indirect; " + text + return + } + + // Removing comment. + f := strings.TrimSpace(strings.TrimPrefix(line.Suffix[0].Token, string(slashSlash))) + if f == "indirect" { + // Remove whole comment. + line.Suffix = nil + return + } + + // Remove comment prefix. + com := &line.Suffix[0] + i := strings.Index(com.Token, "indirect;") + com.Token = "//" + com.Token[i+len("indirect;"):] +} + +// isIndirect reports whether line has a "// indirect" comment, +// meaning it is in go.mod only for its effect on indirect dependencies, +// so that it can be dropped entirely once the effective version of the +// indirect dependency reaches the given minimum version. +func isIndirect(line *modfile.Line) bool { + if len(line.Suffix) == 0 { + return false + } + f := strings.Fields(strings.TrimPrefix(line.Suffix[0].Token, string(slashSlash))) + return (len(f) == 1 && f[0] == "indirect" || len(f) > 1 && f[0] == "indirect;") +} + +// addLine adds a line containing the given tokens to the file. +// +// If the first token of the hint matches the first token of the +// line, the new line is added at the end of the block containing hint, +// extracting hint into a new block if it is not yet in one. +// +// If the hint is non-nil buts its first token does not match, +// the new line is added after the block containing hint +// (or hint itself, if not in a block). +// +// If no hint is provided, addLine appends the line to the end of +// the last block with a matching first token, +// or to the end of the file if no such block exists. +func addLine(x *modfile.FileSyntax, hint modfile.Expr, tokens ...string) *modfile.Line { + if hint == nil { + // If no hint given, add to the last statement of the given type. + Loop: + for i := len(x.Stmt) - 1; i >= 0; i-- { + stmt := x.Stmt[i] + switch stmt := stmt.(type) { + case *modfile.Line: + if stmt.Token != nil && stmt.Token[0] == tokens[0] { + hint = stmt + break Loop + } + case *modfile.LineBlock: + if stmt.Token[0] == tokens[0] { + hint = stmt + break Loop + } + } + } + } + + newLineAfter := func(i int) *modfile.Line { + newl := &modfile.Line{Token: tokens} + if i == len(x.Stmt) { + x.Stmt = append(x.Stmt, newl) + } else { + x.Stmt = append(x.Stmt, nil) + copy(x.Stmt[i+2:], x.Stmt[i+1:]) + x.Stmt[i+1] = newl + } + return newl + } + + if hint != nil { + for i, stmt := range x.Stmt { + switch stmt := stmt.(type) { + case *modfile.Line: + if stmt == hint { + if stmt.Token == nil || stmt.Token[0] != tokens[0] { + return newLineAfter(i) + } + + // Convert line to line block. + stmt.InBlock = true + block := &modfile.LineBlock{Token: stmt.Token[:1], Line: []*modfile.Line{stmt}} + stmt.Token = stmt.Token[1:] + x.Stmt[i] = block + newl := &modfile.Line{Token: tokens[1:], InBlock: true} + block.Line = append(block.Line, newl) + return newl + } + + case *modfile.LineBlock: + if stmt == hint { + if stmt.Token[0] != tokens[0] { + return newLineAfter(i) + } + + newl := &modfile.Line{Token: tokens[1:], InBlock: true} + stmt.Line = append(stmt.Line, newl) + return newl + } + + for j, line := range stmt.Line { + if line == hint { + if stmt.Token[0] != tokens[0] { + return newLineAfter(i) + } + + // Add new line after hint within the block. + stmt.Line = append(stmt.Line, nil) + copy(stmt.Line[j+2:], stmt.Line[j+1:]) + newl := &modfile.Line{Token: tokens[1:], InBlock: true} + stmt.Line[j+1] = newl + return newl + } + } + } + } + } + + newl := &modfile.Line{Token: tokens} + x.Stmt = append(x.Stmt, newl) + return newl +} + +func addReplace(syntax *modfile.FileSyntax, replace *[]*modfile.Replace, oldPath, oldVers, newPath, newVers string) error { + need := true + oldv := module.Version{Path: oldPath, Version: oldVers} + newv := module.Version{Path: newPath, Version: newVers} + tokens := []string{"replace", modfile.AutoQuote(oldPath)} + if oldVers != "" { + tokens = append(tokens, oldVers) + } + tokens = append(tokens, "=>", modfile.AutoQuote(newPath)) + if newVers != "" { + tokens = append(tokens, newVers) + } + + var hint *modfile.Line + for _, r := range *replace { + if r.Old.Path == oldPath && (oldVers == "" || r.Old.Version == oldVers) { + if need { + // Found replacement for old; update to use new. + r.New = newv + updateLine(r.Syntax, tokens...) + need = false + continue + } + // Already added; delete other replacements for same. + markLineAsRemoved(r.Syntax) + *r = modfile.Replace{} + } + if r.Old.Path == oldPath { + hint = r.Syntax + } + } + if need { + *replace = append(*replace, &modfile.Replace{Old: oldv, New: newv, Syntax: addLine(syntax, hint, tokens...)}) + } + return nil +} diff --git a/gnovm/pkg/gnomod/read_test.go b/gnovm/pkg/gnomod/read_test.go new file mode 100644 index 00000000000..cf3b6f59076 --- /dev/null +++ b/gnovm/pkg/gnomod/read_test.go @@ -0,0 +1,541 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gnomod + +import ( + "bytes" + "fmt" + "strings" + "testing" + + "golang.org/x/mod/modfile" +) + +// TestParsePunctuation verifies that certain ASCII punctuation characters +// (brackets, commas) are lexed as separate tokens, even when they're +// surrounded by identifier characters. +func TestParsePunctuation(t *testing.T) { + for _, test := range []struct { + desc, src, want string + }{ + {"paren", "require ()", "require ( )"}, + {"brackets", "require []{},", "require [ ] { } ,"}, + {"mix", "require a[b]c{d}e,", "require a [ b ] c { d } e ,"}, + {"block_mix", "require (\n\ta[b]\n)", "require ( a [ b ] )"}, + {"interval", "require [v1.0.0, v1.1.0)", "require [ v1.0.0 , v1.1.0 )"}, + } { + t.Run(test.desc, func(t *testing.T) { + f, err := parse("gno.mod", []byte(test.src)) + if err != nil { + t.Fatalf("parsing %q: %v", test.src, err) + } + var tokens []string + for _, stmt := range f.Stmt { + switch stmt := stmt.(type) { + case *modfile.Line: + tokens = append(tokens, stmt.Token...) + case *modfile.LineBlock: + tokens = append(tokens, stmt.Token...) + tokens = append(tokens, "(") + for _, line := range stmt.Line { + tokens = append(tokens, line.Token...) + } + tokens = append(tokens, ")") + default: + t.Fatalf("parsing %q: unexpected statement of type %T", test.src, stmt) + } + } + got := strings.Join(tokens, " ") + if got != test.want { + t.Errorf("parsing %q: got %q, want %q", test.src, got, test.want) + } + }) + } +} + +var modulePathTests = []struct { + input []byte + expected string +}{ + {input: []byte("module \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"}, + {input: []byte("module github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"}, + {input: []byte("module \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"}, + {input: []byte("module github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"}, + {input: []byte("module `github.com/rsc/vgotest`"), expected: "github.com/rsc/vgotest"}, + {input: []byte("module \"github.com/rsc/vgotest/v2\""), expected: "github.com/rsc/vgotest/v2"}, + {input: []byte("module github.com/rsc/vgotest/v2"), expected: "github.com/rsc/vgotest/v2"}, + {input: []byte("module \"gopkg.in/yaml.v2\""), expected: "gopkg.in/yaml.v2"}, + {input: []byte("module gopkg.in/yaml.v2"), expected: "gopkg.in/yaml.v2"}, + {input: []byte("module \"gopkg.in/check.v1\"\n"), expected: "gopkg.in/check.v1"}, + {input: []byte("module \"gopkg.in/check.v1\n\""), expected: ""}, + {input: []byte("module gopkg.in/check.v1\n"), expected: "gopkg.in/check.v1"}, + {input: []byte("module \"gopkg.in/check.v1\"\r\n"), expected: "gopkg.in/check.v1"}, + {input: []byte("module gopkg.in/check.v1\r\n"), expected: "gopkg.in/check.v1"}, + {input: []byte("module \"gopkg.in/check.v1\"\n\n"), expected: "gopkg.in/check.v1"}, + {input: []byte("module gopkg.in/check.v1\n\n"), expected: "gopkg.in/check.v1"}, + {input: []byte("module \n\"gopkg.in/check.v1\"\n\n"), expected: ""}, + {input: []byte("module \ngopkg.in/check.v1\n\n"), expected: ""}, + {input: []byte("module \"gopkg.in/check.v1\"asd"), expected: ""}, + {input: []byte("module \n\"gopkg.in/check.v1\"\n\n"), expected: ""}, + {input: []byte("module \ngopkg.in/check.v1\n\n"), expected: ""}, + {input: []byte("module \"gopkg.in/check.v1\"asd"), expected: ""}, + {input: []byte("module \nmodule a/b/c "), expected: "a/b/c"}, + {input: []byte("module \" \""), expected: " "}, + {input: []byte("module "), expected: ""}, + {input: []byte("module \" a/b/c \""), expected: " a/b/c "}, + {input: []byte("module \"github.com/rsc/vgotest1\" // with a comment"), expected: "github.com/rsc/vgotest1"}, +} + +func TestModulePath(t *testing.T) { + for _, test := range modulePathTests { + t.Run(string(test.input), func(t *testing.T) { + result := ModulePath(test.input) + if result != test.expected { + t.Fatalf("ModulePath(%q): %s, want %s", string(test.input), result, test.expected) + } + }) + } +} + +func TestParseVersions(t *testing.T) { + tests := []struct { + desc, input string + ok bool + }{ + // go lines + {desc: "empty", input: "module m\ngo \n", ok: false}, + {desc: "one", input: "module m\ngo 1\n", ok: false}, + {desc: "two", input: "module m\ngo 1.22\n", ok: true}, + {desc: "three", input: "module m\ngo 1.22.333", ok: true}, + {desc: "before", input: "module m\ngo v1.2\n", ok: false}, + {desc: "after", input: "module m\ngo 1.2rc1\n", ok: true}, + {desc: "space", input: "module m\ngo 1.2 3.4\n", ok: false}, + {desc: "alt1", input: "module m\ngo 1.2.3\n", ok: true}, + {desc: "alt2", input: "module m\ngo 1.2rc1\n", ok: true}, + {desc: "alt3", input: "module m\ngo 1.2beta1\n", ok: true}, + {desc: "alt4", input: "module m\ngo 1.2.beta1\n", ok: false}, + } + t.Run("Strict", func(t *testing.T) { + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + if _, err := Parse("gno.mod", []byte(test.input)); err == nil && !test.ok { + t.Error("unexpected success") + } else if err != nil && test.ok { + t.Errorf("unexpected error: %v", err) + } + }) + } + }) +} + +func TestComments(t *testing.T) { + for _, test := range []struct { + desc, input, want string + }{ + { + desc: "comment_only", + input: ` +// a +// b +`, + want: ` +comments before "// a" +comments before "// b" +`, + }, { + desc: "line", + input: ` +// a + +// b +module m // c +// d + +// e +`, + want: ` +comments before "// a" +line before "// b" +line suffix "// c" +comments before "// d" +comments before "// e" +`, + }, { + desc: "cr_removed", + input: "// a\r\r\n", + want: `comments before "// a\r"`, + }, + } { + t.Run(test.desc, func(t *testing.T) { + f, err := Parse("gno.mod", []byte(test.input)) + if err != nil { + t.Fatal(err) + } + + if test.desc == "block" { + panic("hov") + } + + buf := &bytes.Buffer{} + printComments := func(prefix string, cs *modfile.Comments) { + for _, c := range cs.Before { + fmt.Fprintf(buf, "%s before %q\n", prefix, c.Token) + } + for _, c := range cs.Suffix { + fmt.Fprintf(buf, "%s suffix %q\n", prefix, c.Token) + } + for _, c := range cs.After { + fmt.Fprintf(buf, "%s after %q\n", prefix, c.Token) + } + } + + printComments("file", &f.Syntax.Comments) + for _, stmt := range f.Syntax.Stmt { + switch stmt := stmt.(type) { + case *modfile.CommentBlock: + printComments("comments", stmt.Comment()) + case *modfile.Line: + printComments("line", stmt.Comment()) + } + } + + got := strings.TrimSpace(buf.String()) + want := strings.TrimSpace(test.want) + if got != want { + t.Errorf("got:\n%s\nwant:\n%s", got, want) + } + }) + } +} + +var addRequireTests = []struct { + desc string + in string + path string + vers string + out string +}{ + { + `existing`, + ` + module m + require x.y/z v1.2.3 + `, + "x.y/z", "v1.5.6", + ` + module m + require x.y/z v1.5.6 + `, + }, + { + `existing2`, + ` + module m + require ( + x.y/z v1.2.3 // first + x.z/a v0.1.0 // first-a + ) + require x.y/z v1.4.5 // second + require ( + x.y/z v1.6.7 // third + x.z/a v0.2.0 // third-a + ) + `, + "x.y/z", "v1.8.9", + ` + module m + + require ( + x.y/z v1.8.9 // first + x.z/a v0.1.0 // first-a + ) + + require x.z/a v0.2.0 // third-a + `, + }, + { + `new`, + ` + module m + require x.y/z v1.2.3 + `, + "x.y/w", "v1.5.6", + ` + module m + require ( + x.y/z v1.2.3 + x.y/w v1.5.6 + ) + `, + }, + { + `new2`, + ` + module m + require x.y/z v1.2.3 + require x.y/q/v2 v2.3.4 + `, + "x.y/w", "v1.5.6", + ` + module m + require x.y/z v1.2.3 + require ( + x.y/q/v2 v2.3.4 + x.y/w v1.5.6 + ) + `, + }, +} + +var addModuleStmtTests = []struct { + desc string + in string + path string + out string +}{ + { + `existing`, + ` + module m + require x.y/z v1.2.3 + `, + "n", + ` + module n + require x.y/z v1.2.3 + `, + }, + { + `new`, + ``, + "m", + ` + module m + `, + }, +} + +var addReplaceTests = []struct { + desc string + in string + oldPath string + oldVers string + newPath string + newVers string + out string +}{ + { + `replace_with_module`, + ` + module m + require x.y/z v1.2.3 + `, + "x.y/z", + "v1.5.6", + "a.b/c", + "v1.5.6", + ` + module m + require x.y/z v1.2.3 + replace x.y/z v1.5.6 => a.b/c v1.5.6 + `, + }, + { + `replace_with_dir`, + ` + module m + require x.y/z v1.2.3 + `, + "x.y/z", + "v1.5.6", + "/path/to/dir", + "", + ` + module m + require x.y/z v1.2.3 + replace x.y/z v1.5.6 => /path/to/dir + `, + }, +} + +var dropRequireTests = []struct { + desc string + in string + path string + out string +}{ + { + `existing`, + ` + module m + require x.y/z v1.2.3 + `, + "x.y/z", + ` + module m + `, + }, + { + `existing2`, + ` + module m + require ( + x.y/z v1.2.3 // first + x.z/a v0.1.0 // first-a + ) + require x.y/z v1.4.5 // second + require ( + x.y/z v1.6.7 // third + x.z/a v0.2.0 // third-a + ) + `, + "x.y/z", + ` + module m + + require x.z/a v0.1.0 // first-a + + require x.z/a v0.2.0 // third-a + `, + }, + { + `not_exists`, + ` + module m + require x.y/z v1.2.3 + `, + "a.b/c", + ` + module m + require x.y/z v1.2.3 + `, + }, +} + +var dropReplaceTests = []struct { + desc string + in string + path string + vers string + out string +}{ + { + `existing`, + ` + module m + require x.y/z v1.2.3 + + replace x.y/z v1.2.3 => a.b/c v1.5.6 + `, + "x.y/z", + "v1.2.3", + ` + module m + require x.y/z v1.2.3 + `, + }, + { + `not_exists`, + ` + module m + require x.y/z v1.2.3 + + replace x.y/z v1.2.3 => a.b/c v1.5.6 + `, + "a.b/c", + "v3.2.1", + ` + module m + require x.y/z v1.2.3 + + replace x.y/z v1.2.3 => a.b/c v1.5.6 + `, + }, +} + +func TestAddRequire(t *testing.T) { + for _, tt := range addRequireTests { + t.Run(tt.desc, func(t *testing.T) { + testEdit(t, tt.in, tt.out, func(f *File) error { + err := f.AddRequire(tt.path, tt.vers) + f.Syntax.Cleanup() + return err + }) + }) + } +} + +func TestAddModuleStmt(t *testing.T) { + for _, tt := range addModuleStmtTests { + t.Run(tt.desc, func(t *testing.T) { + testEdit(t, tt.in, tt.out, func(f *File) error { + err := f.AddModuleStmt(tt.path) + f.Syntax.Cleanup() + return err + }) + }) + } +} + +func TestAddReplace(t *testing.T) { + for _, tt := range addReplaceTests { + t.Run(tt.desc, func(t *testing.T) { + testEdit(t, tt.in, tt.out, func(f *File) error { + f.AddReplace(tt.oldPath, tt.oldVers, tt.newPath, tt.newVers) + f.Syntax.Cleanup() + return nil + }) + }) + } +} + +func TestDropRequire(t *testing.T) { + for _, tt := range dropRequireTests { + t.Run(tt.desc, func(t *testing.T) { + testEdit(t, tt.in, tt.out, func(f *File) error { + err := f.DropRequire(tt.path) + f.Syntax.Cleanup() + return err + }) + }) + } +} + +func TestDropReplace(t *testing.T) { + for _, tt := range dropReplaceTests { + t.Run(tt.desc, func(t *testing.T) { + testEdit(t, tt.in, tt.out, func(f *File) error { + err := f.DropReplace(tt.path, tt.vers) + f.Syntax.Cleanup() + return err + }) + }) + } +} + +func testEdit(t *testing.T, in, want string, transform func(f *File) error) *File { + t.Helper() + f, err := Parse("in", []byte(in)) + if err != nil { + t.Fatal(err) + } + g, err := Parse("out", []byte(want)) + if err != nil { + t.Fatal(err) + } + golden := modfile.Format(g.Syntax) + if err := transform(f); err != nil { + t.Fatal(err) + } + out := modfile.Format(f.Syntax) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(out, golden) { + t.Errorf("have:\n%s\nwant:\n%s", out, golden) + } + + return f +} diff --git a/gnovm/pkg/integration/coverage.go b/gnovm/pkg/integration/coverage.go new file mode 100644 index 00000000000..017f5f9de88 --- /dev/null +++ b/gnovm/pkg/integration/coverage.go @@ -0,0 +1,68 @@ +package integration + +import ( + "flag" + "fmt" + "os" + "path/filepath" + + "github.com/rogpeppe/go-internal/testscript" +) + +var coverageEnv struct { + coverdir string +} + +func init() { + flag.StringVar(&coverageEnv.coverdir, + "txtarcoverdir", "", "write testscripts coverage intermediate files to this directory") +} + +// ResolveCoverageDir attempts to resolve the coverage directory from the 'TXTARCOVERDIR' +// environment variable first, and if not set, from the 'test.txtarcoverdir' flag. +// It returns the resolved directory and a boolean indicating if the resolution was successful. +func ResolveCoverageDir() (string, bool) { + // Attempt to resolve the cover directory from the environment variable or flag + coverdir := os.Getenv("TXTARCOVERDIR") + if coverdir == "" { + coverdir = coverageEnv.coverdir + } + + return coverdir, coverdir != "" +} + +// SetupTestscriptsCoverage sets up the given testscripts environment for coverage. +// It will mostly override `GOCOVERDIR` with the target cover directory +func SetupTestscriptsCoverage(p *testscript.Params, coverdir string) error { + // Check if the given coverage directory exist + info, err := os.Stat(coverdir) + if err != nil { + return fmt.Errorf("output directory %q inaccessible: %w", coverdir, err) + } else if !info.IsDir() { + return fmt.Errorf("output %q not a directory", coverdir) + } + + // We need to have an absolute path here, because current directory + // context will change while executing testscripts. + if !filepath.IsAbs(coverdir) { + var err error + if coverdir, err = filepath.Abs(coverdir); err != nil { + return fmt.Errorf("unable to determine absolute path of %q: %w", coverdir, err) + } + } + + // Backup the original setup function + origSetup := p.Setup + p.Setup = func(env *testscript.Env) error { + if origSetup != nil { + // Call previous setup first + origSetup(env) + } + + // Override `GOCOVEDIR` directory for sub-execution + env.Setenv("GOCOVERDIR", coverdir) + return nil + } + + return nil +} diff --git a/gnovm/pkg/integration/gno.go b/gnovm/pkg/integration/gno.go new file mode 100644 index 00000000000..ee0216fa9e8 --- /dev/null +++ b/gnovm/pkg/integration/gno.go @@ -0,0 +1,100 @@ +package integration + +import ( + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + "testing" + + "github.com/gnolang/gno/gnovm/pkg/gnoenv" + osm "github.com/gnolang/gno/tm2/pkg/os" + "github.com/rogpeppe/go-internal/testscript" +) + +// SetupGno prepares the given testscript environment for tests that utilize the gno command. +// If the `gno` binary doesn't exist, it's built using the `go build` command into the specified buildDir. +// The function also include the `gno` command into `p.Cmds` to and wrap environment into p.Setup +// to correctly set up the environment variables needed for the `gno` command. +func SetupGno(p *testscript.Params, buildDir string) error { + // Try to fetch `GNOROOT` from the environment variables + gnoroot := gnoenv.RootDir() + + if !osm.DirExists(buildDir) { + return fmt.Errorf("%q does not exist or is not a directory", buildDir) + } + + // Determine the path to the gno binary within the build directory + gnoBin := filepath.Join(buildDir, "gno") + if _, err := os.Stat(gnoBin); err != nil { + if !errors.Is(err, os.ErrNotExist) { + // Handle other potential errors from os.Stat + return err + } + + // Build a fresh gno binary in a temp directory + gnoArgsBuilder := []string{"build", "-o", gnoBin} + + // Forward `-covermode` settings if set + if coverMode := testing.CoverMode(); coverMode != "" { + gnoArgsBuilder = append(gnoArgsBuilder, "-covermode", coverMode) + } + + // Append the path to the gno command source + gnoArgsBuilder = append(gnoArgsBuilder, filepath.Join(gnoroot, "gnovm", "cmd", "gno")) + + if err = exec.Command("go", gnoArgsBuilder...).Run(); err != nil { + return fmt.Errorf("unable to build gno binary: %w", err) + } + } + + // Store the original setup scripts for potential wrapping + origSetup := p.Setup + p.Setup = func(env *testscript.Env) error { + // If there's an original setup, execute it + if origSetup != nil { + if err := origSetup(env); err != nil { + return err + } + } + + // Set the GNOROOT environment variable + env.Setenv("GNOROOT", gnoroot) + + // Create a temporary home directory because certain commands require access to $HOME/.cache/go-build + home, err := os.MkdirTemp("", "gno") + if err != nil { + return fmt.Errorf("unable to create temporary home directory: %w", err) + } + env.Setenv("HOME", home) + + // Cleanup home folder + env.Defer(func() { os.RemoveAll(home) }) + + return nil + } + + // Initialize cmds map if needed + if p.Cmds == nil { + p.Cmds = make(map[string]func(ts *testscript.TestScript, neg bool, args []string)) + } + + // Register the gno command for testscripts + p.Cmds["gno"] = func(ts *testscript.TestScript, neg bool, args []string) { + err := ts.Exec(gnoBin, args...) + if err != nil { + ts.Logf("gno command error: %+v", err) + } + + commandSucceeded := (err == nil) + successExpected := !neg + + // Compare the command's success status with the expected outcome. + if commandSucceeded != successExpected { + ts.Fatalf("unexpected gno command outcome (err=%t expected=%t)", commandSucceeded, successExpected) + } + } + + return nil +} diff --git a/gnovm/pkg/repl/repl.go b/gnovm/pkg/repl/repl.go index 0f60b948f39..c7786cf08b0 100644 --- a/gnovm/pkg/repl/repl.go +++ b/gnovm/pkg/repl/repl.go @@ -161,7 +161,7 @@ func (r *Repl) Process(input string) (out string, err error) { return r.handleExpression(exp) } - return "", fmt.Errorf("error parsing code:\n\t- as expression (error: %q)\n\t- as declarations (error: %q)", expErr.Error(), declErr.Error()) + return "", fmt.Errorf("error parsing code:\n\t- as expression: %w\n\t- as declarations: %w", expErr, declErr) } func (r *Repl) handleExpression(e *ast.File) (string, error) { diff --git a/gnovm/pkg/repl/repl_test.go b/gnovm/pkg/repl/repl_test.go index 3c4d1f7c6c6..fbb2efe8890 100644 --- a/gnovm/pkg/repl/repl_test.go +++ b/gnovm/pkg/repl/repl_test.go @@ -69,7 +69,7 @@ var fixtures = []struct { CodeSteps: []step{ { Line: "importasdasd", - Error: "recovered from panic: test/test1.gno:7: name importasdasd not declared", + Error: "test/test1.gno:7: name importasdasd not declared", }, { Line: "var a := 1", @@ -199,6 +199,8 @@ func TestRepl(t *testing.T) { } func TestReplOpts(t *testing.T) { + t.Parallel() + require := require.New(t) r := NewRepl(WithStd(nil, nil, nil), WithStore(nil)) @@ -212,6 +214,8 @@ func TestReplOpts(t *testing.T) { } func TestReplReset(t *testing.T) { + t.Parallel() + require := require.New(t) r := NewRepl() diff --git a/gnovm/stdlibs/crypto/sha256/sha256.gno b/gnovm/stdlibs/crypto/sha256/sha256.gno index efb8ebb8e37..c36313f5482 100644 --- a/gnovm/stdlibs/crypto/sha256/sha256.gno +++ b/gnovm/stdlibs/crypto/sha256/sha256.gno @@ -1,12 +1,8 @@ package sha256 -import ( - isha256 "internal/crypto/sha256" -) - const Size = 32 // Sum returns the SHA-256 checksum of the data. -func Sum256(data []byte) [Size]byte { - return isha256.Sum256(data) -} +func Sum256(data []byte) [Size]byte { return sum256(data) } + +func sum256(data []byte) [32]byte // injected diff --git a/gnovm/stdlibs/crypto/sha256/sha256.go b/gnovm/stdlibs/crypto/sha256/sha256.go new file mode 100644 index 00000000000..c3aac1106e2 --- /dev/null +++ b/gnovm/stdlibs/crypto/sha256/sha256.go @@ -0,0 +1,7 @@ +package sha256 + +import "crypto/sha256" + +func X_sum256(data []byte) [32]byte { + return sha256.Sum256(data) +} diff --git a/gnovm/stdlibs/encoding/base64/base64.gno b/gnovm/stdlibs/encoding/base64/base64.gno index 7889a548832..ea3b0a55c2a 100644 --- a/gnovm/stdlibs/encoding/base64/base64.gno +++ b/gnovm/stdlibs/encoding/base64/base64.gno @@ -496,7 +496,9 @@ func (enc *Encoding) Decode(dst, src []byte) (n int, err error) { _ = enc.decodeMap si := 0 - for strconv.IntSize >= 64 && len(src)-si >= 8 && len(dst)-n >= 8 { + // XXX: Go source checks for strconv.IntSize >= 64 as well in this loop. + // In the gnovm, int size is always guaranteed to be 64 bits. + for len(src)-si >= 8 && len(dst)-n >= 8 { src2 := src[si : si+8] if dn, ok := assemble64( enc.decodeMap[src2[0]], diff --git a/gnovm/stdlibs/encoding/encoding.gno b/gnovm/stdlibs/encoding/encoding.gno new file mode 100644 index 00000000000..50acf3c23a1 --- /dev/null +++ b/gnovm/stdlibs/encoding/encoding.gno @@ -0,0 +1,54 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package encoding defines interfaces shared by other packages that +// convert data to and from byte-level and textual representations. +// Packages that check for these interfaces include encoding/gob, +// encoding/json, and encoding/xml. As a result, implementing an +// interface once can make a type useful in multiple encodings. +// Standard types that implement these interfaces include time.Time and net.IP. +// The interfaces come in pairs that produce and consume encoded data. +// +// Adding encoding/decoding methods to existing types may constitute a breaking change, +// as they can be used for serialization in communicating with programs +// written with different library versions. +// The policy for packages maintained by the Go project is to only allow +// the addition of marshaling functions if no existing, reasonable marshaling exists. +package encoding + +// BinaryMarshaler is the interface implemented by an object that can +// marshal itself into a binary form. +// +// MarshalBinary encodes the receiver into a binary form and returns the result. +type BinaryMarshaler interface { + MarshalBinary() (data []byte, err error) +} + +// BinaryUnmarshaler is the interface implemented by an object that can +// unmarshal a binary representation of itself. +// +// UnmarshalBinary must be able to decode the form generated by MarshalBinary. +// UnmarshalBinary must copy the data if it wishes to retain the data +// after returning. +type BinaryUnmarshaler interface { + UnmarshalBinary(data []byte) error +} + +// TextMarshaler is the interface implemented by an object that can +// marshal itself into a textual form. +// +// MarshalText encodes the receiver into UTF-8-encoded text and returns the result. +type TextMarshaler interface { + MarshalText() (text []byte, err error) +} + +// TextUnmarshaler is the interface implemented by an object that can +// unmarshal a textual representation of itself. +// +// UnmarshalText must be able to decode the form generated by MarshalText. +// UnmarshalText must copy the text if it wishes to retain the text +// after returning. +type TextUnmarshaler interface { + UnmarshalText(text []byte) error +} diff --git a/gnovm/stdlibs/hash/adler32/adler32.gno b/gnovm/stdlibs/hash/adler32/adler32.gno new file mode 100644 index 00000000000..38d644d1ee5 --- /dev/null +++ b/gnovm/stdlibs/hash/adler32/adler32.gno @@ -0,0 +1,135 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package adler32 implements the Adler-32 checksum. +// +// It is defined in RFC 1950: +// +// Adler-32 is composed of two sums accumulated per byte: s1 is +// the sum of all bytes, s2 is the sum of all s1 values. Both sums +// are done modulo 65521. s1 is initialized to 1, s2 to zero. The +// Adler-32 checksum is stored as s2*65536 + s1 in most- +// significant-byte first (network) order. +package adler32 + +import ( + "errors" + "hash" +) + +const ( + // mod is the largest prime that is less than 65536. + mod = 65521 + // nmax is the largest n such that + // 255 * n * (n+1) / 2 + (n+1) * (mod-1) <= 2^32-1. + // It is mentioned in RFC 1950 (search for "5552"). + nmax = 5552 +) + +// The size of an Adler-32 checksum in bytes. +const Size = 4 + +// digest represents the partial evaluation of a checksum. +// The low 16 bits are s1, the high 16 bits are s2. +type digest uint32 + +func (d *digest) Reset() { *d = 1 } + +// New returns a new hash.Hash32 computing the Adler-32 checksum. Its +// Sum method will lay the value out in big-endian byte order. The +// returned Hash32 also implements encoding.BinaryMarshaler and +// encoding.BinaryUnmarshaler to marshal and unmarshal the internal +// state of the hash. +func New() hash.Hash32 { + d := new(digest) + d.Reset() + return d +} + +func (d *digest) Size() int { return Size } + +func (d *digest) BlockSize() int { return 4 } + +const ( + magic = "adl\x01" + marshaledSize = len(magic) + 4 +) + +func (d *digest) MarshalBinary() ([]byte, error) { + b := make([]byte, 0, marshaledSize) + b = append(b, magic...) + b = appendUint32(b, uint32(*d)) + return b, nil +} + +func (d *digest) UnmarshalBinary(b []byte) error { + if len(b) < len(magic) || string(b[:len(magic)]) != magic { + return errors.New("hash/adler32: invalid hash state identifier") + } + if len(b) != marshaledSize { + return errors.New("hash/adler32: invalid hash state size") + } + *d = digest(readUint32(b[len(magic):])) + return nil +} + +func appendUint32(b []byte, x uint32) []byte { + a := [4]byte{ + byte(x >> 24), + byte(x >> 16), + byte(x >> 8), + byte(x), + } + return append(b, a[:]...) +} + +func readUint32(b []byte) uint32 { + _ = b[3] + return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 +} + +// Add p to the running checksum d. +func update(d digest, p []byte) digest { + s1, s2 := uint32(d&0xffff), uint32(d>>16) + for len(p) > 0 { + var q []byte + if len(p) > nmax { + p, q = p[:nmax], p[nmax:] + } + for len(p) >= 4 { + s1 += uint32(p[0]) + s2 += s1 + s1 += uint32(p[1]) + s2 += s1 + s1 += uint32(p[2]) + s2 += s1 + s1 += uint32(p[3]) + s2 += s1 + p = p[4:] + } + for _, x := range p { + s1 += uint32(x) + s2 += s1 + } + s1 %= mod + s2 %= mod + p = q + } + return digest(s2<<16 | s1) +} + +func (d *digest) Write(p []byte) (nn int, err error) { + *d = update(*d, p) + return len(p), nil +} + +func (d *digest) Sum32() uint32 { return uint32(*d) } + +func (d *digest) Sum(in []byte) []byte { + s := uint32(*d) + return append(in, byte(s>>24), byte(s>>16), byte(s>>8), byte(s)) +} + +// Checksum returns the Adler-32 checksum of data. +func Checksum(data []byte) uint32 { return uint32(update(1, data)) } diff --git a/gnovm/stdlibs/hash/hash.gno b/gnovm/stdlibs/hash/hash.gno new file mode 100644 index 00000000000..62cf6a45184 --- /dev/null +++ b/gnovm/stdlibs/hash/hash.gno @@ -0,0 +1,58 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package hash provides interfaces for hash functions. +package hash + +import "io" + +// Hash is the common interface implemented by all hash functions. +// +// Hash implementations in the standard library (e.g. hash/crc32 and +// crypto/sha256) implement the encoding.BinaryMarshaler and +// encoding.BinaryUnmarshaler interfaces. Marshaling a hash implementation +// allows its internal state to be saved and used for additional processing +// later, without having to re-write the data previously written to the hash. +// The hash state may contain portions of the input in its original form, +// which users are expected to handle for any possible security implications. +// +// Compatibility: Any future changes to hash or crypto packages will endeavor +// to maintain compatibility with state encoded using previous versions. +// That is, any released versions of the packages should be able to +// decode data written with any previously released version, +// subject to issues such as security fixes. +// See the Go compatibility document for background: https://golang.org/doc/go1compat +type Hash interface { + // Write (via the embedded io.Writer interface) adds more data to the running hash. + // It never returns an error. + io.Writer + + // Sum appends the current hash to b and returns the resulting slice. + // It does not change the underlying hash state. + Sum(b []byte) []byte + + // Reset resets the Hash to its initial state. + Reset() + + // Size returns the number of bytes Sum will return. + Size() int + + // BlockSize returns the hash's underlying block size. + // The Write method must be able to accept any amount + // of data, but it may operate more efficiently if all writes + // are a multiple of the block size. + BlockSize() int +} + +// Hash32 is the common interface implemented by all 32-bit hash functions. +type Hash32 interface { + Hash + Sum32() uint32 +} + +// Hash64 is the common interface implemented by all 64-bit hash functions. +type Hash64 interface { + Hash + Sum64() uint64 +} diff --git a/gnovm/stdlibs/hash/marshal_test.gno b/gnovm/stdlibs/hash/marshal_test.gno new file mode 100644 index 00000000000..b31d35faa77 --- /dev/null +++ b/gnovm/stdlibs/hash/marshal_test.gno @@ -0,0 +1,87 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that the hashes in the standard library implement +// BinaryMarshaler, BinaryUnmarshaler, +// and lock in the current representations. + +package hash + +import ( + "bytes" + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "encoding" + "encoding/hex" + "hash" + "hash/adler32" + "testing" +) + +func fromHex(s string) []byte { + b, err := hex.DecodeString(s) + if err != nil { + panic(err) + } + return b +} + +var marshalTests = []struct { + name string + new func() hash.Hash + golden []byte +}{ + {"adler32", func() hash.Hash { return adler32.New() }, fromHex("61646c01460a789d")}, +} + +func TestMarshalHash(t *testing.T) { + for _, tt := range marshalTests { + t.Run(tt.name, func(t *testing.T) { + buf := make([]byte, 256) + for i := range buf { + buf[i] = byte(i) + } + + h := tt.new() + h.Write(buf[:256]) + sum := h.Sum(nil) + + h2 := tt.new() + h3 := tt.new() + const split = 249 + for i := 0; i < split; i++ { + h2.Write(buf[i : i+1]) + } + h2m, ok := h2.(encoding.BinaryMarshaler) + if !ok { + t.Fatalf("Hash does not implement MarshalBinary") + } + enc, err := h2m.MarshalBinary() + if err != nil { + t.Fatalf("MarshalBinary: %v", err) + } + if !bytes.Equal(enc, tt.golden) { + t.Errorf("MarshalBinary = %x, want %x", enc, tt.golden) + } + h3u, ok := h3.(encoding.BinaryUnmarshaler) + if !ok { + t.Fatalf("Hash does not implement UnmarshalBinary") + } + if err := h3u.UnmarshalBinary(enc); err != nil { + t.Fatalf("UnmarshalBinary: %v", err) + } + h2.Write(buf[split:]) + h3.Write(buf[split:]) + sum2 := h2.Sum(nil) + sum3 := h3.Sum(nil) + if !bytes.Equal(sum2, sum) { + t.Fatalf("Sum after MarshalBinary = %x, want %x", sum2, sum) + } + if !bytes.Equal(sum3, sum) { + t.Fatalf("Sum after UnmarshalBinary = %x, want %x", sum3, sum) + } + }) + } +} diff --git a/gnovm/stdlibs/internal/crypto/sha256/sha256.gno b/gnovm/stdlibs/internal/crypto/sha256/sha256.gno deleted file mode 100644 index 458b2123f59..00000000000 --- a/gnovm/stdlibs/internal/crypto/sha256/sha256.gno +++ /dev/null @@ -1,3 +0,0 @@ -package sha256 - -// XXX injected via stdlibs/stdlibs.go diff --git a/gnovm/stdlibs/internal/math/math.gno b/gnovm/stdlibs/internal/math/math.gno deleted file mode 100644 index 42245392caf..00000000000 --- a/gnovm/stdlibs/internal/math/math.gno +++ /dev/null @@ -1,3 +0,0 @@ -package math - -// XXX injected via stdlibs/stdlibs.go diff --git a/gnovm/stdlibs/io/export_test.gno b/gnovm/stdlibs/io/export_test.gno index fa3e8e76f61..6204ffc4591 100644 --- a/gnovm/stdlibs/io/export_test.gno +++ b/gnovm/stdlibs/io/export_test.gno @@ -5,4 +5,8 @@ package io // exported for test -var ErrInvalidWrite = errInvalidWrite +var ( + ErrInvalidWrite = errInvalidWrite + ErrWhence = errWhence + ErrOffset = errOffset +) diff --git a/gnovm/stdlibs/io/io.gno b/gnovm/stdlibs/io/io.gno index 54caf32cb95..6ee52cfe293 100644 --- a/gnovm/stdlibs/io/io.gno +++ b/gnovm/stdlibs/io/io.gno @@ -16,6 +16,8 @@ import ( "errors" ) +// TODO: implement rest of io package after sync package added. + // Seek whence values. const ( SeekStart = 0 // seek relative to the origin of the file @@ -477,6 +479,15 @@ func (l *LimitedReader) Read(p []byte) (n int, err error) { // NewSectionReader returns a SectionReader that reads from r // starting at offset off and stops with EOF after n bytes. func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader { + var remaining int64 + const maxInt64 = 1<<63 - 1 + if off <= maxInt64-n { + remaining = n + off + } else { + // Overflow, with no way to return error. + // Assume we can read up to an offset of `1<<63 - 1` bytes in this case. + remaining = maxInt64 + } return &SectionReader{r, off, off, off + n} } @@ -543,6 +554,53 @@ func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error) { // Size returns the size of the section in bytes. func (s *SectionReader) Size() int64 { return s.limit - s.base } +// An OffsetWriter maps writers at offset base to offset base + off in the underlying writer. +type OffsetWriter struct { + w WriterAt + base int64 // the original offset + off int64 // the current offset +} + +// NewOffsetWriter returns a new OffsetWriter that writes to w starting at offset off. +func NewOffsetWriter(w WriterAt, off int64) *OffsetWriter { + return &OffsetWriter{w: w, off: off} +} + +func (o *OffsetWriter) Write(p []byte) (n int, err error) { + // n, err = o.w.WriterAt(p, o.off) + wa := o.w + n, err = wa.WriteAt(p, o.off) + o.off += int64(n) + return +} + +func (o *OffsetWriter) WriteAt(p []byte, off int64) (n int, err error) { + if off < 0 { + return 0, errOffset + } + + off += o.base + return o.w.WriteAt(p, off) +} + +func (o *OffsetWriter) Seek(offset int64, whence int) (int64, error) { + switch whence { + default: + return 0, errWhence + case SeekStart: + offset += o.base + case SeekCurrent: + offset += o.off + } + + if offset < o.base { + return 0, errOffset + } + + o.off = offset + return offset - o.base, nil +} + // TeeReader returns a Reader that writes to w what it reads from r. // All reads from r performed through it are matched with // corresponding writes to w. There is no internal buffering - @@ -614,7 +672,12 @@ func (discard) ReadFrom(r Reader) (n int64, err error) { // NopCloser returns a ReadCloser with a no-op Close method wrapping // the provided Reader r. +// If r implements WriterTo, the returned ReadCloser will implement WriterTo +// by forwarding calls to r. func NopCloser(r Reader) ReadCloser { + if _, ok := r.(WriterTo); ok { + return nopCloserWriterTo{r} + } return nopCloser{r} } @@ -624,6 +687,16 @@ type nopCloser struct { func (nopCloser) Close() error { return nil } +type nopCloserWriterTo struct { + Reader +} + +func (nopCloserWriterTo) Close() error { return nil } + +func (c nopCloserWriterTo) WriteTo(w Writer) (n int64, err error) { + return c.Reader.(WriterTo).WriteTo(w) +} + // ReadAll reads from r until an error or EOF and returns the data it read. // A successful call returns err == nil, not err == EOF. Because ReadAll is // defined to read from src until EOF, it does not treat an EOF from Read @@ -643,5 +716,9 @@ func ReadAll(r Reader) ([]byte, error) { } return b, err } + if len(b) == cap(b) { + // Add more capacity (let append pick how much). + b = append(b, 0)[:len(b)] + } } } diff --git a/gnovm/stdlibs/io/io_test.gno b/gnovm/stdlibs/io/io_test.gno index a97f6b8c075..613b7d13e35 100644 --- a/gnovm/stdlibs/io/io_test.gno +++ b/gnovm/stdlibs/io/io_test.gno @@ -1,14 +1,15 @@ +package io_test + // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package io_test - import ( "bytes" "errors" "fmt" "io" + "os" "strings" "testing" ) @@ -459,3 +460,84 @@ func TestCopyLargeWriter(t *testing.T) { t.Errorf("Copy error: got %v, want %v", err, want) } } + +func TestNopCloserWriterToForwarding(t *testing.T) { + for _, tc := range [...]struct { + Name string + r io.Reader + }{ + {"not a WriterTo", io.Reader(nil)}, + {"a WriterTo", struct { + io.Reader + io.WriterTo + }{}}, + } { + nc := io.NopCloser(tc.r) + + _, expected := tc.r.(io.WriterTo) + _, got := nc.(io.WriterTo) + if expected != got { + t.Errorf("NopCloser incorrectly forwards WriterTo for %s, got %t want %t", tc.Name, got, expected) + } + } +} + +// XXX os.CreateTemp is not available for now +// func TestOffsetWriter_Seek(t *testing.T) { +// tmpfilename := "TestOffsetWriter_Seek" +// tmpfile, err := os.CreateTemp(t.TempDir(), tmpfilename) +// if err != nil || tmpfile == nil { +// t.Fatalf("CreateTemp(%s) failed: %v", tmpfilename, err) +// } +// defer tmpfile.Close() +// w := NewOffsetWriter(tmpfile, 0) + +// // Should throw error errWhence if whence is not valid +// t.Run("errWhence", func(t *testing.T) { +// for _, whence := range []int{-3, -2, -1, 3, 4, 5} { +// var offset int64 = 0 +// gotOff, gotErr := w.Seek(offset, whence) +// if gotOff != 0 || gotErr != ErrWhence { +// t.Errorf("For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, %v)", +// whence, offset, gotOff, gotErr, 0, ErrWhence) +// } +// } +// }) + +// // Should throw error errOffset if offset is negative +// t.Run("errOffset", func(t *testing.T) { +// for _, whence := range []int{SeekStart, SeekCurrent} { +// for offset := int64(-3); offset < 0; offset++ { +// gotOff, gotErr := w.Seek(offset, whence) +// if gotOff != 0 || gotErr != ErrOffset { +// t.Errorf("For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, %v)", +// whence, offset, gotOff, gotErr, 0, ErrOffset) +// } +// } +// } +// }) + +// // Normal tests +// t.Run("normal", func(t *testing.T) { +// tests := []struct { +// offset int64 +// whence int +// returnOff int64 +// }{ +// // keep in order +// {whence: SeekStart, offset: 1, returnOff: 1}, +// {whence: SeekStart, offset: 2, returnOff: 2}, +// {whence: SeekStart, offset: 3, returnOff: 3}, +// {whence: SeekCurrent, offset: 1, returnOff: 4}, +// {whence: SeekCurrent, offset: 2, returnOff: 6}, +// {whence: SeekCurrent, offset: 3, returnOff: 9}, +// } +// for idx, tt := range tests { +// gotOff, gotErr := w.Seek(tt.offset, tt.whence) +// if gotOff != tt.returnOff || gotErr != nil { +// t.Errorf("%d:: For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, )", +// idx+1, tt.whence, tt.offset, gotOff, gotErr, tt.returnOff) +// } +// } +// }) +// } diff --git a/gnovm/stdlibs/io/ioutil/ioutil.gno b/gnovm/stdlibs/io/ioutil/ioutil.gno deleted file mode 100644 index 935031c0511..00000000000 --- a/gnovm/stdlibs/io/ioutil/ioutil.gno +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package ioutil implements some I/O utility functions. -// -// As of Go 1.16, the same functionality is now provided -// by package io or package os, and those implementations -// should be preferred in new code. -// See the specific function documentation for details. -package ioutil - -import ( - "io" -) - -// ReadAll reads from r until an error or EOF and returns the data it read. -// A successful call returns err == nil, not err == EOF. Because ReadAll is -// defined to read from src until EOF, it does not treat an EOF from Read -// as an error to be reported. -// -// As of Go 1.16, this function simply calls io.ReadAll. -func ReadAll(r io.Reader) ([]byte, error) { - return io.ReadAll(r) -} - -/* XXX os and os/fs removed. -// ReadFile reads the file named by filename and returns the contents. -// A successful call returns err == nil, not err == EOF. Because ReadFile -// reads the whole file, it does not treat an EOF from Read as an error -// to be reported. -// -// As of Go 1.16, this function simply calls os.ReadFile. -func ReadFile(filename string) ([]byte, error) { - return os.ReadFile(filename) -} - -// WriteFile writes data to a file named by filename. -// If the file does not exist, WriteFile creates it with permissions perm -// (before umask); otherwise WriteFile truncates it before writing, without changing permissions. -// -// As of Go 1.16, this function simply calls os.WriteFile. -func WriteFile(filename string, data []byte, perm fs.FileMode) error { - return os.WriteFile(filename, data, perm) -} - -// ReadDir reads the directory named by dirname and returns -// a list of fs.FileInfo for the directory's contents, -// sorted by filename. If an error occurs reading the directory, -// ReadDir returns no directory entries along with the error. -// -// As of Go 1.16, os.ReadDir is a more efficient and correct choice: -// it returns a list of fs.DirEntry instead of fs.FileInfo, -// and it returns partial results in the case of an error -// midway through reading a directory. -func ReadDir(dirname string) ([]fs.FileInfo, error) { - f, err := os.Open(dirname) - if err != nil { - return nil, err - } - list, err := f.Readdir(-1) - f.Close() - if err != nil { - return nil, err - } - sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() }) - return list, nil -} -*/ - -// NopCloser returns a ReadCloser with a no-op Close method wrapping -// the provided Reader r. -// -// As of Go 1.16, this function simply calls io.NopCloser. -func NopCloser(r io.Reader) io.ReadCloser { - return io.NopCloser(r) -} - -// Discard is an io.Writer on which all Write calls succeed -// without doing anything. -// -// As of Go 1.16, this value is simply io.Discard. -var Discard io.Writer = io.Discard diff --git a/gnovm/stdlibs/io/multi_test.gno b/gnovm/stdlibs/io/multi_test.gno index ee800b3ec24..31345279318 100644 --- a/gnovm/stdlibs/io/multi_test.gno +++ b/gnovm/stdlibs/io/multi_test.gno @@ -1,9 +1,9 @@ +package io_test + // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package io_test - import ( "bytes" "crypto/sha1" diff --git a/gnovm/stdlibs/math/abs.gno b/gnovm/stdlibs/math/abs.gno index 7a55c0c5bd2..08be14548dd 100644 --- a/gnovm/stdlibs/math/abs.gno +++ b/gnovm/stdlibs/math/abs.gno @@ -4,10 +4,6 @@ package math -import ( - imath "internal/math" // XXX -) - // Abs returns the absolute value of x. // // Special cases are: @@ -15,5 +11,5 @@ import ( // Abs(±Inf) = +Inf // Abs(NaN) = NaN func Abs(x float64) float64 { - return imath.Float64frombits(imath.Float64bits(x) &^ (1 << 63)) + return Float64frombits(Float64bits(x) &^ (1 << 63)) } diff --git a/gnovm/stdlibs/math/acosh.gno b/gnovm/stdlibs/math/acosh.gno new file mode 100644 index 00000000000..261f25ce9f9 --- /dev/null +++ b/gnovm/stdlibs/math/acosh.gno @@ -0,0 +1,62 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code, the long comment, and the constants +// below are from FreeBSD's /usr/src/lib/msun/src/e_acosh.c +// and came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// +// __ieee754_acosh(x) +// Method : +// Based on +// acosh(x) = log [ x + sqrt(x*x-1) ] +// we have +// acosh(x) := log(x)+ln2, if x is large; else +// acosh(x) := log(2x-1/(sqrt(x*x-1)+x)) if x>2; else +// acosh(x) := log1p(t+sqrt(2.0*t+t*t)); where t=x-1. +// +// Special cases: +// acosh(x) is NaN with signal if x<1. +// acosh(NaN) is NaN without signal. +// + +// Acosh returns the inverse hyperbolic cosine of x. +// +// Special cases are: +// +// Acosh(+Inf) = +Inf +// Acosh(x) = NaN if x < 1 +// Acosh(NaN) = NaN +func Acosh(x float64) float64 { + return acosh(x) +} + +func acosh(x float64) float64 { + const Large = 1 << 28 // 2**28 + // first case is special case + switch { + case x < 1 || IsNaN(x): + return NaN() + case x == 1: + return 0 + case x >= Large: + return Log(x) + Ln2 // x > 2**28 + case x > 2: + return Log(2*x - 1/(x+Sqrt(x*x-1))) // 2**28 > x > 2 + } + t := x - 1 + return Log1p(t + Sqrt(2*t+t*t)) // 2 >= x > 1 +} diff --git a/gnovm/stdlibs/math/all_test.gno b/gnovm/stdlibs/math/all_test.gno index ba7b1528d23..3d2a018b118 100644 --- a/gnovm/stdlibs/math/all_test.gno +++ b/gnovm/stdlibs/math/all_test.gno @@ -2,28 +2,586 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package math +package math_test import ( + "fmt" + "math" "testing" ) +// XXX: The dot import for "math" is replaced to a normal import. +// As such, all calls to export math functions are prefixed with math (as opposed +// to the original all_test.gno). + +var vf = []float64{ + 4.9790119248836735e+00, + 7.7388724745781045e+00, + -2.7688005719200159e-01, + -5.0106036182710749e+00, + 9.6362937071984173e+00, + 2.9263772392439646e+00, + 5.2290834314593066e+00, + 2.7279399104360102e+00, + 1.8253080916808550e+00, + -8.6859247685756013e+00, +} + // The expected results below were computed by the high precision calculators // at https://keisan.casio.com/. More exact input values (array vf[], above) // were obtained by printing them with "%.26f". The answers were calculated // to 26 digits (by using the "Digit number" drop-down control of each // calculator). -var vf = []float64{ - 4.9790119248836735e+00, - 7.7388724745781045e+00, +var acos = []float64{ + 1.0496193546107222142571536e+00, + 6.8584012813664425171660692e-01, + 1.5984878714577160325521819e+00, + 2.0956199361475859327461799e+00, + 2.7053008467824138592616927e-01, + 1.2738121680361776018155625e+00, + 1.0205369421140629186287407e+00, + 1.2945003481781246062157835e+00, + 1.3872364345374451433846657e+00, + 2.6231510803970463967294145e+00, +} + +var acosh = []float64{ + 2.4743347004159012494457618e+00, + 2.8576385344292769649802701e+00, + 7.2796961502981066190593175e-01, + 2.4796794418831451156471977e+00, + 3.0552020742306061857212962e+00, + 2.044238592688586588942468e+00, + 2.5158701513104513595766636e+00, + 1.99050839282411638174299e+00, + 1.6988625798424034227205445e+00, + 2.9611454842470387925531875e+00, +} + +var asin = []float64{ + 5.2117697218417440497416805e-01, + 8.8495619865825236751471477e-01, + -02.769154466281941332086016e-02, + -5.2482360935268931351485822e-01, + 1.3002662421166552333051524e+00, + 2.9698415875871901741575922e-01, + 5.5025938468083370060258102e-01, + 2.7629597861677201301553823e-01, + 1.83559892257451475846656e-01, + -1.0523547536021497774980928e+00, +} + +var asinh = []float64{ + 2.3083139124923523427628243e+00, + 2.743551594301593620039021e+00, + -2.7345908534880091229413487e-01, + -2.3145157644718338650499085e+00, + 2.9613652154015058521951083e+00, + 1.7949041616585821933067568e+00, + 2.3564032905983506405561554e+00, + 1.7287118790768438878045346e+00, + 1.3626658083714826013073193e+00, + -2.8581483626513914445234004e+00, +} + +var atan = []float64{ + 1.372590262129621651920085e+00, + 1.442290609645298083020664e+00, + -2.7011324359471758245192595e-01, + -1.3738077684543379452781531e+00, + 1.4673921193587666049154681e+00, + 1.2415173565870168649117764e+00, + 1.3818396865615168979966498e+00, + 1.2194305844639670701091426e+00, + 1.0696031952318783760193244e+00, + -1.4561721938838084990898679e+00, +} + +var atanh = []float64{ + 5.4651163712251938116878204e-01, + 1.0299474112843111224914709e+00, + -2.7695084420740135145234906e-02, + -5.5072096119207195480202529e-01, + 1.9943940993171843235906642e+00, + 3.01448604578089708203017e-01, + 5.8033427206942188834370595e-01, + 2.7987997499441511013958297e-01, + 1.8459947964298794318714228e-01, + -1.3273186910532645867272502e+00, +} + +var atan2 = []float64{ + 1.1088291730037004444527075e+00, + 9.1218183188715804018797795e-01, + 1.5984772603216203736068915e+00, + 2.0352918654092086637227327e+00, + 8.0391819139044720267356014e-01, + 1.2861075249894661588866752e+00, + 1.0889904479131695712182587e+00, + 1.3044821793397925293797357e+00, + 1.3902530903455392306872261e+00, + 2.2859857424479142655411058e+00, +} + +var cbrt = []float64{ + 1.7075799841925094446722675e+00, + 1.9779982212970353936691498e+00, + -6.5177429017779910853339447e-01, + -1.7111838886544019873338113e+00, + 2.1279920909827937423960472e+00, + 1.4303536770460741452312367e+00, + 1.7357021059106154902341052e+00, + 1.3972633462554328350552916e+00, + 1.2221149580905388454977636e+00, + -2.0556003730500069110343596e+00, +} + +var ceil = []float64{ + 5.0000000000000000e+00, + 8.0000000000000000e+00, + math.Copysign(0, -1), + -5.0000000000000000e+00, + 1.0000000000000000e+01, + 3.0000000000000000e+00, + 6.0000000000000000e+00, + 3.0000000000000000e+00, + 2.0000000000000000e+00, + -8.0000000000000000e+00, +} + +var copysign = []float64{ + -4.9790119248836735e+00, + -7.7388724745781045e+00, -2.7688005719200159e-01, -5.0106036182710749e+00, + -9.6362937071984173e+00, + -2.9263772392439646e+00, + -5.2290834314593066e+00, + -2.7279399104360102e+00, + -1.8253080916808550e+00, + -8.6859247685756013e+00, +} + +var cos = []float64{ + 2.634752140995199110787593e-01, + 1.148551260848219865642039e-01, + 9.6191297325640768154550453e-01, + 2.938141150061714816890637e-01, + -9.777138189897924126294461e-01, + -9.7693041344303219127199518e-01, + 4.940088096948647263961162e-01, + -9.1565869021018925545016502e-01, + -2.517729313893103197176091e-01, + -7.39241351595676573201918e-01, +} + +// Results for 100000 * math.Pi + vf[i] +var cosLarge = []float64{ + 2.634752141185559426744e-01, + 1.14855126055543100712e-01, + 9.61912973266488928113e-01, + 2.9381411499556122552e-01, + -9.777138189880161924641e-01, + -9.76930413445147608049e-01, + 4.940088097314976789841e-01, + -9.15658690217517835002e-01, + -2.51772931436786954751e-01, + -7.3924135157173099849e-01, +} + +var cosh = []float64{ + 7.2668796942212842775517446e+01, + 1.1479413465659254502011135e+03, + 1.0385767908766418550935495e+00, + 7.5000957789658051428857788e+01, + 7.655246669605357888468613e+03, + 9.3567491758321272072888257e+00, + 9.331351599270605471131735e+01, + 7.6833430994624643209296404e+00, + 3.1829371625150718153881164e+00, + 2.9595059261916188501640911e+03, +} + +var erf = []float64{ + 5.1865354817738701906913566e-01, + 7.2623875834137295116929844e-01, + -3.123458688281309990629839e-02, + -5.2143121110253302920437013e-01, + 8.2704742671312902508629582e-01, + 3.2101767558376376743993945e-01, + 5.403990312223245516066252e-01, + 3.0034702916738588551174831e-01, + 2.0369924417882241241559589e-01, + -7.8069386968009226729944677e-01, +} + +var erfc = []float64{ + 4.8134645182261298093086434e-01, + 2.7376124165862704883070156e-01, + 1.0312345868828130999062984e+00, + 1.5214312111025330292043701e+00, + 1.7295257328687097491370418e-01, + 6.7898232441623623256006055e-01, + 4.596009687776754483933748e-01, + 6.9965297083261411448825169e-01, + 7.9630075582117758758440411e-01, + 1.7806938696800922672994468e+00, +} + +var erfinv = []float64{ + 4.746037673358033586786350696e-01, + 8.559054432692110956388764172e-01, + -2.45427830571707336251331946e-02, + -4.78116683518973366268905506e-01, + 1.479804430319470983648120853e+00, + 2.654485787128896161882650211e-01, + 5.027444534221520197823192493e-01, + 2.466703532707627818954585670e-01, + 1.632011465103005426240343116e-01, + -1.06672334642196900710000389e+00, +} + +var exp = []float64{ + 1.4533071302642137507696589e+02, + 2.2958822575694449002537581e+03, + 7.5814542574851666582042306e-01, + 6.6668778421791005061482264e-03, + 1.5310493273896033740861206e+04, + 1.8659907517999328638667732e+01, + 1.8662167355098714543942057e+02, + 1.5301332413189378961665788e+01, + 6.2047063430646876349125085e+00, + 1.6894712385826521111610438e-04, +} + +var expm1 = []float64{ + 5.105047796122957327384770212e-02, + 8.046199708567344080562675439e-02, + -2.764970978891639815187418703e-03, + -4.8871434888875355394330300273e-02, + 1.0115864277221467777117227494e-01, + 2.969616407795910726014621657e-02, + 5.368214487944892300914037972e-02, + 2.765488851131274068067445335e-02, + 1.842068661871398836913874273e-02, + -8.3193870863553801814961137573e-02, +} + +var expm1Large = []float64{ + 4.2031418113550844e+21, + 4.0690789717473863e+33, + -0.9372627915981363e+00, + -1.0, + 7.077694784145933e+41, + 5.117936223839153e+12, + 5.124137759001189e+22, + 7.03546003972584e+11, + 8.456921800389698e+07, + -1.0, +} + +var exp2 = []float64{ + 3.1537839463286288034313104e+01, + 2.1361549283756232296144849e+02, + 8.2537402562185562902577219e-01, + 3.1021158628740294833424229e-02, + 7.9581744110252191462569661e+02, + 7.6019905892596359262696423e+00, + 3.7506882048388096973183084e+01, + 6.6250893439173561733216375e+00, + 3.5438267900243941544605339e+00, + 2.4281533133513300984289196e-03, +} + +var fabs = []float64{ + 4.9790119248836735e+00, + 7.7388724745781045e+00, + 2.7688005719200159e-01, + 5.0106036182710749e+00, 9.6362937071984173e+00, 2.9263772392439646e+00, 5.2290834314593066e+00, 2.7279399104360102e+00, 1.8253080916808550e+00, - -8.6859247685756013e+00, + 8.6859247685756013e+00, +} + +var fdim = []float64{ + 4.9790119248836735e+00, + 7.7388724745781045e+00, + 0.0000000000000000e+00, + 0.0000000000000000e+00, + 9.6362937071984173e+00, + 2.9263772392439646e+00, + 5.2290834314593066e+00, + 2.7279399104360102e+00, + 1.8253080916808550e+00, + 0.0000000000000000e+00, +} + +var floor = []float64{ + 4.0000000000000000e+00, + 7.0000000000000000e+00, + -1.0000000000000000e+00, + -6.0000000000000000e+00, + 9.0000000000000000e+00, + 2.0000000000000000e+00, + 5.0000000000000000e+00, + 2.0000000000000000e+00, + 1.0000000000000000e+00, + -9.0000000000000000e+00, +} + +var fmod = []float64{ + 4.197615023265299782906368e-02, + 2.261127525421895434476482e+00, + 3.231794108794261433104108e-02, + 4.989396381728925078391512e+00, + 3.637062928015826201999516e-01, + 1.220868282268106064236690e+00, + 4.770916568540693347699744e+00, + 1.816180268691969246219742e+00, + 8.734595415957246977711748e-01, + 1.314075231424398637614104e+00, +} + +type fi struct { + f float64 + i int +} + +var frexp = []fi{ + {6.2237649061045918750e-01, 3}, + {9.6735905932226306250e-01, 3}, + {-5.5376011438400318000e-01, -1}, + {-6.2632545228388436250e-01, 3}, + {6.02268356699901081250e-01, 4}, + {7.3159430981099115000e-01, 2}, + {6.5363542893241332500e-01, 3}, + {6.8198497760900255000e-01, 2}, + {9.1265404584042750000e-01, 1}, + {-5.4287029803597508250e-01, 4}, +} + +var gamma = []float64{ + 2.3254348370739963835386613898e+01, + 2.991153837155317076427529816e+03, + -4.561154336726758060575129109e+00, + 7.719403468842639065959210984e-01, + 1.6111876618855418534325755566e+05, + 1.8706575145216421164173224946e+00, + 3.4082787447257502836734201635e+01, + 1.579733951448952054898583387e+00, + 9.3834586598354592860187267089e-01, + -2.093995902923148389186189429e-05, +} + +var j0 = []float64{ + -1.8444682230601672018219338e-01, + 2.27353668906331975435892e-01, + 9.809259936157051116270273e-01, + -1.741170131426226587841181e-01, + -2.1389448451144143352039069e-01, + -2.340905848928038763337414e-01, + -1.0029099691890912094586326e-01, + -1.5466726714884328135358907e-01, + 3.252650187653420388714693e-01, + -8.72218484409407250005360235e-03, +} + +var j1 = []float64{ + -3.251526395295203422162967e-01, + 1.893581711430515718062564e-01, + -1.3711761352467242914491514e-01, + 3.287486536269617297529617e-01, + 1.3133899188830978473849215e-01, + 3.660243417832986825301766e-01, + -3.4436769271848174665420672e-01, + 4.329481396640773768835036e-01, + 5.8181350531954794639333955e-01, + -2.7030574577733036112996607e-01, +} + +var j2 = []float64{ + 5.3837518920137802565192769e-02, + -1.7841678003393207281244667e-01, + 9.521746934916464142495821e-03, + 4.28958355470987397983072e-02, + 2.4115371837854494725492872e-01, + 4.842458532394520316844449e-01, + -3.142145220618633390125946e-02, + 4.720849184745124761189957e-01, + 3.122312022520957042957497e-01, + 7.096213118930231185707277e-02, +} + +var jM3 = []float64{ + -3.684042080996403091021151e-01, + 2.8157665936340887268092661e-01, + 4.401005480841948348343589e-04, + 3.629926999056814081597135e-01, + 3.123672198825455192489266e-02, + -2.958805510589623607540455e-01, + -3.2033177696533233403289416e-01, + -2.592737332129663376736604e-01, + -1.0241334641061485092351251e-01, + -2.3762660886100206491674503e-01, +} + +var lgamma = []fi{ + {3.146492141244545774319734e+00, 1}, + {8.003414490659126375852113e+00, 1}, + {1.517575735509779707488106e+00, -1}, + {-2.588480028182145853558748e-01, 1}, + {1.1989897050205555002007985e+01, 1}, + {6.262899811091257519386906e-01, 1}, + {3.5287924899091566764846037e+00, 1}, + {4.5725644770161182299423372e-01, 1}, + {-6.363667087767961257654854e-02, 1}, + {-1.077385130910300066425564e+01, -1}, +} + +var log = []float64{ + 1.605231462693062999102599e+00, + 2.0462560018708770653153909e+00, + -1.2841708730962657801275038e+00, + 1.6115563905281545116286206e+00, + 2.2655365644872016636317461e+00, + 1.0737652208918379856272735e+00, + 1.6542360106073546632707956e+00, + 1.0035467127723465801264487e+00, + 6.0174879014578057187016475e-01, + 2.161703872847352815363655e+00, +} + +var logb = []float64{ + 2.0000000000000000e+00, + 2.0000000000000000e+00, + -2.0000000000000000e+00, + 2.0000000000000000e+00, + 3.0000000000000000e+00, + 1.0000000000000000e+00, + 2.0000000000000000e+00, + 1.0000000000000000e+00, + 0.0000000000000000e+00, + 3.0000000000000000e+00, +} + +var log10 = []float64{ + 6.9714316642508290997617083e-01, + 8.886776901739320576279124e-01, + -5.5770832400658929815908236e-01, + 6.998900476822994346229723e-01, + 9.8391002850684232013281033e-01, + 4.6633031029295153334285302e-01, + 7.1842557117242328821552533e-01, + 4.3583479968917773161304553e-01, + 2.6133617905227038228626834e-01, + 9.3881606348649405716214241e-01, +} + +var log1p = []float64{ + 4.8590257759797794104158205e-02, + 7.4540265965225865330849141e-02, + -2.7726407903942672823234024e-03, + -5.1404917651627649094953380e-02, + 9.1998280672258624681335010e-02, + 2.8843762576593352865894824e-02, + 5.0969534581863707268992645e-02, + 2.6913947602193238458458594e-02, + 1.8088493239630770262045333e-02, + -9.0865245631588989681559268e-02, +} + +var log2 = []float64{ + 2.3158594707062190618898251e+00, + 2.9521233862883917703341018e+00, + -1.8526669502700329984917062e+00, + 2.3249844127278861543568029e+00, + 3.268478366538305087466309e+00, + 1.5491157592596970278166492e+00, + 2.3865580889631732407886495e+00, + 1.447811865817085365540347e+00, + 8.6813999540425116282815557e-01, + 3.118679457227342224364709e+00, +} + +var modf = [][2]float64{ + {4.0000000000000000e+00, 9.7901192488367350108546816e-01}, + {7.0000000000000000e+00, 7.3887247457810456552351752e-01}, + {math.Copysign(0, -1), -2.7688005719200159404635997e-01}, + {-5.0000000000000000e+00, -1.060361827107492160848778e-02}, + {9.0000000000000000e+00, 6.3629370719841737980004837e-01}, + {2.0000000000000000e+00, 9.2637723924396464525443662e-01}, + {5.0000000000000000e+00, 2.2908343145930665230025625e-01}, + {2.0000000000000000e+00, 7.2793991043601025126008608e-01}, + {1.0000000000000000e+00, 8.2530809168085506044576505e-01}, + {-8.0000000000000000e+00, -6.8592476857560136238589621e-01}, +} + +var nextafter32 = []float32{ + 4.979012489318848e+00, + 7.738873004913330e+00, + -2.768800258636475e-01, + -5.010602951049805e+00, + 9.636294364929199e+00, + 2.926377534866333e+00, + 5.229084014892578e+00, + 2.727940082550049e+00, + 1.825308203697205e+00, + -8.685923576354980e+00, +} + +var nextafter64 = []float64{ + 4.97901192488367438926388786e+00, + 7.73887247457810545370193722e+00, + -2.7688005719200153853520874e-01, + -5.01060361827107403343006808e+00, + 9.63629370719841915615688777e+00, + 2.92637723924396508934364647e+00, + 5.22908343145930754047867595e+00, + 2.72793991043601069534929593e+00, + 1.82530809168085528249036997e+00, + -8.68592476857559958602905681e+00, +} + +var pow = []float64{ + 9.5282232631648411840742957e+04, + 5.4811599352999901232411871e+07, + 5.2859121715894396531132279e-01, + 9.7587991957286474464259698e-06, + 4.328064329346044846740467e+09, + 8.4406761805034547437659092e+02, + 1.6946633276191194947742146e+05, + 5.3449040147551939075312879e+02, + 6.688182138451414936380374e+01, + 2.0609869004248742886827439e-09, +} + +var remainder = []float64{ + 4.197615023265299782906368e-02, + 2.261127525421895434476482e+00, + 3.231794108794261433104108e-02, + -2.120723654214984321697556e-02, + 3.637062928015826201999516e-01, + 1.220868282268106064236690e+00, + -4.581668629186133046005125e-01, + -9.117596417440410050403443e-01, + 8.734595415957246977711748e-01, + 1.314075231424398637614104e+00, +} + +var round = []float64{ + 5, + 8, + math.Copysign(0, -1), + -5, + 10, + 3, + 5, + 3, + 2, + -9, } var signbit = []bool{ @@ -52,95 +610,3418 @@ var sin = []float64{ -6.734405869050344734943028e-01, } -var vfsignbitSC = []float64{ - Inf(-1), - Copysign(0, -1), +// Results for 100000 * math.Pi + vf[i] +var sinLarge = []float64{ + -9.646661658548936063912e-01, + 9.933822527198506903752e-01, + -2.7335587036246899796e-01, + 9.55862576853689321268e-01, + -2.099421066862688873691e-01, + 2.13557878070308981163e-01, + -8.694568970959221300497e-01, + 4.01956668098863248917e-01, + 9.67786335404528727927e-01, + -6.7344058693131973066e-01, +} + +var sinh = []float64{ + 7.2661916084208532301448439e+01, + 1.1479409110035194500526446e+03, + -2.8043136512812518927312641e-01, + -7.499429091181587232835164e+01, + 7.6552466042906758523925934e+03, + 9.3031583421672014313789064e+00, + 9.330815755828109072810322e+01, + 7.6179893137269146407361477e+00, + 3.021769180549615819524392e+00, + -2.95950575724449499189888e+03, +} + +var sqrt = []float64{ + 2.2313699659365484748756904e+00, + 2.7818829009464263511285458e+00, + 5.2619393496314796848143251e-01, + 2.2384377628763938724244104e+00, + 3.1042380236055381099288487e+00, + 1.7106657298385224403917771e+00, + 2.286718922705479046148059e+00, + 1.6516476350711159636222979e+00, + 1.3510396336454586262419247e+00, + 2.9471892997524949215723329e+00, +} + +var tan = []float64{ + -3.661316565040227801781974e+00, + 8.64900232648597589369854e+00, + -2.8417941955033612725238097e-01, + 3.253290185974728640827156e+00, + 2.147275640380293804770778e-01, + -2.18600910711067004921551e-01, + -1.760002817872367935518928e+00, + -4.389808914752818126249079e-01, + -3.843885560201130679995041e+00, + 9.10988793377685105753416e-01, +} + +// Results for 100000 * math.Pi + vf[i] +var tanLarge = []float64{ + -3.66131656475596512705e+00, + 8.6490023287202547927e+00, + -2.841794195104782406e-01, + 3.2532901861033120983e+00, + 2.14727564046880001365e-01, + -2.18600910700688062874e-01, + -1.760002817699722747043e+00, + -4.38980891453536115952e-01, + -3.84388555942723509071e+00, + 9.1098879344275101051e-01, +} + +var tanh = []float64{ + 9.9990531206936338549262119e-01, + 9.9999962057085294197613294e-01, + -2.7001505097318677233756845e-01, + -9.9991110943061718603541401e-01, + 9.9999999146798465745022007e-01, + 9.9427249436125236705001048e-01, + 9.9994257600983138572705076e-01, + 9.9149409509772875982054701e-01, + 9.4936501296239685514466577e-01, + -9.9999994291374030946055701e-01, +} + +var trunc = []float64{ + 4.0000000000000000e+00, + 7.0000000000000000e+00, + math.Copysign(0, -1), + -5.0000000000000000e+00, + 9.0000000000000000e+00, + 2.0000000000000000e+00, + 5.0000000000000000e+00, + 2.0000000000000000e+00, + 1.0000000000000000e+00, + -8.0000000000000000e+00, +} + +var y0 = []float64{ + -3.053399153780788357534855e-01, + 1.7437227649515231515503649e-01, + -8.6221781263678836910392572e-01, + -3.100664880987498407872839e-01, + 1.422200649300982280645377e-01, + 4.000004067997901144239363e-01, + -3.3340749753099352392332536e-01, + 4.5399790746668954555205502e-01, + 4.8290004112497761007536522e-01, + 2.7036697826604756229601611e-01, +} + +var y1 = []float64{ + 0.15494213737457922210218611, + -0.2165955142081145245075746, + -2.4644949631241895201032829, + 0.1442740489541836405154505, + 0.2215379960518984777080163, + 0.3038800915160754150565448, + 0.0691107642452362383808547, + 0.2380116417809914424860165, + -0.20849492979459761009678934, + 0.0242503179793232308250804, +} + +var y2 = []float64{ + 0.3675780219390303613394936, + -0.23034826393250119879267257, + -16.939677983817727205631397, + 0.367653980523052152867791, + -0.0962401471767804440353136, + -0.1923169356184851105200523, + 0.35984072054267882391843766, + -0.2794987252299739821654982, + -0.7113490692587462579757954, + -0.2647831587821263302087457, +} + +var yM3 = []float64{ + -0.14035984421094849100895341, + -0.097535139617792072703973, + 242.25775994555580176377379, + -0.1492267014802818619511046, + 0.26148702629155918694500469, + 0.56675383593895176530394248, + -0.206150264009006981070575, + 0.64784284687568332737963658, + 1.3503631555901938037008443, + 0.1461869756579956803341844, +} + +// arguments and expected results for special cases +var vfacosSC = []float64{ + -math.Pi, + 1, + math.Pi, + math.NaN(), +} + +var acosSC = []float64{ + math.NaN(), 0, - Inf(1), - NaN(), + math.NaN(), + math.NaN(), } -var signbitSC = []bool{ - true, - true, - false, - false, - false, +var vfacoshSC = []float64{ + math.Inf(-1), + 0.5, + 1, + math.Inf(1), + math.NaN(), } -var vfsinSC = []float64{ - Inf(-1), - Copysign(0, -1), +var acoshSC = []float64{ + math.NaN(), + math.NaN(), 0, - Inf(1), - NaN(), + math.Inf(1), + math.NaN(), } -var sinSC = []float64{ - NaN(), - Copysign(0, -1), +var vfasinSC = []float64{ + -math.Pi, + math.Copysign(0, -1), 0, - NaN(), - NaN(), + math.Pi, + math.NaN(), } -func tolerance(a, b, e float64) bool { - // Multiplying by e here can underflow denormal values to zero. - // Check a==b so that at least if a and b are small and identical - // we say they match. - if a == b { - return true - } - d := a - b - if d < 0 { - d = -d - } +var asinSC = []float64{ + math.NaN(), + math.Copysign(0, -1), + 0, + math.NaN(), + math.NaN(), +} - // note: b is correct (expected) value, a is actual value. - // make error tolerance a fraction of b, not a. - if b != 0 { - e = e * b - if e < 0 { - e = -e - } - } - return d < e +var vfasinhSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), } -func close(a, b float64) bool { return tolerance(a, b, 1e-14) } -func veryclose(a, b float64) bool { return tolerance(a, b, 4e-16) } -func soclose(a, b, e float64) bool { return tolerance(a, b, e) } -func alike(a, b float64) bool { - switch { - case IsNaN(a) && IsNaN(b): - return true - case a == b: - return Signbit(a) == Signbit(b) - } - return false + +var asinhSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), } -func TestSin(t *testing.T) { - for i := 0; i < len(vf); i++ { - if f := Sin(vf[i]); !veryclose(sin[i], f) { - t.Errorf("Sin(%g) = %g, want %g", vf[i], f, sin[i]) - } - } - for i := 0; i < len(vfsinSC); i++ { - if f := Sin(vfsinSC[i]); !alike(sinSC[i], f) { - t.Errorf("Sin(%g) = %g, want %g", vfsinSC[i], f, sinSC[i]) - } - } +var vfatanSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), } -func TestSignbit(t *testing.T) { - for i := 0; i < len(vf); i++ { - if f := Signbit(vf[i]); signbit[i] != f { - t.Errorf("Signbit(%g) = %t, want %t", vf[i], f, signbit[i]) - } - } - for i := 0; i < len(vfsignbitSC); i++ { - if f := Signbit(vfsignbitSC[i]); signbitSC[i] != f { - t.Errorf("Signbit(%g) = %t, want %t", vfsignbitSC[i], f, signbitSC[i]) - } +var atanSC = []float64{ + -math.Pi / 2, + math.Copysign(0, -1), + 0, + math.Pi / 2, + math.NaN(), +} + +var vfatanhSC = []float64{ + math.Inf(-1), + -math.Pi, + -1, + math.Copysign(0, -1), + 0, + 1, + math.Pi, + math.Inf(1), + math.NaN(), +} + +var atanhSC = []float64{ + math.NaN(), + math.NaN(), + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), + math.NaN(), + math.NaN(), +} + +var vfatan2SC = [][2]float64{ + {math.Inf(-1), math.Inf(-1)}, + {math.Inf(-1), -math.Pi}, + {math.Inf(-1), 0}, + {math.Inf(-1), +math.Pi}, + {math.Inf(-1), math.Inf(1)}, + {math.Inf(-1), math.NaN()}, + {-math.Pi, math.Inf(-1)}, + {-math.Pi, 0}, + {-math.Pi, math.Inf(1)}, + {-math.Pi, math.NaN()}, + {math.Copysign(0, -1), math.Inf(-1)}, + {math.Copysign(0, -1), -math.Pi}, + {math.Copysign(0, -1), math.Copysign(0, -1)}, + {math.Copysign(0, -1), 0}, + {math.Copysign(0, -1), +math.Pi}, + {math.Copysign(0, -1), math.Inf(1)}, + {math.Copysign(0, -1), math.NaN()}, + {0, math.Inf(-1)}, + {0, -math.Pi}, + {0, math.Copysign(0, -1)}, + {0, 0}, + {0, +math.Pi}, + {0, math.Inf(1)}, + {0, math.NaN()}, + {+math.Pi, math.Inf(-1)}, + {+math.Pi, 0}, + {+math.Pi, math.Inf(1)}, + {1.0, math.Inf(1)}, + {-1.0, math.Inf(1)}, + {+math.Pi, math.NaN()}, + {math.Inf(1), math.Inf(-1)}, + {math.Inf(1), -math.Pi}, + {math.Inf(1), 0}, + {math.Inf(1), +math.Pi}, + {math.Inf(1), math.Inf(1)}, + {math.Inf(1), math.NaN()}, + {math.NaN(), math.NaN()}, +} + +var atan2SC = []float64{ + // https://github.com/gnolang/gno/issues/1152 + -3.0 * math.Pi / 4, // atan2(-Inf, -Inf) + -math.Pi / 2, // atan2(-Inf, -math.Pi) + -math.Pi / 2, // atan2(-Inf, +0) + -math.Pi / 2, // atan2(-Inf, +math.Pi) + -math.Pi / 4, // atan2(-Inf, +Inf) + math.NaN(), // atan2(-Inf, NaN) + -math.Pi, // atan2(-math.Pi, -Inf) + -math.Pi / 2, // atan2(-math.Pi, +0) + math.Copysign(0, -1), // atan2(-math.Pi, Inf) + math.NaN(), // atan2(-math.Pi, NaN) + -math.Pi, // atan2(-0, -Inf) + -math.Pi, // atan2(-0, -math.Pi) + -math.Pi, // atan2(-0, -0) + math.Copysign(0, -1), // atan2(-0, +0) + math.Copysign(0, -1), // atan2(-0, +math.Pi) + math.Copysign(0, -1), // atan2(-0, +Inf) + math.NaN(), // atan2(-0, NaN) + math.Pi, // atan2(+0, -Inf) + math.Pi, // atan2(+0, -math.Pi) + math.Pi, // atan2(+0, -0) + 0, // atan2(+0, +0) + 0, // atan2(+0, +math.Pi) + 0, // atan2(+0, +Inf) + math.NaN(), // atan2(+0, NaN) + math.Pi, // atan2(+math.Pi, -Inf) + math.Pi / 2, // atan2(+math.Pi, +0) + 0, // atan2(+math.Pi, +Inf) + 0, // atan2(+1, +Inf) + math.Copysign(0, -1), // atan2(-1, +Inf) + math.NaN(), // atan2(+math.Pi, NaN) + 3 * math.Pi / 4, // atan2(+Inf, -Inf) + math.Pi / 2, // atan2(+Inf, -math.Pi) + math.Pi / 2, // atan2(+Inf, +0) + math.Pi / 2, // atan2(+Inf, +math.Pi) + math.Pi / 4, // atan2(+Inf, +Inf) + math.NaN(), // atan2(+Inf, NaN) + math.NaN(), // atan2(NaN, NaN) +} + +var vfcbrtSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), +} + +var cbrtSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), +} + +var vfceilSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), +} + +var ceilSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), +} + +var vfcopysignSC = []float64{ + math.Inf(-1), + math.Inf(1), + math.NaN(), +} + +var copysignSC = []float64{ + math.Inf(-1), + math.Inf(-1), + math.NaN(), +} + +var vfcosSC = []float64{ + math.Inf(-1), + math.Inf(1), + math.NaN(), +} + +var cosSC = []float64{ + math.NaN(), + math.NaN(), + math.NaN(), +} + +var vfcoshSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), +} + +var coshSC = []float64{ + math.Inf(1), + 1, + 1, + math.Inf(1), + math.NaN(), +} + +var vferfSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), + -1000, + 1000, +} + +var erfSC = []float64{ + -1, + math.Copysign(0, -1), + 0, + 1, + math.NaN(), + -1, + 1, +} + +var vferfcSC = []float64{ + math.Inf(-1), + math.Inf(1), + math.NaN(), + -1000, + 1000, +} + +var erfcSC = []float64{ + 2, + 0, + math.NaN(), + 2, + 0, +} + +var vferfinvSC = []float64{ + 1, + -1, + 0, + math.Inf(-1), + math.Inf(1), + math.NaN(), +} + +var erfinvSC = []float64{ + math.Inf(+1), + math.Inf(-1), + 0, + math.NaN(), + math.NaN(), + math.NaN(), +} + +var vferfcinvSC = []float64{ + 0, + 2, + 1, + math.Inf(1), + math.Inf(-1), + math.NaN(), +} + +var erfcinvSC = []float64{ + math.Inf(+1), + math.Inf(-1), + 0, + math.NaN(), + math.NaN(), + math.NaN(), +} + +var vfexpSC = []float64{ + math.Inf(-1), + -2000, + 2000, + math.Inf(1), + math.NaN(), + // smallest float64 that overflows math.Exp(x) + 7.097827128933841e+02, + // Issue 18912 + 1.48852223e+09, + 1.4885222e+09, + 1, + // near zero + 3.725290298461915e-09, + // denormal + -740, +} + +var expSC = []float64{ + 0, + 0, + math.Inf(1), + math.Inf(1), + math.NaN(), + math.Inf(1), + math.Inf(1), + math.Inf(1), + 2.718281828459045, + 1.0000000037252903, + 4.2e-322, +} + +var vfexp2SC = []float64{ + math.Inf(-1), + -2000, + 2000, + math.Inf(1), + math.NaN(), + // smallest float64 that overflows math.Exp2(x) + 1024, + // near underflow + -1.07399999999999e+03, + // near zero + 3.725290298461915e-09, +} + +var exp2SC = []float64{ + 0, + 0, + math.Inf(1), + math.Inf(1), + math.NaN(), + math.Inf(1), + 5e-324, + 1.0000000025821745, +} + +var vfexpm1SC = []float64{ + math.Inf(-1), + -710, + math.Copysign(0, -1), + 0, + 710, + math.Inf(1), + math.NaN(), +} + +var expm1SC = []float64{ + -1, + -1, + math.Copysign(0, -1), + 0, + math.Inf(1), + math.Inf(1), + math.NaN(), +} + +var vffabsSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), +} + +var fabsSC = []float64{ + math.Inf(1), + 0, + 0, + math.Inf(1), + math.NaN(), +} + +var vffdimSC = [][2]float64{ + {math.Inf(-1), math.Inf(-1)}, + {math.Inf(-1), math.Inf(1)}, + {math.Inf(-1), math.NaN()}, + {math.Copysign(0, -1), math.Copysign(0, -1)}, + {math.Copysign(0, -1), 0}, + {0, math.Copysign(0, -1)}, + {0, 0}, + {math.Inf(1), math.Inf(-1)}, + {math.Inf(1), math.Inf(1)}, + {math.Inf(1), math.NaN()}, + {math.NaN(), math.Inf(-1)}, + {math.NaN(), math.Copysign(0, -1)}, + {math.NaN(), 0}, + {math.NaN(), math.Inf(1)}, + {math.NaN(), math.NaN()}, +} + +var ( + nan = math.Float64frombits(0xFFF8000000000000) // SSE2 DIVSD 0/0 + vffdim2SC = [][2]float64{ + {math.Inf(-1), math.Inf(-1)}, + {math.Inf(-1), math.Inf(1)}, + {math.Inf(-1), nan}, + {math.Copysign(0, -1), math.Copysign(0, -1)}, + {math.Copysign(0, -1), 0}, + {0, math.Copysign(0, -1)}, + {0, 0}, + {math.Inf(1), math.Inf(-1)}, + {math.Inf(1), math.Inf(1)}, + {math.Inf(1), nan}, + {nan, math.Inf(-1)}, + {nan, math.Copysign(0, -1)}, + {nan, 0}, + {nan, math.Inf(1)}, + {nan, nan}, + } +) + +var fdimSC = []float64{ + math.NaN(), + 0, + math.NaN(), + 0, + 0, + 0, + 0, + math.Inf(1), + math.NaN(), + math.NaN(), + math.NaN(), + math.NaN(), + math.NaN(), + math.NaN(), + math.NaN(), +} + +var fmaxSC = []float64{ + math.Inf(-1), + math.Inf(1), + math.NaN(), + math.Copysign(0, -1), + 0, + 0, + 0, + math.Inf(1), + math.Inf(1), + math.Inf(1), + math.NaN(), + math.NaN(), + math.NaN(), + math.Inf(1), + math.NaN(), +} + +var fminSC = []float64{ + math.Inf(-1), + math.Inf(-1), + math.Inf(-1), + math.Copysign(0, -1), + math.Copysign(0, -1), + math.Copysign(0, -1), + 0, + math.Inf(-1), + math.Inf(1), + math.NaN(), + math.Inf(-1), + math.NaN(), + math.NaN(), + math.NaN(), + math.NaN(), +} + +var vffmodSC = [][2]float64{ + {math.Inf(-1), math.Inf(-1)}, + {math.Inf(-1), -math.Pi}, + {math.Inf(-1), 0}, + {math.Inf(-1), math.Pi}, + {math.Inf(-1), math.Inf(1)}, + {math.Inf(-1), math.NaN()}, + {-math.Pi, math.Inf(-1)}, + {-math.Pi, 0}, + {-math.Pi, math.Inf(1)}, + {-math.Pi, math.NaN()}, + {math.Copysign(0, -1), math.Inf(-1)}, + {math.Copysign(0, -1), 0}, + {math.Copysign(0, -1), math.Inf(1)}, + {math.Copysign(0, -1), math.NaN()}, + {0, math.Inf(-1)}, + {0, 0}, + {0, math.Inf(1)}, + {0, math.NaN()}, + {math.Pi, math.Inf(-1)}, + {math.Pi, 0}, + {math.Pi, math.Inf(1)}, + {math.Pi, math.NaN()}, + {math.Inf(1), math.Inf(-1)}, + {math.Inf(1), -math.Pi}, + {math.Inf(1), 0}, + {math.Inf(1), math.Pi}, + {math.Inf(1), math.Inf(1)}, + {math.Inf(1), math.NaN()}, + {math.NaN(), math.Inf(-1)}, + {math.NaN(), -math.Pi}, + {math.NaN(), 0}, + {math.NaN(), math.Pi}, + {math.NaN(), math.Inf(1)}, + {math.NaN(), math.NaN()}, +} + +var fmodSC = []float64{ + math.NaN(), // fmod(-Inf, -Inf) + math.NaN(), // fmod(-Inf, -math.Pi) + math.NaN(), // fmod(-Inf, 0) + math.NaN(), // fmod(-Inf, math.Pi) + math.NaN(), // fmod(-Inf, +Inf) + math.NaN(), // fmod(-Inf, NaN) + -math.Pi, // fmod(-math.Pi, -Inf) + math.NaN(), // fmod(-math.Pi, 0) + -math.Pi, // fmod(-math.Pi, +Inf) + math.NaN(), // fmod(-math.Pi, NaN) + math.Copysign(0, -1), // fmod(-0, -Inf) + math.NaN(), // fmod(-0, 0) + math.Copysign(0, -1), // fmod(-0, Inf) + math.NaN(), // fmod(-0, NaN) + 0, // fmod(0, -Inf) + math.NaN(), // fmod(0, 0) + 0, // fmod(0, +Inf) + math.NaN(), // fmod(0, NaN) + math.Pi, // fmod(math.Pi, -Inf) + math.NaN(), // fmod(math.Pi, 0) + math.Pi, // fmod(math.Pi, +Inf) + math.NaN(), // fmod(math.Pi, NaN) + math.NaN(), // fmod(+Inf, -Inf) + math.NaN(), // fmod(+Inf, -math.Pi) + math.NaN(), // fmod(+Inf, 0) + math.NaN(), // fmod(+Inf, math.Pi) + math.NaN(), // fmod(+Inf, +Inf) + math.NaN(), // fmod(+Inf, NaN) + math.NaN(), // fmod(NaN, -Inf) + math.NaN(), // fmod(NaN, -math.Pi) + math.NaN(), // fmod(NaN, 0) + math.NaN(), // fmod(NaN, math.Pi) + math.NaN(), // fmod(NaN, +Inf) + math.NaN(), // fmod(NaN, NaN) +} + +var vffrexpSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), +} + +var frexpSC = []fi{ + {math.Inf(-1), 0}, + {math.Copysign(0, -1), 0}, + {0, 0}, + {math.Inf(1), 0}, + {math.NaN(), 0}, +} + +var vfgamma = [][2]float64{ + {math.Inf(1), math.Inf(1)}, + {math.Inf(-1), math.NaN()}, + {0, math.Inf(1)}, + {math.Copysign(0, -1), math.Inf(-1)}, + {math.NaN(), math.NaN()}, + {-1, math.NaN()}, + {-2, math.NaN()}, + {-3, math.NaN()}, + {-1e16, math.NaN()}, + {-1e300, math.NaN()}, + {1.7e308, math.Inf(1)}, + + // Test inputs inspired by Python test suite. + // Outputs computed at high precision by PARI/GP. + // If recomputing table entries, be careful to use + // high-precision (%.1000g) formatting of the float64 inputs. + // For example, -2.0000000000000004 is the float64 with exact value + // -2.00000000000000044408920985626161695, and + // gamma(-2.0000000000000004) = -1249999999999999.5386078562728167651513, while + // gamma(-2.00000000000000044408920985626161695) = -1125899906826907.2044875028130093136826. + // Thus the table lists -1.1258999068426235e+15 as the answer. + {0.5, 1.772453850905516}, + {1.5, 0.886226925452758}, + {2.5, 1.329340388179137}, + {3.5, 3.3233509704478426}, + {-0.5, -3.544907701811032}, + {-1.5, 2.363271801207355}, + {-2.5, -0.9453087204829419}, + {-3.5, 0.2700882058522691}, + {0.1, 9.51350769866873}, + {0.01, 99.4325851191506}, + {1e-08, 9.999999942278434e+07}, + {1e-16, 1e+16}, + {0.001, 999.4237724845955}, + {1e-16, 1e+16}, + {1e-308, 1e+308}, + {5.6e-309, 1.7857142857142864e+308}, + {5.5e-309, math.Inf(1)}, + {1e-309, math.Inf(1)}, + {1e-323, math.Inf(1)}, + {5e-324, math.Inf(1)}, + {-0.1, -10.686287021193193}, + {-0.01, -100.58719796441078}, + {-1e-08, -1.0000000057721567e+08}, + {-1e-16, -1e+16}, + {-0.001, -1000.5782056293586}, + {-1e-16, -1e+16}, + {-1e-308, -1e+308}, + {-5.6e-309, -1.7857142857142864e+308}, + {-5.5e-309, math.Inf(-1)}, + {-1e-309, math.Inf(-1)}, + {-1e-323, math.Inf(-1)}, + {-5e-324, math.Inf(-1)}, + {-0.9999999999999999, -9.007199254740992e+15}, + {-1.0000000000000002, 4.5035996273704955e+15}, + {-1.9999999999999998, 2.2517998136852485e+15}, + {-2.0000000000000004, -1.1258999068426235e+15}, + {-100.00000000000001, -7.540083334883109e-145}, + {-99.99999999999999, 7.540083334884096e-145}, + {17, 2.0922789888e+13}, + {171, 7.257415615307999e+306}, + {171.6, 1.5858969096672565e+308}, + {171.624, 1.7942117599248104e+308}, + {171.625, math.Inf(1)}, + {172, math.Inf(1)}, + {2000, math.Inf(1)}, + {-100.5, -3.3536908198076787e-159}, + {-160.5, -5.255546447007829e-286}, + {-170.5, -3.3127395215386074e-308}, + {-171.5, 1.9316265431712e-310}, + {-176.5, -1.196e-321}, + {-177.5, 5e-324}, + {-178.5, math.Copysign(0, -1)}, + {-179.5, 0}, + {-201.0001, 0}, + {-202.9999, math.Copysign(0, -1)}, + {-1000.5, math.Copysign(0, -1)}, + {-1.0000000003e+09, math.Copysign(0, -1)}, + {-4.5035996273704955e+15, 0}, + {-63.349078729022985, 4.177797167776188e-88}, + {-127.45117632943295, 1.183111089623681e-214}, +} + +var vfhypotSC = [][2]float64{ + {math.Inf(-1), math.Inf(-1)}, + {math.Inf(-1), 0}, + {math.Inf(-1), math.Inf(1)}, + {math.Inf(-1), math.NaN()}, + {math.Copysign(0, -1), math.Copysign(0, -1)}, + {math.Copysign(0, -1), 0}, + {0, math.Copysign(0, -1)}, + {0, 0}, // +0, +0 + {0, math.Inf(-1)}, + {0, math.Inf(1)}, + {0, math.NaN()}, + {math.Inf(1), math.Inf(-1)}, + {math.Inf(1), 0}, + {math.Inf(1), math.Inf(1)}, + {math.Inf(1), math.NaN()}, + {math.NaN(), math.Inf(-1)}, + {math.NaN(), 0}, + {math.NaN(), math.Inf(1)}, + {math.NaN(), math.NaN()}, +} + +var hypotSC = []float64{ + math.Inf(1), + math.Inf(1), + math.Inf(1), + math.Inf(1), + 0, + 0, + 0, + 0, + math.Inf(1), + math.Inf(1), + math.NaN(), + math.Inf(1), + math.Inf(1), + math.Inf(1), + math.Inf(1), + math.Inf(1), + math.NaN(), + math.Inf(1), + math.NaN(), +} + +var ilogbSC = []int{ + math.MaxInt32, + math.MinInt32, + math.MaxInt32, + math.MaxInt32, +} + +var vfj0SC = []float64{ + math.Inf(-1), + 0, + math.Inf(1), + math.NaN(), +} + +var j0SC = []float64{ + 0, + 1, + 0, + math.NaN(), +} + +var j1SC = []float64{ + 0, + 0, + 0, + math.NaN(), +} + +var j2SC = []float64{ + 0, + 0, + 0, + math.NaN(), +} + +var jM3SC = []float64{ + 0, + 0, + 0, + math.NaN(), +} + +var vfldexpSC = []fi{ + {0, 0}, + {0, -1075}, + {0, 1024}, + {math.Copysign(0, -1), 0}, + {math.Copysign(0, -1), -1075}, + {math.Copysign(0, -1), 1024}, + {math.Inf(1), 0}, + {math.Inf(1), -1024}, + {math.Inf(-1), 0}, + {math.Inf(-1), -1024}, + {math.NaN(), -1024}, + // XXX: changed to use constant values instead of unsafe.Sizeof(0) + {10, int(1) << (uint64(7) * 8)}, + {10, -(int(1) << (uint64(7) * 8))}, +} + +var ldexpSC = []float64{ + 0, + 0, + 0, + math.Copysign(0, -1), + math.Copysign(0, -1), + math.Copysign(0, -1), + math.Inf(1), + math.Inf(1), + math.Inf(-1), + math.Inf(-1), + math.NaN(), + math.Inf(1), + 0, +} + +var vflgammaSC = []float64{ + math.Inf(-1), + -3, + 0, + 1, + 2, + math.Inf(1), + math.NaN(), +} + +var lgammaSC = []fi{ + {math.Inf(-1), 1}, + {math.Inf(1), 1}, + {math.Inf(1), 1}, + {0, 1}, + {0, 1}, + {math.Inf(1), 1}, + {math.NaN(), 1}, +} + +var vflogSC = []float64{ + math.Inf(-1), + -math.Pi, + math.Copysign(0, -1), + 0, + 1, + math.Inf(1), + math.NaN(), +} + +var logSC = []float64{ + math.NaN(), + math.NaN(), + math.Inf(-1), + math.Inf(-1), + 0, + math.Inf(1), + math.NaN(), +} + +var vflogbSC = []float64{ + math.Inf(-1), + 0, + math.Inf(1), + math.NaN(), +} + +var logbSC = []float64{ + math.Inf(1), + math.Inf(-1), + math.Inf(1), + math.NaN(), +} + +var vflog1pSC = []float64{ + math.Inf(-1), + -math.Pi, + -1, + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), + 4503599627370496.5, // Issue #29488 +} + +var log1pSC = []float64{ + math.NaN(), + math.NaN(), + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), + 36.04365338911715, // Issue #29488 +} + +var vfmodfSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + math.Inf(1), + math.NaN(), +} + +var modfSC = [][2]float64{ + {math.Inf(-1), math.NaN()}, // [2]float64{math.Copysign(0, -1), math.Inf(-1)}, + {math.Copysign(0, -1), math.Copysign(0, -1)}, + {math.Inf(1), math.NaN()}, // [2]float64{0, math.Inf(1)}, + {math.NaN(), math.NaN()}, +} + +var vfnextafter32SC = [][2]float32{ + {0, 0}, + {0, float32(math.Copysign(0, -1))}, + {0, -1}, + {0, float32(math.NaN())}, + {float32(math.Copysign(0, -1)), 1}, + {float32(math.Copysign(0, -1)), 0}, + {float32(math.Copysign(0, -1)), float32(math.Copysign(0, -1))}, + {float32(math.Copysign(0, -1)), -1}, + {float32(math.NaN()), 0}, + {float32(math.NaN()), float32(math.NaN())}, +} + +var nextafter32SC = []float32{ + 0, + 0, + -1.401298464e-45, // math.Float32frombits(0x80000001) + float32(math.NaN()), + 1.401298464e-45, // math.Float32frombits(0x00000001) + float32(math.Copysign(0, -1)), + float32(math.Copysign(0, -1)), + -1.401298464e-45, // math.Float32frombits(0x80000001) + float32(math.NaN()), + float32(math.NaN()), +} + +var vfnextafter64SC = [][2]float64{ + {0, 0}, + {0, math.Copysign(0, -1)}, + {0, -1}, + {0, math.NaN()}, + {math.Copysign(0, -1), 1}, + {math.Copysign(0, -1), 0}, + {math.Copysign(0, -1), math.Copysign(0, -1)}, + {math.Copysign(0, -1), -1}, + {math.NaN(), 0}, + {math.NaN(), math.NaN()}, +} + +var nextafter64SC = []float64{ + 0, + 0, + -4.9406564584124654418e-324, // math.Float64frombits(0x8000000000000001) + math.NaN(), + 4.9406564584124654418e-324, // math.Float64frombits(0x0000000000000001) + math.Copysign(0, -1), + math.Copysign(0, -1), + -4.9406564584124654418e-324, // math.Float64frombits(0x8000000000000001) + math.NaN(), + math.NaN(), +} + +var vfpowSC = [][2]float64{ + {math.Inf(-1), -math.Pi}, + {math.Inf(-1), -3}, + {math.Inf(-1), math.Copysign(0, -1)}, + {math.Inf(-1), 0}, + {math.Inf(-1), 1}, + {math.Inf(-1), 3}, + {math.Inf(-1), math.Pi}, + {math.Inf(-1), 0.5}, + {math.Inf(-1), math.NaN()}, + + {-math.Pi, math.Inf(-1)}, + {-math.Pi, -math.Pi}, + {-math.Pi, math.Copysign(0, -1)}, + {-math.Pi, 0}, + {-math.Pi, 1}, + {-math.Pi, math.Pi}, + {-math.Pi, math.Inf(1)}, + {-math.Pi, math.NaN()}, + + {-1, math.Inf(-1)}, + {-1, math.Inf(1)}, + {-1, math.NaN()}, + {-0.5, math.Inf(-1)}, + {-0.5, math.Inf(1)}, + {math.Copysign(0, -1), math.Inf(-1)}, + {math.Copysign(0, -1), -math.Pi}, + {math.Copysign(0, -1), -0.5}, + {math.Copysign(0, -1), -3}, + {math.Copysign(0, -1), 3}, + {math.Copysign(0, -1), math.Pi}, + {math.Copysign(0, -1), 0.5}, + {math.Copysign(0, -1), math.Inf(1)}, + + {0, math.Inf(-1)}, + {0, -math.Pi}, + {0, -3}, + {0, math.Copysign(0, -1)}, + {0, 0}, + {0, 3}, + {0, math.Pi}, + {0, math.Inf(1)}, + {0, math.NaN()}, + + {0.5, math.Inf(-1)}, + {0.5, math.Inf(1)}, + {1, math.Inf(-1)}, + {1, math.Inf(1)}, + {1, math.NaN()}, + + {math.Pi, math.Inf(-1)}, + {math.Pi, math.Copysign(0, -1)}, + {math.Pi, 0}, + {math.Pi, 1}, + {math.Pi, math.Inf(1)}, + {math.Pi, math.NaN()}, + {math.Inf(1), -math.Pi}, + {math.Inf(1), math.Copysign(0, -1)}, + {math.Inf(1), 0}, + {math.Inf(1), 1}, + {math.Inf(1), math.Pi}, + {math.Inf(1), math.NaN()}, + {math.NaN(), -math.Pi}, + {math.NaN(), math.Copysign(0, -1)}, + {math.NaN(), 0}, + {math.NaN(), 1}, + {math.NaN(), math.Pi}, + {math.NaN(), math.NaN()}, + + // Issue #7394 overflow checks + {2, float64(1 << 32)}, + {2, -float64(1 << 32)}, + {-2, float64(1<<32 + 1)}, + {0.5, float64(1 << 45)}, + {0.5, -float64(1 << 45)}, + // https://github.com/gnolang/gno/issues/1085 + {math.Nextafter(1, 2), float1Shift63}, + {math.Nextafter(1, -2), float1Shift63}, + {math.Nextafter(-1, 2), float1Shift63}, + {math.Nextafter(-1, -2), float1Shift63}, + + // Issue #57465 + {math.Copysign(0, -1), 1e19}, + {math.Copysign(0, -1), -1e19}, + {math.Copysign(0, -1), 1<<53 - 1}, + {math.Copysign(0, -1), -(1<<53 - 1)}, +} + +const float1Shift63 float64 = 1 << 63 + +var powSC = []float64{ + 0, // pow(-Inf, -math.Pi) + math.Copysign(0, -1), // pow(-Inf, -3) + 1, // pow(-Inf, -0) + 1, // pow(-Inf, +0) + math.Inf(-1), // pow(-Inf, 1) + math.Inf(-1), // pow(-Inf, 3) + math.Inf(1), // pow(-Inf, math.Pi) + math.Inf(1), // pow(-Inf, 0.5) + math.NaN(), // pow(-Inf, NaN) + 0, // pow(-math.Pi, -Inf) + math.NaN(), // pow(-math.Pi, -math.Pi) + 1, // pow(-math.Pi, -0) + 1, // pow(-math.Pi, +0) + -math.Pi, // pow(-math.Pi, 1) + math.NaN(), // pow(-math.Pi, math.Pi) + math.Inf(1), // pow(-math.Pi, +Inf) + math.NaN(), // pow(-math.Pi, NaN) + 1, // pow(-1, -Inf) IEEE 754-2008 + 1, // pow(-1, +Inf) IEEE 754-2008 + math.NaN(), // pow(-1, NaN) + math.Inf(1), // pow(-1/2, -Inf) + 0, // pow(-1/2, +Inf) + math.Inf(1), // pow(-0, -Inf) + math.Inf(1), // pow(-0, -math.Pi) + math.Inf(1), // pow(-0, -0.5) + math.Inf(-1), // pow(-0, -3) IEEE 754-2008 + math.Copysign(0, -1), // pow(-0, 3) IEEE 754-2008 + 0, // pow(-0, +math.Pi) + 0, // pow(-0, 0.5) + 0, // pow(-0, +Inf) + math.Inf(1), // pow(+0, -Inf) + math.Inf(1), // pow(+0, -math.Pi) + math.Inf(1), // pow(+0, -3) + 1, // pow(+0, -0) + 1, // pow(+0, +0) + 0, // pow(+0, 3) + 0, // pow(+0, +math.Pi) + 0, // pow(+0, +Inf) + math.NaN(), // pow(+0, NaN) + math.Inf(1), // pow(1/2, -Inf) + 0, // pow(1/2, +Inf) + 1, // pow(1, -Inf) IEEE 754-2008 + 1, // pow(1, +Inf) IEEE 754-2008 + 1, // pow(1, NaN) IEEE 754-2008 + 0, // pow(+math.Pi, -Inf) + 1, // pow(+math.Pi, -0) + 1, // pow(+math.Pi, +0) + math.Pi, // pow(+math.Pi, 1) + math.Inf(1), // pow(+math.Pi, +Inf) + math.NaN(), // pow(+math.Pi, NaN) + 0, // pow(+Inf, -math.Pi) + 1, // pow(+Inf, -0) + 1, // pow(+Inf, +0) + math.Inf(1), // pow(+Inf, 1) + math.Inf(1), // pow(+Inf, math.Pi) + math.NaN(), // pow(+Inf, NaN) + math.NaN(), // pow(NaN, -math.Pi) + 1, // pow(NaN, -0) + 1, // pow(NaN, +0) + math.NaN(), // pow(NaN, 1) + math.NaN(), // pow(NaN, +math.Pi) + math.NaN(), // pow(NaN, NaN) + + // Issue #7394 overflow checks + math.Inf(1), // pow(2, float64(1 << 32)) + 0, // pow(2, -float64(1 << 32)) + math.Inf(-1), // pow(-2, float64(1<<32 + 1)) + 0, // pow(1/2, float64(1 << 45)) + math.Inf(1), // pow(1/2, -float64(1 << 45)) + math.Inf(1), // pow(math.Nextafter(1, 2), float64(1 << 63)) + 0, // pow(math.Nextafter(1, -2), float64(1 << 63)) + 0, // pow(math.Nextafter(-1, 2), float64(1 << 63)) + math.Inf(1), // pow(math.Nextafter(-1, -2), float64(1 << 63)) + + // Issue #57465 + 0, // pow(-0, 1e19) + math.Inf(1), // pow(-0, -1e19) + math.Copysign(0, -1), // pow(-0, 1<<53 -1) + math.Inf(-1), // pow(-0, -(1<<53 -1)) +} + +var vfpow10SC = []int{ + math.MinInt32, + -324, + -323, + -50, + -22, + -1, + 0, + 1, + 22, + 50, + 100, + 200, + 308, + 309, + math.MaxInt32, +} + +var pow10SC = []float64{ + 0, // pow10(math.MinInt32) + 0, // pow10(-324) + 1.0e-323, // pow10(-323) + 1.0e-50, // pow10(-50) + 1.0e-22, // pow10(-22) + 1.0e-1, // pow10(-1) + 1.0e0, // pow10(0) + 1.0e1, // pow10(1) + 1.0e22, // pow10(22) + 1.0e50, // pow10(50) + 1.0e100, // pow10(100) + 1.0e200, // pow10(200) + 1.0e308, // pow10(308) + math.Inf(1), // pow10(309) + math.Inf(1), // pow10(math.MaxInt32) +} + +var vfroundSC = [][2]float64{ + {0, 0}, + {1.390671161567e-309, 0}, // denormal + {0.49999999999999994, 0}, // 0.5-epsilon + {0.5, 1}, + {0.5000000000000001, 1}, // 0.5+epsilon + {-1.5, -2}, + {-2.5, -3}, + {math.NaN(), math.NaN()}, + {math.Inf(1), math.Inf(1)}, + {2251799813685249.5, 2251799813685250}, // 1 bit fraction + {2251799813685250.5, 2251799813685251}, + {4503599627370495.5, 4503599627370496}, // 1 bit fraction, rounding to 0 bit fraction + {4503599627370497, 4503599627370497}, // large integer +} + +var vfroundEvenSC = [][2]float64{ + {0, 0}, + {1.390671161567e-309, 0}, // denormal + {0.49999999999999994, 0}, // 0.5-epsilon + {0.5, 0}, + {0.5000000000000001, 1}, // 0.5+epsilon + {-1.5, -2}, + {-2.5, -2}, + {math.NaN(), math.NaN()}, + {math.Inf(1), math.Inf(1)}, + {2251799813685249.5, 2251799813685250}, // 1 bit fraction + {2251799813685250.5, 2251799813685250}, + {4503599627370495.5, 4503599627370496}, // 1 bit fraction, rounding to 0 bit fraction + {4503599627370497, 4503599627370497}, // large integer +} + +var vfsignbitSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), +} + +var signbitSC = []bool{ + true, + true, + false, + false, + false, +} + +var vfsinSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), +} + +var sinSC = []float64{ + math.NaN(), + math.Copysign(0, -1), + 0, + math.NaN(), + math.NaN(), +} + +var vfsinhSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), +} + +var sinhSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), +} + +var vfsqrtSC = []float64{ + math.Inf(-1), + -math.Pi, + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), + math.Float64frombits(2), // subnormal; see https://golang.org/issue/13013 +} + +var sqrtSC = []float64{ + math.NaN(), + math.NaN(), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), + 3.1434555694052576e-162, +} + +var vftanhSC = []float64{ + math.Inf(-1), + math.Copysign(0, -1), + 0, + math.Inf(1), + math.NaN(), +} + +var tanhSC = []float64{ + -1, + math.Copysign(0, -1), + 0, + 1, + math.NaN(), +} + +var vfy0SC = []float64{ + math.Inf(-1), + 0, + math.Inf(1), + math.NaN(), + -1, +} + +var y0SC = []float64{ + math.NaN(), + math.Inf(-1), + 0, + math.NaN(), + math.NaN(), +} + +var y1SC = []float64{ + math.NaN(), + math.Inf(-1), + 0, + math.NaN(), + math.NaN(), +} + +var y2SC = []float64{ + math.NaN(), + math.Inf(-1), + 0, + math.NaN(), + math.NaN(), +} + +var yM3SC = []float64{ + math.NaN(), + math.Inf(1), + 0, + math.NaN(), + math.NaN(), +} + +// arguments and expected results for boundary cases +const ( + SmallestNormalFloat64 = 2.2250738585072014e-308 // 2**-1022 + LargestSubnormalFloat64 = SmallestNormalFloat64 - math.SmallestNonzeroFloat64 +) + +var vffrexpBC = []float64{ + SmallestNormalFloat64, + LargestSubnormalFloat64, + math.SmallestNonzeroFloat64, + math.MaxFloat64, + -SmallestNormalFloat64, + -LargestSubnormalFloat64, + -math.SmallestNonzeroFloat64, + -math.MaxFloat64, +} + +var frexpBC = []fi{ + {0.5, -1021}, + {0.99999999999999978, -1022}, + {0.5, -1073}, + {0.99999999999999989, 1024}, + {-0.5, -1021}, + {-0.99999999999999978, -1022}, + {-0.5, -1073}, + {-0.99999999999999989, 1024}, +} + +var vfldexpBC = []fi{ + {SmallestNormalFloat64, -52}, + {LargestSubnormalFloat64, -51}, + {math.SmallestNonzeroFloat64, 1074}, + {math.MaxFloat64, -(1023 + 1074)}, + {1, -1075}, + {-1, -1075}, + {1, 1024}, + {-1, 1024}, + {1.0000000000000002, -1075}, + {1, -1075}, +} + +var ldexpBC = []float64{ + math.SmallestNonzeroFloat64, + 1e-323, // 2**-1073 + 1, + 1e-323, // 2**-1073 + 0, + math.Copysign(0, -1), + math.Inf(1), + math.Inf(-1), + math.SmallestNonzeroFloat64, + 0, +} + +var logbBC = []float64{ + -1022, + -1023, + -1074, + 1023, + -1022, + -1023, + -1074, + 1023, +} + +// Test cases were generated with Berkeley TestFloat-3e/testfloat_gen. +// http://www.jhauser.us/arithmetic/TestFloat.html. +// The default rounding mode is selected (nearest/even), and exception flags are ignored. +var fmaC = []struct{ x, y, z, want float64 }{ + // Large exponent spread + {-3.999999999999087, -1.1123914289620494e-16, -7.999877929687506, -7.999877929687505}, + {-262112.0000004768, -0.06251525855623184, 1.1102230248837136e-16, 16385.99945072085}, + {-6.462348523533467e-27, -2.3763644720331857e-211, 4.000000000931324, 4.000000000931324}, + + // Effective addition + {-2.0000000037252907, 6.7904383376e-313, -3.3951933161e-313, -1.697607001654e-312}, + {-0.12499999999999999, 512.007568359375, -1.4193627164960366e-16, -64.00094604492188}, + {-2.7550648847397148e-39, -3.4028301595800694e+38, 0.9960937495343386, 1.9335955376735676}, + {5.723369164769208e+24, 3.8149300927159385e-06, 1.84489958778182e+19, 4.028324913621874e+19}, + {-0.4843749999990904, -3.6893487872543293e+19, 9.223653786709391e+18, 2.7093936974938993e+19}, + {-3.8146972665201165e-06, 4.2949672959999385e+09, -2.2204460489938386e-16, -16384.000003844263}, + {6.98156394130982e-309, -1.1072962560000002e+09, -4.4414561548793455e-308, -7.73065965765153e-300}, + + // Effective subtraction + {5e-324, 4.5, -2e-323, 0}, + {5e-324, 7, -3.5e-323, 0}, + {5e-324, 0.5000000000000001, -5e-324, math.Copysign(0, -1)}, + {-2.1240680525e-314, -1.233647078189316e+308, -0.25781249999954525, -0.25780987964919844}, + {8.579992955364441e-308, 0.6037391876780558, -4.4501307410480706e-308, 7.29947236107098e-309}, + {-4.450143471986689e-308, -0.9960937499927239, -4.450419332475649e-308, -1.7659233458788e-310}, + {1.4932076393918112, -2.2248022430460833e-308, 4.449875571054211e-308, 1.127783865601762e-308}, + + // Overflow + {-2.288020632214759e+38, -8.98846570988901e+307, 1.7696041796300924e+308, math.Inf(0)}, + {1.4888652783208255e+308, -9.007199254742012e+15, -6.807282911929205e+38, math.Inf(-1)}, + {9.142703268902826e+192, -1.3504889569802838e+296, -1.9082200803806996e-89, math.Inf(-1)}, + + // Finite x and y, but non-finite z. + {31.99218749627471, -1.7976930544991702e+308, math.Inf(0), math.Inf(0)}, + {-1.7976931281784667e+308, -2.0009765625002265, math.Inf(-1), math.Inf(-1)}, + + // Special + {0, 0, 0, 0}, + {math.Copysign(0, -1), 0, 0, 0}, + {0, 0, math.Copysign(0, -1), 0}, + {math.Copysign(0, -1), 0, math.Copysign(0, -1), math.Copysign(0, -1)}, + {-1.1754226043408471e-38, math.NaN(), math.Inf(0), math.NaN()}, + {0, 0, 2.22507385643494e-308, 2.22507385643494e-308}, + {-8.65697792e+09, math.NaN(), -7.516192799999999e+09, math.NaN()}, + {-0.00012207403779029757, 3.221225471996093e+09, math.NaN(), math.NaN()}, + {math.Inf(-1), 0.1252441407414153, -1.387184532981584e-76, math.Inf(-1)}, + {math.Inf(0), 1.525878907671432e-05, -9.214364835452549e+18, math.Inf(0)}, + + // Random + {0.1777916152213626, -32.000015266239636, -2.2204459148334633e-16, -5.689334401293007}, + {-2.0816681711722314e-16, -0.4997558592585846, -0.9465627129124969, -0.9465627129124968}, + {-1.9999997615814211, 1.8518819259933516e+19, 16.874999999999996, -3.703763410463646e+19}, + {-0.12499994039717421, 32767.99999976135, -2.0752587082923246e+19, -2.075258708292325e+19}, + {7.705600568510257e-34, -1.801432979000528e+16, -0.17224197722973714, -0.17224197722973716}, + {3.8988133103758913e-308, -0.9848632812499999, 3.893879244098556e-308, 5.40811742605814e-310}, + {-0.012651981190687427, 6.911985574912436e+38, 6.669240527007144e+18, -8.745031148409496e+36}, + {4.612811918325842e+18, 1.4901161193847641e-08, 2.6077032311277997e-08, 6.873625395187494e+10}, + {-9.094947033611148e-13, 4.450691014249257e-308, 2.086006742350485e-308, 2.086006742346437e-308}, + {-7.751454006381804e-05, 5.588653777189071e-308, -2.2207280111272877e-308, -2.2211612130544025e-308}, + + // Issue #61130 + {-1, 1, 1, 0}, + {1, 1, -1, 0}, +} + +var sqrt32 = []float32{ + 0, + float32(math.Copysign(0, -1)), + float32(math.NaN()), + float32(math.Inf(1)), + float32(math.Inf(-1)), + 1, + 2, + -2, + 4.9790119248836735e+00, + 7.7388724745781045e+00, + -2.7688005719200159e-01, + -5.0106036182710749e+00, +} + +func tolerance(a, b, e float64) bool { + // Multiplying by e here can underflow denormal values to zero. + // Check a==b so that at least if a and b are small and identical + // we say they match. + if a == b { + return true + } + d := a - b + if d < 0 { + d = -d + } + + // note: b is correct (expected) value, a is actual value. + // make error tolerance a fraction of b, not a. + if b != 0 { + e = e * b + if e < 0 { + e = -e + } + } + return d < e +} +func close(a, b float64) bool { return tolerance(a, b, 1e-14) } +func veryclose(a, b float64) bool { return tolerance(a, b, 4e-16) } +func soclose(a, b, e float64) bool { return tolerance(a, b, e) } +func alike(a, b float64) bool { + switch { + case math.IsNaN(a) && math.IsNaN(b): + return true + case a == b: + return math.Signbit(a) == math.Signbit(b) + } + return false +} + +func TestNaN(t *testing.T) { + f64 := math.NaN() + if f64 == f64 { + t.Fatalf("math.NaN() returns %g, expected NaN", f64) + } + f32 := float32(f64) + if f32 == f32 { + t.Fatalf("float32(math.NaN()) is %g, expected NaN", f32) + } +} + +func TestAcos(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := vf[i] / 10 + if f := math.Acos(a); !close(acos[i], f) { + t.Errorf("math.Acos(%g) = %g, want %g", a, f, acos[i]) + } + } + for i := 0; i < len(vfacosSC); i++ { + if f := math.Acos(vfacosSC[i]); !alike(acosSC[i], f) { + t.Errorf("math.Acos(%g) = %g, want %g", vfacosSC[i], f, acosSC[i]) + } + } +} + +func TestAcosh(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := 1 + math.Abs(vf[i]) + if f := math.Acosh(a); !veryclose(acosh[i], f) { + t.Errorf("math.Acosh(%g) = %g, want %g", a, f, acosh[i]) + } + } + for i := 0; i < len(vfacoshSC); i++ { + if f := math.Acosh(vfacoshSC[i]); !alike(acoshSC[i], f) { + t.Errorf("math.Acosh(%g) = %g, want %g", vfacoshSC[i], f, acoshSC[i]) + } + } +} + +func TestAsin(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := vf[i] / 10 + if f := math.Asin(a); !veryclose(asin[i], f) { + t.Errorf("math.Asin(%g) = %g, want %g", a, f, asin[i]) + } + } + for i := 0; i < len(vfasinSC); i++ { + if f := math.Asin(vfasinSC[i]); !alike(asinSC[i], f) { + t.Errorf("math.Asin(%g) = %g, want %g", vfasinSC[i], f, asinSC[i]) + } + } +} + +func TestAsinh(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Asinh(vf[i]); !veryclose(asinh[i], f) { + t.Errorf("math.Asinh(%g) = %g, want %g", vf[i], f, asinh[i]) + } + } + for i := 0; i < len(vfasinhSC); i++ { + if f := math.Asinh(vfasinhSC[i]); !alike(asinhSC[i], f) { + t.Errorf("math.Asinh(%g) = %g, want %g", vfasinhSC[i], f, asinhSC[i]) + } + } +} + +func TestAtan(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Atan(vf[i]); !veryclose(atan[i], f) { + t.Errorf("math.Atan(%g) = %g, want %g", vf[i], f, atan[i]) + } + } + for i := 0; i < len(vfatanSC); i++ { + if f := math.Atan(vfatanSC[i]); !alike(atanSC[i], f) { + t.Errorf("math.Atan(%g) = %g, want %g", vfatanSC[i], f, atanSC[i]) + } + } +} + +func TestAtanh(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := vf[i] / 10 + if f := math.Atanh(a); !veryclose(atanh[i], f) { + t.Errorf("math.Atanh(%g) = %g, want %g", a, f, atanh[i]) + } + } + for i := 0; i < len(vfatanhSC); i++ { + if f := math.Atanh(vfatanhSC[i]); !alike(atanhSC[i], f) { + t.Errorf("math.Atanh(%g) = %g, want %g", vfatanhSC[i], f, atanhSC[i]) + } + } +} + +func TestAtan2(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Atan2(10, vf[i]); !veryclose(atan2[i], f) { + t.Errorf("math.Atan2(10, %g) = %g, want %g", vf[i], f, atan2[i]) + } + } + for i := 0; i < len(vfatan2SC); i++ { + if f := math.Atan2(vfatan2SC[i][0], vfatan2SC[i][1]); !alike(atan2SC[i], f) { + t.Errorf("math.Atan2(%g, %g) = %g, want %g", vfatan2SC[i][0], vfatan2SC[i][1], f, atan2SC[i]) + } + } +} + +func TestCbrt(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Cbrt(vf[i]); !veryclose(cbrt[i], f) { + t.Errorf("math.Cbrt(%g) = %g, want %g", vf[i], f, cbrt[i]) + } + } + for i := 0; i < len(vfcbrtSC); i++ { + if f := math.Cbrt(vfcbrtSC[i]); !alike(cbrtSC[i], f) { + t.Errorf("math.Cbrt(%g) = %g, want %g", vfcbrtSC[i], f, cbrtSC[i]) + } + } +} + +func TestCeil(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Ceil(vf[i]); !alike(ceil[i], f) { + t.Errorf("math.Ceil(%g) = %g, want %g", vf[i], f, ceil[i]) + } + } + for i := 0; i < len(vfceilSC); i++ { + if f := math.Ceil(vfceilSC[i]); !alike(ceilSC[i], f) { + t.Errorf("math.Ceil(%g) = %g, want %g", vfceilSC[i], f, ceilSC[i]) + } + } +} + +func TestCopysign(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Copysign(vf[i], -1); copysign[i] != f { + t.Errorf("math.Copysign(%g, -1) = %g, want %g", vf[i], f, copysign[i]) + } + } + for i := 0; i < len(vf); i++ { + if f := math.Copysign(vf[i], 1); -copysign[i] != f { + t.Errorf("math.Copysign(%g, 1) = %g, want %g", vf[i], f, -copysign[i]) + } + } + for i := 0; i < len(vfcopysignSC); i++ { + if f := math.Copysign(vfcopysignSC[i], -1); !alike(copysignSC[i], f) { + t.Errorf("math.Copysign(%g, -1) = %g, want %g", vfcopysignSC[i], f, copysignSC[i]) + } + } +} + +func TestCos(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Cos(vf[i]); !veryclose(cos[i], f) { + t.Errorf("math.Cos(%g) = %g, want %g", vf[i], f, cos[i]) + } + } + for i := 0; i < len(vfcosSC); i++ { + if f := math.Cos(vfcosSC[i]); !alike(cosSC[i], f) { + t.Errorf("math.Cos(%g) = %g, want %g", vfcosSC[i], f, cosSC[i]) + } + } +} + +func TestCosh(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Cosh(vf[i]); !close(cosh[i], f) { + t.Errorf("math.Cosh(%g) = %g, want %g", vf[i], f, cosh[i]) + } + } + for i := 0; i < len(vfcoshSC); i++ { + if f := math.Cosh(vfcoshSC[i]); !alike(coshSC[i], f) { + t.Errorf("math.Cosh(%g) = %g, want %g", vfcoshSC[i], f, coshSC[i]) + } + } +} + +func TestErf(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := vf[i] / 10 + if f := math.Erf(a); !veryclose(erf[i], f) { + t.Errorf("math.Erf(%g) = %g, want %g", a, f, erf[i]) + } + } + for i := 0; i < len(vferfSC); i++ { + if f := math.Erf(vferfSC[i]); !alike(erfSC[i], f) { + t.Errorf("math.Erf(%g) = %g, want %g", vferfSC[i], f, erfSC[i]) + } + } +} + +func TestErfc(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := vf[i] / 10 + if f := math.Erfc(a); !veryclose(erfc[i], f) { + t.Errorf("math.Erfc(%g) = %g, want %g", a, f, erfc[i]) + } + } + for i := 0; i < len(vferfcSC); i++ { + if f := math.Erfc(vferfcSC[i]); !alike(erfcSC[i], f) { + t.Errorf("math.Erfc(%g) = %g, want %g", vferfcSC[i], f, erfcSC[i]) + } + } +} + +func TestErfinv(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := vf[i] / 10 + if f := math.Erfinv(a); !veryclose(erfinv[i], f) { + t.Errorf("math.Erfinv(%g) = %g, want %g", a, f, erfinv[i]) + } + } + for i := 0; i < len(vferfinvSC); i++ { + if f := math.Erfinv(vferfinvSC[i]); !alike(erfinvSC[i], f) { + t.Errorf("math.Erfinv(%g) = %g, want %g", vferfinvSC[i], f, erfinvSC[i]) + } + } + for x := -0.9; x <= 0.90; x += 1e-2 { + if f := math.Erf(math.Erfinv(x)); !close(x, f) { + t.Errorf("math.Erf(math.Erfinv(%g)) = %g, want %g", x, f, x) + } + } + for x := -0.9; x <= 0.90; x += 1e-2 { + if f := math.Erfinv(math.Erf(x)); !close(x, f) { + t.Errorf("math.Erfinv(math.Erf(%g)) = %g, want %g", x, f, x) + } + } +} + +func TestErfcinv(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := 1.0 - (vf[i] / 10) + if f := math.Erfcinv(a); !veryclose(erfinv[i], f) { + t.Errorf("math.Erfcinv(%g) = %g, want %g", a, f, erfinv[i]) + } + } + for i := 0; i < len(vferfcinvSC); i++ { + if f := math.Erfcinv(vferfcinvSC[i]); !alike(erfcinvSC[i], f) { + t.Errorf("math.Erfcinv(%g) = %g, want %g", vferfcinvSC[i], f, erfcinvSC[i]) + } + } + for x := 0.1; x <= 1.9; x += 1e-2 { + if f := math.Erfc(math.Erfcinv(x)); !close(x, f) { + t.Errorf("math.Erfc(math.Erfcinv(%g)) = %g, want %g", x, f, x) + } + } + for x := 0.1; x <= 1.9; x += 1e-2 { + if f := math.Erfcinv(math.Erfc(x)); !close(x, f) { + t.Errorf("math.Erfcinv(math.Erfc(%g)) = %g, want %g", x, f, x) + } + } +} + +func TestExp(t *testing.T) { + testExp(t, math.Exp, "Exp") + testExp(t, math.ExpGo, "ExpGo") +} + +func testExp(t *testing.T, Exp func(float64) float64, name string) { + for i := 0; i < len(vf); i++ { + if f := math.Exp(vf[i]); !veryclose(exp[i], f) { + t.Errorf("%s(%g) = %g, want %g", name, vf[i], f, exp[i]) + } + } + for i := 0; i < len(vfexpSC); i++ { + if f := math.Exp(vfexpSC[i]); !alike(expSC[i], f) { + t.Errorf("%s(%g) = %g, want %g", name, vfexpSC[i], f, expSC[i]) + } + } +} + +func TestExpm1(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := vf[i] / 100 + if f := math.Expm1(a); !veryclose(expm1[i], f) { + t.Errorf("math.Expm1(%g) = %g, want %g", a, f, expm1[i]) + } + } + for i := 0; i < len(vf); i++ { + a := vf[i] * 10 + if f := math.Expm1(a); !close(expm1Large[i], f) { + t.Errorf("math.Expm1(%g) = %g, want %g", a, f, expm1Large[i]) + } + } + for i := 0; i < len(vfexpm1SC); i++ { + if f := math.Expm1(vfexpm1SC[i]); !alike(expm1SC[i], f) { + t.Errorf("math.Expm1(%g) = %g, want %g", vfexpm1SC[i], f, expm1SC[i]) + } + } +} + +func TestExp2(t *testing.T) { + testExp2(t, math.Exp2, "Exp2") + testExp2(t, math.Exp2Go, "Exp2Go") +} + +func testExp2(t *testing.T, Exp2 func(float64) float64, name string) { + for i := 0; i < len(vf); i++ { + if f := math.Exp2(vf[i]); !close(exp2[i], f) { + t.Errorf("%s(%g) = %g, want %g", name, vf[i], f, exp2[i]) + } + } + for i := 0; i < len(vfexp2SC); i++ { + if f := math.Exp2(vfexp2SC[i]); !alike(exp2SC[i], f) { + t.Errorf("%s(%g) = %g, want %g", name, vfexp2SC[i], f, exp2SC[i]) + } + } + for n := -1074; n < 1024; n++ { + f := math.Exp2(float64(n)) + vf := math.Ldexp(1, n) + if f != vf { + t.Errorf("%s(%d) = %g, want %g", name, n, f, vf) + } + } +} + +func TestAbs(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Abs(vf[i]); fabs[i] != f { + t.Errorf("math.Abs(%g) = %g, want %g", vf[i], f, fabs[i]) + } + } + for i := 0; i < len(vffabsSC); i++ { + if f := math.Abs(vffabsSC[i]); !alike(fabsSC[i], f) { + t.Errorf("math.Abs(%g) = %g, want %g", vffabsSC[i], f, fabsSC[i]) + } + } +} + +func TestDim(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Dim(vf[i], 0); fdim[i] != f { + t.Errorf("math.Dim(%g, %g) = %g, want %g", vf[i], 0.0, f, fdim[i]) + } + } + for i := 0; i < len(vffdimSC); i++ { + if f := math.Dim(vffdimSC[i][0], vffdimSC[i][1]); !alike(fdimSC[i], f) { + t.Errorf("math.Dim(%g, %g) = %g, want %g", vffdimSC[i][0], vffdimSC[i][1], f, fdimSC[i]) + } + } + for i := 0; i < len(vffdim2SC); i++ { + if f := math.Dim(vffdim2SC[i][0], vffdim2SC[i][1]); !alike(fdimSC[i], f) { + t.Errorf("math.Dim(%g, %g) = %g, want %g", vffdim2SC[i][0], vffdim2SC[i][1], f, fdimSC[i]) + } + } +} + +func TestFloor(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Floor(vf[i]); !alike(floor[i], f) { + t.Errorf("math.Floor(%g) = %g, want %g", vf[i], f, floor[i]) + } + } + for i := 0; i < len(vfceilSC); i++ { + if f := math.Floor(vfceilSC[i]); !alike(ceilSC[i], f) { + t.Errorf("math.Floor(%g) = %g, want %g", vfceilSC[i], f, ceilSC[i]) + } + } +} + +func TestMax(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Max(vf[i], ceil[i]); ceil[i] != f { + t.Errorf("math.Max(%g, %g) = %g, want %g", vf[i], ceil[i], f, ceil[i]) + } + } + for i := 0; i < len(vffdimSC); i++ { + if f := math.Max(vffdimSC[i][0], vffdimSC[i][1]); !alike(fmaxSC[i], f) { + t.Errorf("math.Max(%g, %g) = %g, want %g", vffdimSC[i][0], vffdimSC[i][1], f, fmaxSC[i]) + } + } + for i := 0; i < len(vffdim2SC); i++ { + if f := math.Max(vffdim2SC[i][0], vffdim2SC[i][1]); !alike(fmaxSC[i], f) { + t.Errorf("math.Max(%g, %g) = %g, want %g", vffdim2SC[i][0], vffdim2SC[i][1], f, fmaxSC[i]) + } + } +} + +func TestMin(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Min(vf[i], floor[i]); floor[i] != f { + t.Errorf("math.Min(%g, %g) = %g, want %g", vf[i], floor[i], f, floor[i]) + } + } + for i := 0; i < len(vffdimSC); i++ { + if f := math.Min(vffdimSC[i][0], vffdimSC[i][1]); !alike(fminSC[i], f) { + t.Errorf("math.Min(%g, %g) = %g, want %g", vffdimSC[i][0], vffdimSC[i][1], f, fminSC[i]) + } + } + for i := 0; i < len(vffdim2SC); i++ { + if f := math.Min(vffdim2SC[i][0], vffdim2SC[i][1]); !alike(fminSC[i], f) { + t.Errorf("math.Min(%g, %g) = %g, want %g", vffdim2SC[i][0], vffdim2SC[i][1], f, fminSC[i]) + } + } +} + +func TestMod(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Mod(10, vf[i]); fmod[i] != f { + t.Errorf("math.Mod(10, %g) = %g, want %g", vf[i], f, fmod[i]) + } + } + for i := 0; i < len(vffmodSC); i++ { + if f := math.Mod(vffmodSC[i][0], vffmodSC[i][1]); !alike(fmodSC[i], f) { + t.Errorf("math.Mod(%g, %g) = %g, want %g", vffmodSC[i][0], vffmodSC[i][1], f, fmodSC[i]) + } + } + // verify precision of result for extreme inputs + if f := math.Mod(5.9790119248836734e+200, 1.1258465975523544); 0.6447968302508578 != f { + t.Errorf("math.Remainder(5.9790119248836734e+200, 1.1258465975523544) = %g, want 0.6447968302508578", f) + } +} + +func TestFrexp(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f, j := math.Frexp(vf[i]); !veryclose(frexp[i].f, f) || frexp[i].i != j { + t.Errorf("math.Frexp(%g) = %g, %d, want %g, %d", vf[i], f, j, frexp[i].f, frexp[i].i) + } + } + for i := 0; i < len(vffrexpSC); i++ { + if f, j := math.Frexp(vffrexpSC[i]); !alike(frexpSC[i].f, f) || frexpSC[i].i != j { + t.Errorf("math.Frexp(%g) = %g, %d, want %g, %d", vffrexpSC[i], f, j, frexpSC[i].f, frexpSC[i].i) + } + } + for i := 0; i < len(vffrexpBC); i++ { + if f, j := math.Frexp(vffrexpBC[i]); !alike(frexpBC[i].f, f) || frexpBC[i].i != j { + t.Errorf("math.Frexp(%g) = %g, %d, want %g, %d", vffrexpBC[i], f, j, frexpBC[i].f, frexpBC[i].i) + } + } +} + +func TestGamma(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Gamma(vf[i]); !close(gamma[i], f) { + t.Errorf("math.Gamma(%g) = %g, want %g", vf[i], f, gamma[i]) + } + } + for _, g := range vfgamma { + f := math.Gamma(g[0]) + var ok bool + if math.IsNaN(g[1]) || math.IsInf(g[1], 0) || g[1] == 0 || f == 0 { + ok = alike(g[1], f) + } else if g[0] > -50 && g[0] <= 171 { + ok = veryclose(g[1], f) + } else { + ok = close(g[1], f) + } + if !ok { + t.Errorf("math.Gamma(%g) = %g, want %g", g[0], f, g[1]) + } + } +} + +func TestHypot(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := math.Abs(1e200 * tanh[i] * math.Sqrt(2)) + if f := math.Hypot(1e200*tanh[i], 1e200*tanh[i]); !veryclose(a, f) { + t.Errorf("math.Hypot(%g, %g) = %g, want %g", 1e200*tanh[i], 1e200*tanh[i], f, a) + } + } + for i := 0; i < len(vfhypotSC); i++ { + if f := math.Hypot(vfhypotSC[i][0], vfhypotSC[i][1]); !alike(hypotSC[i], f) { + t.Errorf("math.Hypot(%g, %g) = %g, want %g", vfhypotSC[i][0], vfhypotSC[i][1], f, hypotSC[i]) + } + } +} + +func TestHypotGo(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := math.Abs(1e200 * tanh[i] * math.Sqrt(2)) + if f := math.HypotGo(1e200*tanh[i], 1e200*tanh[i]); !veryclose(a, f) { + t.Errorf("math.HypotGo(%g, %g) = %g, want %g", 1e200*tanh[i], 1e200*tanh[i], f, a) + } + } + for i := 0; i < len(vfhypotSC); i++ { + if f := math.HypotGo(vfhypotSC[i][0], vfhypotSC[i][1]); !alike(hypotSC[i], f) { + t.Errorf("math.HypotGo(%g, %g) = %g, want %g", vfhypotSC[i][0], vfhypotSC[i][1], f, hypotSC[i]) + } + } +} + +func TestIlogb(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := frexp[i].i - 1 // adjust because fr in the interval [½, 1) + if e := math.Ilogb(vf[i]); a != e { + t.Errorf("math.Ilogb(%g) = %d, want %d", vf[i], e, a) + } + } + for i := 0; i < len(vflogbSC); i++ { + if e := math.Ilogb(vflogbSC[i]); ilogbSC[i] != e { + t.Errorf("math.Ilogb(%g) = %d, want %d", vflogbSC[i], e, ilogbSC[i]) + } + } + for i := 0; i < len(vffrexpBC); i++ { + if e := math.Ilogb(vffrexpBC[i]); int(logbBC[i]) != e { + t.Errorf("math.Ilogb(%g) = %d, want %d", vffrexpBC[i], e, int(logbBC[i])) + } + } +} + +func TestJ0(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.J0(vf[i]); !soclose(j0[i], f, 4e-14) { + t.Errorf("math.J0(%g) = %g, want %g", vf[i], f, j0[i]) + } + } + for i := 0; i < len(vfj0SC); i++ { + if f := math.J0(vfj0SC[i]); !alike(j0SC[i], f) { + t.Errorf("math.J0(%g) = %g, want %g", vfj0SC[i], f, j0SC[i]) + } + } +} + +func TestJ1(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.J1(vf[i]); !close(j1[i], f) { + t.Errorf("math.J1(%g) = %g, want %g", vf[i], f, j1[i]) + } + } + for i := 0; i < len(vfj0SC); i++ { + if f := math.J1(vfj0SC[i]); !alike(j1SC[i], f) { + t.Errorf("math.J1(%g) = %g, want %g", vfj0SC[i], f, j1SC[i]) + } + } +} + +func TestJn(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Jn(2, vf[i]); !close(j2[i], f) { + t.Errorf("math.Jn(2, %g) = %g, want %g", vf[i], f, j2[i]) + } + if f := math.Jn(-3, vf[i]); !close(jM3[i], f) { + t.Errorf("math.Jn(-3, %g) = %g, want %g", vf[i], f, jM3[i]) + } + } + for i := 0; i < len(vfj0SC); i++ { + if f := math.Jn(2, vfj0SC[i]); !alike(j2SC[i], f) { + t.Errorf("math.Jn(2, %g) = %g, want %g", vfj0SC[i], f, j2SC[i]) + } + if f := math.Jn(-3, vfj0SC[i]); !alike(jM3SC[i], f) { + t.Errorf("math.Jn(-3, %g) = %g, want %g", vfj0SC[i], f, jM3SC[i]) + } + } +} + +func TestLdexp(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Ldexp(frexp[i].f, frexp[i].i); !veryclose(vf[i], f) { + t.Errorf("math.Ldexp(%g, %d) = %g, want %g", frexp[i].f, frexp[i].i, f, vf[i]) + } + } + for i := 0; i < len(vffrexpSC); i++ { + if f := math.Ldexp(frexpSC[i].f, frexpSC[i].i); !alike(vffrexpSC[i], f) { + t.Errorf("math.Ldexp(%g, %d) = %g, want %g", frexpSC[i].f, frexpSC[i].i, f, vffrexpSC[i]) + } + } + for i := 0; i < len(vfldexpSC); i++ { + if f := math.Ldexp(vfldexpSC[i].f, vfldexpSC[i].i); !alike(ldexpSC[i], f) { + t.Errorf("math.Ldexp(%g, %d) = %g, want %g", vfldexpSC[i].f, vfldexpSC[i].i, f, ldexpSC[i]) + } + } + for i := 0; i < len(vffrexpBC); i++ { + if f := math.Ldexp(frexpBC[i].f, frexpBC[i].i); !alike(vffrexpBC[i], f) { + t.Errorf("math.Ldexp(%g, %d) = %g, want %g", frexpBC[i].f, frexpBC[i].i, f, vffrexpBC[i]) + } + } + for i := 0; i < len(vfldexpBC); i++ { + if f := math.Ldexp(vfldexpBC[i].f, vfldexpBC[i].i); !alike(ldexpBC[i], f) { + t.Errorf("math.Ldexp(%g, %d) = %g, want %g", vfldexpBC[i].f, vfldexpBC[i].i, f, ldexpBC[i]) + } + } +} + +func TestLgamma(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f, s := math.Lgamma(vf[i]); !close(lgamma[i].f, f) || lgamma[i].i != s { + t.Errorf("math.Lgamma(%g) = %g, %d, want %g, %d", vf[i], f, s, lgamma[i].f, lgamma[i].i) + } + } + for i := 0; i < len(vflgammaSC); i++ { + if f, s := math.Lgamma(vflgammaSC[i]); !alike(lgammaSC[i].f, f) || lgammaSC[i].i != s { + t.Errorf("math.Lgamma(%g) = %g, %d, want %g, %d", vflgammaSC[i], f, s, lgammaSC[i].f, lgammaSC[i].i) + } + } +} + +func TestLog(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := math.Abs(vf[i]) + if f := math.Log(a); log[i] != f { + t.Errorf("math.Log(%g) = %g, want %g", a, f, log[i]) + } + } + if f := math.Log(10); f != math.Ln10 { + t.Errorf("math.Log(%g) = %g, want %g", 10.0, f, math.Ln10) + } + for i := 0; i < len(vflogSC); i++ { + if f := math.Log(vflogSC[i]); !alike(logSC[i], f) { + t.Errorf("math.Log(%g) = %g, want %g", vflogSC[i], f, logSC[i]) + } + } +} + +func TestLogb(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Logb(vf[i]); logb[i] != f { + t.Errorf("math.Logb(%g) = %g, want %g", vf[i], f, logb[i]) + } + } + for i := 0; i < len(vflogbSC); i++ { + if f := math.Logb(vflogbSC[i]); !alike(logbSC[i], f) { + t.Errorf("math.Logb(%g) = %g, want %g", vflogbSC[i], f, logbSC[i]) + } + } + for i := 0; i < len(vffrexpBC); i++ { + if f := math.Logb(vffrexpBC[i]); !alike(logbBC[i], f) { + t.Errorf("math.Logb(%g) = %g, want %g", vffrexpBC[i], f, logbBC[i]) + } + } +} + +func TestLog10(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := math.Abs(vf[i]) + if f := math.Log10(a); !veryclose(log10[i], f) { + t.Errorf("math.Log10(%g) = %g, want %g", a, f, log10[i]) + } + } + if f := math.Log10(math.E); f != math.Log10E { + t.Errorf("math.Log10(%g) = %g, want %g", math.E, f, math.Log10E) + } + for i := 0; i < len(vflogSC); i++ { + if f := math.Log10(vflogSC[i]); !alike(logSC[i], f) { + t.Errorf("math.Log10(%g) = %g, want %g", vflogSC[i], f, logSC[i]) + } + } +} + +func TestLog1p(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := vf[i] / 100 + if f := math.Log1p(a); !veryclose(log1p[i], f) { + t.Errorf("math.Log1p(%g) = %g, want %g", a, f, log1p[i]) + } + } + a := 9.0 + if f := math.Log1p(a); f != math.Ln10 { + t.Errorf("math.Log1p(%g) = %g, want %g", a, f, math.Ln10) + } + for i := 0; i < len(vflogSC); i++ { + if f := math.Log1p(vflog1pSC[i]); !alike(log1pSC[i], f) { + t.Errorf("math.Log1p(%g) = %g, want %g", vflog1pSC[i], f, log1pSC[i]) + } + } +} + +func TestLog2(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := math.Abs(vf[i]) + if f := math.Log2(a); !veryclose(log2[i], f) { + t.Errorf("math.Log2(%g) = %g, want %g", a, f, log2[i]) + } + } + if f := math.Log2(math.E); f != math.Log2E { + t.Errorf("math.Log2(%g) = %g, want %g", math.E, f, math.Log2E) + } + for i := 0; i < len(vflogSC); i++ { + if f := math.Log2(vflogSC[i]); !alike(logSC[i], f) { + t.Errorf("math.Log2(%g) = %g, want %g", vflogSC[i], f, logSC[i]) + } + } + for i := -1074; i <= 1023; i++ { + f := math.Ldexp(1, i) + l := math.Log2(f) + if l != float64(i) { + t.Errorf("math.Log2(2**%d) = %g, want %d", i, l, i) + } + } +} + +func TestModf(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f, g := math.Modf(vf[i]); !veryclose(modf[i][0], f) || !veryclose(modf[i][1], g) { + t.Errorf("math.Modf(%g) = %g, %g, want %g, %g", vf[i], f, g, modf[i][0], modf[i][1]) + } + } + for i := 0; i < len(vfmodfSC); i++ { + if f, g := math.Modf(vfmodfSC[i]); !alike(modfSC[i][0], f) || !alike(modfSC[i][1], g) { + t.Errorf("math.Modf(%g) = %g, %g, want %g, %g", vfmodfSC[i], f, g, modfSC[i][0], modfSC[i][1]) + } + } +} + +func TestNextafter32(t *testing.T) { + for i := 0; i < len(vf); i++ { + vfi := float32(vf[i]) + if f := math.Nextafter32(vfi, 10); nextafter32[i] != f { + t.Errorf("math.Nextafter32(%g, %g) = %g want %g", vfi, 10.0, f, nextafter32[i]) + } + } + for i := 0; i < len(vfnextafter32SC); i++ { + if f := math.Nextafter32(vfnextafter32SC[i][0], vfnextafter32SC[i][1]); !alike(float64(nextafter32SC[i]), float64(f)) { + t.Errorf("math.Nextafter32(%g, %g) = %g want %g", vfnextafter32SC[i][0], vfnextafter32SC[i][1], f, nextafter32SC[i]) + } + } +} + +func TestNextafter64(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Nextafter(vf[i], 10); nextafter64[i] != f { + t.Errorf("math.Nextafter64(%g, %g) = %g want %g", vf[i], 10.0, f, nextafter64[i]) + } + } + for i := 0; i < len(vfnextafter64SC); i++ { + if f := math.Nextafter(vfnextafter64SC[i][0], vfnextafter64SC[i][1]); !alike(nextafter64SC[i], f) { + t.Errorf("math.Nextafter64(%g, %g) = %g want %g", vfnextafter64SC[i][0], vfnextafter64SC[i][1], f, nextafter64SC[i]) + } + } +} + +func TestPow(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Pow(10, vf[i]); !close(pow[i], f) { + t.Errorf("math.Pow(10, %g) = %g, want %g", vf[i], f, pow[i]) + } + } + for i := 0; i < len(vfpowSC); i++ { + if f := math.Pow(vfpowSC[i][0], vfpowSC[i][1]); !alike(powSC[i], f) { + t.Errorf("math.Pow(%g, %g) = %g, want %g", vfpowSC[i][0], vfpowSC[i][1], f, powSC[i]) + } + } +} + +func TestPow10(t *testing.T) { + for i := 0; i < len(vfpow10SC); i++ { + if f := math.Pow10(vfpow10SC[i]); !alike(pow10SC[i], f) { + t.Errorf("math.Pow10(%d) = %g, want %g", vfpow10SC[i], f, pow10SC[i]) + } + } +} + +func TestRemainder(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Remainder(10, vf[i]); remainder[i] != f { + t.Errorf("math.Remainder(10, %g) = %g, want %g", vf[i], f, remainder[i]) + } + } + for i := 0; i < len(vffmodSC); i++ { + if f := math.Remainder(vffmodSC[i][0], vffmodSC[i][1]); !alike(fmodSC[i], f) { + t.Errorf("math.Remainder(%g, %g) = %g, want %g", vffmodSC[i][0], vffmodSC[i][1], f, fmodSC[i]) + } + } + // verify precision of result for extreme inputs + if f := math.Remainder(5.9790119248836734e+200, 1.1258465975523544); -0.4810497673014966 != f { + t.Errorf("math.Remainder(5.9790119248836734e+200, 1.1258465975523544) = %g, want -0.4810497673014966", f) + } + // verify that sign is correct when r == 0. + test := func(x, y float64) { + if r := math.Remainder(x, y); r == 0 && math.Signbit(r) != math.Signbit(x) { + t.Errorf("math.Remainder(x=%f, y=%f) = %f, sign of (zero) result should agree with sign of x", x, y, r) + } + } + for x := 0.0; x <= 3.0; x += 1 { + for y := 1.0; y <= 3.0; y += 1 { + test(x, y) + test(x, -y) + test(-x, y) + test(-x, -y) + } + } +} + +func TestRound(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Round(vf[i]); !alike(round[i], f) { + t.Errorf("math.Round(%g) = %g, want %g", vf[i], f, round[i]) + } + } + for i := 0; i < len(vfroundSC); i++ { + if f := math.Round(vfroundSC[i][0]); !alike(vfroundSC[i][1], f) { + t.Errorf("math.Round(%g) = %g, want %g", vfroundSC[i][0], f, vfroundSC[i][1]) + } + } +} + +func TestRoundToEven(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.RoundToEven(vf[i]); !alike(round[i], f) { + t.Errorf("math.RoundToEven(%g) = %g, want %g", vf[i], f, round[i]) + } + } + for i := 0; i < len(vfroundEvenSC); i++ { + if f := math.RoundToEven(vfroundEvenSC[i][0]); !alike(vfroundEvenSC[i][1], f) { + t.Errorf("math.RoundToEven(%g) = %g, want %g", vfroundEvenSC[i][0], f, vfroundEvenSC[i][1]) + } + } +} + +func TestSignbit(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Signbit(vf[i]); signbit[i] != f { + t.Errorf("math.Signbit(%g) = %t, want %t", vf[i], f, signbit[i]) + } + } + for i := 0; i < len(vfsignbitSC); i++ { + if f := math.Signbit(vfsignbitSC[i]); signbitSC[i] != f { + t.Errorf("math.Signbit(%g) = %t, want %t", vfsignbitSC[i], f, signbitSC[i]) + } + } +} + +func TestSin(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Sin(vf[i]); !veryclose(sin[i], f) { + t.Errorf("math.Sin(%g) = %g, want %g", vf[i], f, sin[i]) + } + } + for i := 0; i < len(vfsinSC); i++ { + if f := math.Sin(vfsinSC[i]); !alike(sinSC[i], f) { + t.Errorf("math.Sin(%g) = %g, want %g", vfsinSC[i], f, sinSC[i]) + } + } +} + +func TestSincos(t *testing.T) { + for i := 0; i < len(vf); i++ { + if s, c := math.Sincos(vf[i]); !veryclose(sin[i], s) || !veryclose(cos[i], c) { + t.Errorf("math.Sincos(%g) = %g, %g want %g, %g", vf[i], s, c, sin[i], cos[i]) + } + } +} + +func TestSinh(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Sinh(vf[i]); !close(sinh[i], f) { + t.Errorf("math.Sinh(%g) = %g, want %g", vf[i], f, sinh[i]) + } + } + for i := 0; i < len(vfsinhSC); i++ { + if f := math.Sinh(vfsinhSC[i]); !alike(sinhSC[i], f) { + t.Errorf("math.Sinh(%g) = %g, want %g", vfsinhSC[i], f, sinhSC[i]) + } + } +} + +func TestSqrt(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := math.Abs(vf[i]) + if f := math.SqrtGo(a); sqrt[i] != f { + t.Errorf("math.SqrtGo(%g) = %g, want %g", a, f, sqrt[i]) + } + a = math.Abs(vf[i]) + if f := math.Sqrt(a); sqrt[i] != f { + t.Errorf("math.Sqrt(%g) = %g, want %g", a, f, sqrt[i]) + } + } + for i := 0; i < len(vfsqrtSC); i++ { + if f := math.SqrtGo(vfsqrtSC[i]); !alike(sqrtSC[i], f) { + t.Errorf("math.SqrtGo(%g) = %g, want %g", vfsqrtSC[i], f, sqrtSC[i]) + } + if f := math.Sqrt(vfsqrtSC[i]); !alike(sqrtSC[i], f) { + t.Errorf("math.Sqrt(%g) = %g, want %g", vfsqrtSC[i], f, sqrtSC[i]) + } + } +} + +func TestTan(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Tan(vf[i]); !veryclose(tan[i], f) { + t.Errorf("math.Tan(%g) = %g, want %g", vf[i], f, tan[i]) + } + } + // same special cases as Sin + for i := 0; i < len(vfsinSC); i++ { + if f := math.Tan(vfsinSC[i]); !alike(sinSC[i], f) { + t.Errorf("math.Tan(%g) = %g, want %g", vfsinSC[i], f, sinSC[i]) + } + } +} + +func TestTanh(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Tanh(vf[i]); !veryclose(tanh[i], f) { + t.Errorf("math.Tanh(%g) = %g, want %g", vf[i], f, tanh[i]) + } + } + for i := 0; i < len(vftanhSC); i++ { + if f := math.Tanh(vftanhSC[i]); !alike(tanhSC[i], f) { + t.Errorf("math.Tanh(%g) = %g, want %g", vftanhSC[i], f, tanhSC[i]) + } + } +} + +func TestTrunc(t *testing.T) { + for i := 0; i < len(vf); i++ { + if f := math.Trunc(vf[i]); !alike(trunc[i], f) { + t.Errorf("math.Trunc(%g) = %g, want %g", vf[i], f, trunc[i]) + } + } + for i := 0; i < len(vfceilSC); i++ { + if f := math.Trunc(vfceilSC[i]); !alike(ceilSC[i], f) { + t.Errorf("math.Trunc(%g) = %g, want %g", vfceilSC[i], f, ceilSC[i]) + } + } +} + +func TestY0(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := math.Abs(vf[i]) + if f := math.Y0(a); !close(y0[i], f) { + t.Errorf("math.Y0(%g) = %g, want %g", a, f, y0[i]) + } + } + for i := 0; i < len(vfy0SC); i++ { + if f := math.Y0(vfy0SC[i]); !alike(y0SC[i], f) { + t.Errorf("math.Y0(%g) = %g, want %g", vfy0SC[i], f, y0SC[i]) + } + } +} + +func TestY1(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := math.Abs(vf[i]) + if f := math.Y1(a); !soclose(y1[i], f, 2e-14) { + t.Errorf("math.Y1(%g) = %g, want %g", a, f, y1[i]) + } + } + for i := 0; i < len(vfy0SC); i++ { + if f := math.Y1(vfy0SC[i]); !alike(y1SC[i], f) { + t.Errorf("math.Y1(%g) = %g, want %g", vfy0SC[i], f, y1SC[i]) + } + } +} + +func TestYn(t *testing.T) { + for i := 0; i < len(vf); i++ { + a := math.Abs(vf[i]) + if f := math.Yn(2, a); !close(y2[i], f) { + t.Errorf("math.Yn(2, %g) = %g, want %g", a, f, y2[i]) + } + if f := math.Yn(-3, a); !close(yM3[i], f) { + t.Errorf("math.Yn(-3, %g) = %g, want %g", a, f, yM3[i]) + } + } + for i := 0; i < len(vfy0SC); i++ { + if f := math.Yn(2, vfy0SC[i]); !alike(y2SC[i], f) { + t.Errorf("math.Yn(2, %g) = %g, want %g", vfy0SC[i], f, y2SC[i]) + } + if f := math.Yn(-3, vfy0SC[i]); !alike(yM3SC[i], f) { + t.Errorf("math.Yn(-3, %g) = %g, want %g", vfy0SC[i], f, yM3SC[i]) + } + } + if f := math.Yn(0, 0); !alike(math.Inf(-1), f) { + t.Errorf("math.Yn(0, 0) = %g, want %g", f, math.Inf(-1)) + } +} + +var PortableFMA = math.FMA // hide call from compiler intrinsic; falls back to portable code + +func TestFMA(t *testing.T) { + for _, c := range fmaC { + got := math.FMA(c.x, c.y, c.z) + if !alike(got, c.want) { + t.Errorf("math.FMA(%g,%g,%g) == %g; want %g", c.x, c.y, c.z, got, c.want) + } + got = PortableFMA(c.x, c.y, c.z) + if !alike(got, c.want) { + t.Errorf("PortableFMA(%g,%g,%g) == %g; want %g", c.x, c.y, c.z, got, c.want) + } + } +} + +//go:noinline +func fmsub(x, y, z float64) float64 { + return math.FMA(x, y, -z) +} + +//go:noinline +func fnmsub(x, y, z float64) float64 { + return math.FMA(-x, y, z) +} + +//go:noinline +func fnmadd(x, y, z float64) float64 { + return math.FMA(-x, y, -z) +} + +func TestFMANegativeArgs(t *testing.T) { + // Some architectures have instructions for fused multiply-subtract and + // also negated variants of fused multiply-add and subtract. This test + // aims to check that the optimizations that generate those instructions + // are applied correctly, if they exist. + for _, c := range fmaC { + want := PortableFMA(c.x, c.y, -c.z) + got := fmsub(c.x, c.y, c.z) + if !alike(got, want) { + t.Errorf("math.FMA(%g, %g, -(%g)) == %g, want %g", c.x, c.y, c.z, got, want) + } + want = PortableFMA(-c.x, c.y, c.z) + got = fnmsub(c.x, c.y, c.z) + if !alike(got, want) { + t.Errorf("math.FMA(-(%g), %g, %g) == %g, want %g", c.x, c.y, c.z, got, want) + } + want = PortableFMA(-c.x, c.y, -c.z) + got = fnmadd(c.x, c.y, c.z) + if !alike(got, want) { + t.Errorf("math.FMA(-(%g), %g, -(%g)) == %g, want %g", c.x, c.y, c.z, got, want) + } + } +} + +// Check that math functions of high angle values +// return accurate results. [Since (vf[i] + large) - large != vf[i], +// testing for math.Trig(vf[i] + large) == math.Trig(vf[i]), where large is +// a multiple of 2*math.Pi, is misleading.] +func TestLargeCos(t *testing.T) { + large := float64(100000 * math.Pi) + for i := 0; i < len(vf); i++ { + f1 := cosLarge[i] + f2 := math.Cos(vf[i] + large) + if !close(f1, f2) { + t.Errorf("math.Cos(%g) = %g, want %g", vf[i]+large, f2, f1) + } + } +} + +func TestLargeSin(t *testing.T) { + large := float64(100000 * math.Pi) + for i := 0; i < len(vf); i++ { + f1 := sinLarge[i] + f2 := math.Sin(vf[i] + large) + if !close(f1, f2) { + t.Errorf("math.Sin(%g) = %g, want %g", vf[i]+large, f2, f1) + } + } +} + +func TestLargeSincos(t *testing.T) { + large := float64(100000 * math.Pi) + for i := 0; i < len(vf); i++ { + f1, g1 := sinLarge[i], cosLarge[i] + f2, g2 := math.Sincos(vf[i] + large) + if !close(f1, f2) || !close(g1, g2) { + t.Errorf("math.Sincos(%g) = %g, %g, want %g, %g", vf[i]+large, f2, g2, f1, g1) + } + } +} + +func TestLargeTan(t *testing.T) { + large := float64(100000 * math.Pi) + for i := 0; i < len(vf); i++ { + f1 := tanLarge[i] + f2 := math.Tan(vf[i] + large) + if !close(f1, f2) { + t.Errorf("math.Tan(%g) = %g, want %g", vf[i]+large, f2, f1) + } + } +} + +// Check that trigReduce matches the standard reduction results for input values +// below reduceThreshold. +func TestTrigReduce(t *testing.T) { + inputs := make([]float64, len(vf)) + // all of the standard inputs + copy(inputs, vf) + // all of the large inputs + large := float64(100000 * math.Pi) + for _, v := range vf { + inputs = append(inputs, v+large) + } + // Also test some special inputs, math.Pi and right below the reduceThreshold + // XXX: https://github.com/gnolang/gno/issues/1149 + inputs = append(inputs, float64(math.Pi), math.Nextafter(math.ReduceThreshold, 0)) + for _, x := range inputs { + // reduce the value to compare + j, z := math.TrigReduce(x) + xred := float64(j)*(math.Pi/4) + z + + if f, fred := math.Sin(x), math.Sin(xred); !close(f, fred) { + t.Errorf("math.Sin(trigmath.Reduce(%g)) != math.Sin(%g), got %g, want %g", x, x, fred, f) + } + if f, fred := math.Cos(x), math.Cos(xred); !close(f, fred) { + t.Errorf("math.Cos(trigmath.Reduce(%g)) != math.Cos(%g), got %g, want %g", x, x, fred, f) + } + if f, fred := math.Tan(x), math.Tan(xred); !close(f, fred) { + t.Errorf(" math.Tan(trigmath.Reduce(%g)) != math.Tan(%g), got %g, want %g", x, x, fred, f) + } + f, g := math.Sincos(x) + fred, gred := math.Sincos(xred) + if !close(f, fred) || !close(g, gred) { + t.Errorf(" math.Sincos(trigmath.Reduce(%g)) != math.Sincos(%g), got %g, %g, want %g, %g", x, x, fred, gred, f, g) + } + } +} + +// Check that math constants are accepted by compiler +// and have right value (assumes strconv.ParseFloat works). +// https://golang.org/issue/201 + +type floatTest struct { + val interface{} + name string + str string +} + +var floatTests = []floatTest{ + {float64(math.MaxFloat64), "MaxFloat64", "1.7976931348623157e+308"}, + {float64(math.SmallestNonzeroFloat64), "SmallestNonzeroFloat64", "5e-324"}, + {float32(math.MaxFloat32), "MaxFloat32", "3.4028235e+38"}, + {float32(math.SmallestNonzeroFloat32), "SmallestNonzeroFloat32", "1e-45"}, +} + +func TestFloatMinMax(t *testing.T) { + for _, tt := range floatTests { + s := fmt.Sprint(tt.val) + if s != tt.str { + t.Errorf("math.Sprint(%v) = %s, want %s", tt.name, s, tt.str) + } + } +} + +func TestFloatMinima(t *testing.T) { + if q := float32(math.SmallestNonzeroFloat32 / 2); q != 0 { + t.Errorf("float32(math.SmallestNonzeroFloat32 / 2) = %g, want 0", q) + } + if q := float64(math.SmallestNonzeroFloat64 / 2); q != 0 { + t.Errorf("float64(math.SmallestNonzeroFloat64 / 2) = %g, want 0", q) + } +} + +var indirectSqrt = math.Sqrt + +// TestFloat32Sqrt checks the correctness of the float32 square root optimization result. +func TestFloat32Sqrt(t *testing.T) { + for _, v := range sqrt32 { + want := float32(indirectSqrt(float64(v))) + got := float32(math.Sqrt(float64(v))) + if math.IsNaN(float64(want)) { + if !math.IsNaN(float64(got)) { + t.Errorf("got=%#v want=NaN, v=%#v", got, v) + } + continue + } + if got != want { + t.Errorf("got=%#v want=%#v, v=%#v", got, want, v) + } + } +} + +// Benchmarks + +// Global exported variables are used to store the +// return values of functions measured in the benchmarks. +// Storing the results in these variables prevents the compiler +// from completely optimizing the benchmarked functions away. +var ( + GlobalI int + GlobalB bool + GlobalF float64 +) + +func BenchmarkAcos(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Acos(.5) + } + GlobalF = x +} + +func BenchmarkAcosh(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Acosh(1.5) + } + GlobalF = x +} + +func BenchmarkAsin(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Asin(.5) + } + GlobalF = x +} + +func BenchmarkAsinh(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Asinh(.5) + } + GlobalF = x +} + +func BenchmarkAtan(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Atan(.5) + } + GlobalF = x +} + +func BenchmarkAtanh(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Atanh(.5) + } + GlobalF = x +} + +func BenchmarkAtan2(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Atan2(.5, 1) + } + GlobalF = x +} + +func BenchmarkCbrt(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Cbrt(10) + } + GlobalF = x +} + +func BenchmarkCeil(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Ceil(.5) + } + GlobalF = x +} + +var copysignNeg = -1.0 + +func BenchmarkCopysign(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Copysign(.5, copysignNeg) + } + GlobalF = x +} + +func BenchmarkCos(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Cos(.5) + } + GlobalF = x +} + +func BenchmarkCosh(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Cosh(2.5) + } + GlobalF = x +} + +func BenchmarkErf(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Erf(.5) + } + GlobalF = x +} + +func BenchmarkErfc(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Erfc(.5) + } + GlobalF = x +} + +func BenchmarkErfinv(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Erfinv(.5) + } + GlobalF = x +} + +func BenchmarkErfcinv(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Erfcinv(.5) + } + GlobalF = x +} + +func BenchmarkExp(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Exp(.5) + } + GlobalF = x +} + +func BenchmarkExpGo(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.ExpGo(.5) + } + GlobalF = x +} + +func BenchmarkExpm1(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Expm1(.5) + } + GlobalF = x +} + +func BenchmarkExp2(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Exp2(.5) + } + GlobalF = x +} + +func BenchmarkExp2Go(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Exp2Go(.5) + } + GlobalF = x +} + +var absPos = .5 + +func BenchmarkAbs(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Abs(absPos) + } + GlobalF = x +} + +func BenchmarkDim(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Dim(GlobalF, x) + } + GlobalF = x +} + +func BenchmarkFloor(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Floor(.5) + } + GlobalF = x +} + +func BenchmarkMax(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Max(10, 3) + } + GlobalF = x +} + +func BenchmarkMin(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Min(10, 3) + } + GlobalF = x +} + +func BenchmarkMod(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Mod(10, 3) + } + GlobalF = x +} + +func BenchmarkFrexp(b *testing.B) { + x := 0.0 + y := 0 + for i := 0; i < b.N; i++ { + x, y = math.Frexp(8) + } + GlobalF = x + GlobalI = y +} + +func BenchmarkGamma(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Gamma(2.5) + } + GlobalF = x +} + +func BenchmarkHypot(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Hypot(3, 4) + } + GlobalF = x +} + +func BenchmarkHypotGo(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.HypotGo(3, 4) + } + GlobalF = x +} + +func BenchmarkIlogb(b *testing.B) { + x := 0 + for i := 0; i < b.N; i++ { + x = math.Ilogb(.5) + } + GlobalI = x +} + +func BenchmarkJ0(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.J0(2.5) + } + GlobalF = x +} + +func BenchmarkJ1(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.J1(2.5) + } + GlobalF = x +} + +func BenchmarkJn(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Jn(2, 2.5) + } + GlobalF = x +} + +func BenchmarkLdexp(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Ldexp(.5, 2) + } + GlobalF = x +} + +func BenchmarkLgamma(b *testing.B) { + x := 0.0 + y := 0 + for i := 0; i < b.N; i++ { + x, y = math.Lgamma(2.5) + } + GlobalF = x + GlobalI = y +} + +func BenchmarkLog(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Log(.5) + } + GlobalF = x +} + +func BenchmarkLogb(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Logb(.5) + } + GlobalF = x +} + +func BenchmarkLog1p(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Log1p(.5) + } + GlobalF = x +} + +func BenchmarkLog10(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Log10(.5) + } + GlobalF = x +} + +func BenchmarkLog2(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Log2(.5) + } + GlobalF += x +} + +func BenchmarkModf(b *testing.B) { + x := 0.0 + y := 0.0 + for i := 0; i < b.N; i++ { + x, y = math.Modf(1.5) + } + GlobalF += x + GlobalF += y +} + +func BenchmarkNextafter32(b *testing.B) { + x := float32(0.0) + for i := 0; i < b.N; i++ { + x = math.Nextafter32(.5, 1) + } + GlobalF = float64(x) +} + +func BenchmarkNextafter64(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Nextafter(.5, 1) + } + GlobalF = x +} + +func BenchmarkPowInt(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Pow(2, 2) + } + GlobalF = x +} + +func BenchmarkPowFrac(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Pow(2.5, 1.5) + } + GlobalF = x +} + +var pow10pos = int(300) + +func BenchmarkPow10Pos(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Pow10(pow10pos) + } + GlobalF = x +} + +var pow10neg = int(-300) + +func BenchmarkPow10Neg(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Pow10(pow10neg) + } + GlobalF = x +} + +var roundNeg = float64(-2.5) + +func BenchmarkRound(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Round(roundNeg) + } + GlobalF = x +} + +func BenchmarkRoundToEven(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.RoundToEven(roundNeg) + } + GlobalF = x +} + +func BenchmarkRemainder(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Remainder(10, 3) + } + GlobalF = x +} + +var signbitPos = 2.5 + +func BenchmarkSignbit(b *testing.B) { + x := false + for i := 0; i < b.N; i++ { + x = math.Signbit(signbitPos) + } + GlobalB = x +} + +func BenchmarkSin(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Sin(.5) + } + GlobalF = x +} + +func BenchmarkSincos(b *testing.B) { + x := 0.0 + y := 0.0 + for i := 0; i < b.N; i++ { + x, y = math.Sincos(.5) + } + GlobalF += x + GlobalF += y +} + +func BenchmarkSinh(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Sinh(2.5) + } + GlobalF = x +} + +func BenchmarkSqrtIndirect(b *testing.B) { + x, y := 0.0, 10.0 + f := math.Sqrt + for i := 0; i < b.N; i++ { + x += f(y) + } + GlobalF = x +} + +func BenchmarkSqrtLatency(b *testing.B) { + x := 10.0 + for i := 0; i < b.N; i++ { + x = math.Sqrt(x) + } + GlobalF = x +} + +func BenchmarkSqrtIndirectLatency(b *testing.B) { + x := 10.0 + f := math.Sqrt + for i := 0; i < b.N; i++ { + x = f(x) + } + GlobalF = x +} + +func BenchmarkSqrtGoLatency(b *testing.B) { + x := 10.0 + for i := 0; i < b.N; i++ { + x = math.SqrtGo(x) + } + GlobalF = x +} + +func isPrime(i int) bool { + // Yes, this is a dumb way to write this code, + // but calling Sqrt repeatedly in this way demonstrates + // the benefit of using a direct SQRT instruction on systems + // that have one, whereas the obvious loop seems not to + // demonstrate such a benefit. + for j := 2; float64(j) <= math.Sqrt(float64(i)); j++ { + if i%j == 0 { + return false + } + } + return true +} + +func BenchmarkSqrtPrime(b *testing.B) { + x := false + for i := 0; i < b.N; i++ { + x = isPrime(100003) + } + GlobalB = x +} + +func BenchmarkTan(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Tan(.5) + } + GlobalF = x +} + +func BenchmarkTanh(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Tanh(2.5) + } + GlobalF = x +} + +func BenchmarkTrunc(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Trunc(.5) + } + GlobalF = x +} + +func BenchmarkY0(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Y0(2.5) + } + GlobalF = x +} + +func BenchmarkY1(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Y1(2.5) + } + GlobalF = x +} + +func BenchmarkYn(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Yn(2, 2.5) + } + GlobalF = x +} + +func BenchmarkFloat64bits(b *testing.B) { + y := uint64(0) + for i := 0; i < b.N; i++ { + y = math.Float64bits(roundNeg) + } + GlobalI = int(y) +} + +var roundUint64 = uint64(5) + +func BenchmarkFloat64frombits(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.Float64frombits(roundUint64) + } + GlobalF = x +} + +var roundFloat32 = float32(-2.5) + +func BenchmarkFloat32bits(b *testing.B) { + y := uint32(0) + for i := 0; i < b.N; i++ { + y = math.Float32bits(roundFloat32) + } + GlobalI = int(y) +} + +var roundUint32 = uint32(5) + +func BenchmarkFloat32frombits(b *testing.B) { + x := float32(0.0) + for i := 0; i < b.N; i++ { + x = math.Float32frombits(roundUint32) + } + GlobalF = float64(x) +} + +func BenchmarkFMA(b *testing.B) { + x := 0.0 + for i := 0; i < b.N; i++ { + x = math.FMA(math.E, math.Pi, x) } + GlobalF = x } diff --git a/gnovm/stdlibs/math/asin.gno b/gnovm/stdlibs/math/asin.gno new file mode 100644 index 00000000000..d99f5664332 --- /dev/null +++ b/gnovm/stdlibs/math/asin.gno @@ -0,0 +1,61 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Floating-point arcsine and arccosine. + + They are implemented by computing the arctangent + after appropriate range reduction. +*/ + +// Asin returns the arcsine, in radians, of x. +// +// Special cases are: +// +// Asin(±0) = ±0 +// Asin(x) = NaN if x < -1 or x > 1 +func Asin(x float64) float64 { + return asin(x) +} + +func asin(x float64) float64 { + if x == 0 { + return x // special case + } + sign := false + if x < 0 { + x = -x + sign = true + } + if x > 1 { + return NaN() // special case + } + + temp := Sqrt(1 - x*x) + if x > 0.7 { + temp = Pi/2 - satan(temp/x) + } else { + temp = satan(x / temp) + } + + if sign { + temp = -temp + } + return temp +} + +// Acos returns the arccosine, in radians, of x. +// +// Special case is: +// +// Acos(x) = NaN if x < -1 or x > 1 +func Acos(x float64) float64 { + return acos(x) +} + +func acos(x float64) float64 { + return Pi/2 - Asin(x) +} diff --git a/gnovm/stdlibs/math/asinh.gno b/gnovm/stdlibs/math/asinh.gno new file mode 100644 index 00000000000..59fc3d7ad7e --- /dev/null +++ b/gnovm/stdlibs/math/asinh.gno @@ -0,0 +1,74 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code, the long comment, and the constants +// below are from FreeBSD's /usr/src/lib/msun/src/s_asinh.c +// and came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// +// asinh(x) +// Method : +// Based on +// asinh(x) = sign(x) * log [ |x| + sqrt(x*x+1) ] +// we have +// asinh(x) := x if 1+x*x=1, +// := sign(x)*(log(x)+ln2) for large |x|, else +// := sign(x)*log(2|x|+1/(|x|+sqrt(x*x+1))) if|x|>2, else +// := sign(x)*log1p(|x| + x**2/(1 + sqrt(1+x**2))) +// + +// Asinh returns the inverse hyperbolic sine of x. +// +// Special cases are: +// +// Asinh(±0) = ±0 +// Asinh(±Inf) = ±Inf +// Asinh(NaN) = NaN +func Asinh(x float64) float64 { + return asinh(x) +} + +func asinh(x float64) float64 { + const ( + Ln2 = 6.93147180559945286227e-01 // 0x3FE62E42FEFA39EF + NearZero = 1.0 / (1 << 28) // 2**-28 + Large = 1 << 28 // 2**28 + ) + // special cases + if IsNaN(x) || IsInf(x, 0) { + return x + } + sign := false + if x < 0 { + x = -x + sign = true + } + var temp float64 + switch { + case x > Large: + temp = Log(x) + Ln2 // |x| > 2**28 + case x > 2: + temp = Log(2*x + 1/(Sqrt(x*x+1)+x)) // 2**28 > |x| > 2.0 + case x < NearZero: + temp = x // |x| < 2**-28 + default: + temp = Log1p(x + x*x/(1+Sqrt(1+x*x))) // 2.0 > |x| > 2**-28 + } + if sign { + temp = -temp + } + return temp +} diff --git a/gnovm/stdlibs/math/atan.gno b/gnovm/stdlibs/math/atan.gno new file mode 100644 index 00000000000..23cae2dbfec --- /dev/null +++ b/gnovm/stdlibs/math/atan.gno @@ -0,0 +1,108 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Floating-point arctangent. +*/ + +// The original C code, the long comment, and the constants below were +// from http://netlib.sandia.gov/cephes/cmath/atan.c, available from +// http://www.netlib.org/cephes/cmath.tgz. +// The go code is a version of the original C. +// +// atan.c +// Inverse circular tangent (arctangent) +// +// SYNOPSIS: +// double x, y, atan(); +// y = atan( x ); +// +// DESCRIPTION: +// Returns radian angle between -pi/2 and +pi/2 whose tangent is x. +// +// Range reduction is from three intervals into the interval from zero to 0.66. +// The approximant uses a rational function of degree 4/5 of the form +// x + x**3 P(x)/Q(x). +// +// ACCURACY: +// Relative error: +// arithmetic domain # trials peak rms +// DEC -10, 10 50000 2.4e-17 8.3e-18 +// IEEE -10, 10 10^6 1.8e-16 5.0e-17 +// +// Cephes Math Library Release 2.8: June, 2000 +// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier +// +// The readme file at http://netlib.sandia.gov/cephes/ says: +// Some software in this archive may be from the book _Methods and +// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +// International, 1989) or from the Cephes Mathematical Library, a +// commercial product. In either event, it is copyrighted by the author. +// What you see here may be used freely but it comes with no support or +// guarantee. +// +// The two known misprints in the book are repaired here in the +// source listings for the gamma function and the incomplete beta +// integral. +// +// Stephen L. Moshier +// moshier@na-net.ornl.gov + +// xatan evaluates a series valid in the range [0, 0.66]. +func xatan(x float64) float64 { + const ( + P0 = -8.750608600031904122785e-01 + P1 = -1.615753718733365076637e+01 + P2 = -7.500855792314704667340e+01 + P3 = -1.228866684490136173410e+02 + P4 = -6.485021904942025371773e+01 + Q0 = +2.485846490142306297962e+01 + Q1 = +1.650270098316988542046e+02 + Q2 = +4.328810604912902668951e+02 + Q3 = +4.853903996359136964868e+02 + Q4 = +1.945506571482613964425e+02 + ) + z := x * x + z = z * ((((P0*z+P1)*z+P2)*z+P3)*z + P4) / (((((z+Q0)*z+Q1)*z+Q2)*z+Q3)*z + Q4) + z = x*z + x + return z +} + +// satan reduces its argument (known to be positive) +// to the range [0, 0.66] and calls xatan. +func satan(x float64) float64 { + const ( + Morebits = 6.123233995736765886130e-17 // pi/2 = PIO2 + Morebits + Tan3pio8 = 2.41421356237309504880 // tan(3*pi/8) + ) + if x <= 0.66 { + return xatan(x) + } + if x > Tan3pio8 { + return Pi/2 - xatan(1/x) + Morebits + } + return Pi/4 + xatan((x-1)/(x+1)) + 0.5*Morebits +} + +// Atan returns the arctangent, in radians, of x. +// +// Special cases are: +// +// Atan(±0) = ±0 +// Atan(±Inf) = ±Pi/2 +func Atan(x float64) float64 { + return atan(x) +} + +func atan(x float64) float64 { + if x == 0 { + return x + } + if x > 0 { + return satan(x) + } + return -satan(-x) +} diff --git a/gnovm/stdlibs/math/atan2.gno b/gnovm/stdlibs/math/atan2.gno new file mode 100644 index 00000000000..f474cf11e03 --- /dev/null +++ b/gnovm/stdlibs/math/atan2.gno @@ -0,0 +1,74 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// Atan2 returns the arc tangent of y/x, using +// the signs of the two to determine the quadrant +// of the return value. +// +// Special cases are (in order): +// +// Atan2(y, NaN) = NaN +// Atan2(NaN, x) = NaN +// Atan2(+0, x>=0) = +0 +// Atan2(-0, x>=0) = -0 +// Atan2(+0, x<=-0) = +Pi +// Atan2(-0, x<=-0) = -Pi +// Atan2(y>0, 0) = +Pi/2 +// Atan2(y<0, 0) = -Pi/2 +// Atan2(+Inf, +Inf) = +Pi/4 +// Atan2(-Inf, +Inf) = -Pi/4 +// Atan2(+Inf, -Inf) = 3Pi/4 +// Atan2(-Inf, -Inf) = -3Pi/4 +// Atan2(y, +Inf) = 0 +// Atan2(y>0, -Inf) = +Pi +// Atan2(y<0, -Inf) = -Pi +// Atan2(+Inf, x) = +Pi/2 +// Atan2(-Inf, x) = -Pi/2 +func Atan2(y, x float64) float64 { + return atan2(y, x) +} + +func atan2(y, x float64) float64 { + // special cases + switch { + case IsNaN(y) || IsNaN(x): + return NaN() + case y == 0: + if x >= 0 && !Signbit(x) { + return Copysign(0, y) + } + return Copysign(Pi, y) + case x == 0: + return Copysign(Pi/2, y) + case IsInf(x, 0): + if IsInf(x, 1) { + switch { + case IsInf(y, 0): + return Copysign(Pi/4, y) + default: + return Copysign(0, y) + } + } + switch { + case IsInf(y, 0): + return Copysign(3*Pi/4, y) + default: + return Copysign(Pi, y) + } + case IsInf(y, 0): + return Copysign(Pi/2, y) + } + + // Call atan and determine the quadrant. + q := Atan(y / x) + if x < 0 { + if q <= 0 { + return q + Pi + } + return q - Pi + } + return q +} diff --git a/gnovm/stdlibs/math/atanh.gno b/gnovm/stdlibs/math/atanh.gno new file mode 100644 index 00000000000..dcfe7e56382 --- /dev/null +++ b/gnovm/stdlibs/math/atanh.gno @@ -0,0 +1,82 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code, the long comment, and the constants +// below are from FreeBSD's /usr/src/lib/msun/src/e_atanh.c +// and came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// +// __ieee754_atanh(x) +// Method : +// 1. Reduce x to positive by atanh(-x) = -atanh(x) +// 2. For x>=0.5 +// 1 2x x +// atanh(x) = --- * log(1 + -------) = 0.5 * log1p(2 * --------) +// 2 1 - x 1 - x +// +// For x<0.5 +// atanh(x) = 0.5*log1p(2x+2x*x/(1-x)) +// +// Special cases: +// atanh(x) is NaN if |x| > 1 with signal; +// atanh(NaN) is that NaN with no signal; +// atanh(+-1) is +-INF with signal. +// + +// Atanh returns the inverse hyperbolic tangent of x. +// +// Special cases are: +// +// Atanh(1) = +Inf +// Atanh(±0) = ±0 +// Atanh(-1) = -Inf +// Atanh(x) = NaN if x < -1 or x > 1 +// Atanh(NaN) = NaN +func Atanh(x float64) float64 { + return atanh(x) +} + +func atanh(x float64) float64 { + const NearZero = 1.0 / (1 << 28) // 2**-28 + // special cases + switch { + case x < -1 || x > 1 || IsNaN(x): + return NaN() + case x == 1: + return Inf(1) + case x == -1: + return Inf(-1) + } + sign := false + if x < 0 { + x = -x + sign = true + } + var temp float64 + switch { + case x < NearZero: + temp = x + case x < 0.5: + temp = x + x + temp = 0.5 * Log1p(temp+temp*x/(1-x)) + default: + temp = 0.5 * Log1p((x+x)/(1-x)) + } + if sign { + temp = -temp + } + return temp +} diff --git a/gnovm/stdlibs/math/bits.gno b/gnovm/stdlibs/math/bits.gno index 6cf646d2f5e..c5cb93b1594 100644 --- a/gnovm/stdlibs/math/bits.gno +++ b/gnovm/stdlibs/math/bits.gno @@ -4,10 +4,6 @@ package math -import ( - imath "internal/math" -) - const ( uvnan = 0x7FF8000000000001 uvinf = 0x7FF0000000000000 @@ -28,11 +24,11 @@ func Inf(sign int) float64 { } else { v = uvneginf } - return imath.Float64frombits(v) + return Float64frombits(v) } // NaN returns an IEEE 754 “not-a-number” value. -func NaN() float64 { return imath.Float64frombits(uvnan) } +func NaN() float64 { return Float64frombits(uvnan) } // IsNaN reports whether f is an IEEE 754 “not-a-number” value. func IsNaN(f float64) (is bool) { diff --git a/gnovm/stdlibs/math/bits/bits.gno b/gnovm/stdlibs/math/bits/bits.gno new file mode 100644 index 00000000000..381244a1118 --- /dev/null +++ b/gnovm/stdlibs/math/bits/bits.gno @@ -0,0 +1,604 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go run make_tables.go + +// Package bits implements bit counting and manipulation +// functions for the predeclared unsigned integer types. +// +// Functions in this package may be implemented directly by +// the compiler, for better performance. For those functions +// the code in this package will not be used. Which +// functions are implemented by the compiler depends on the +// architecture and the Go release. +package bits + +const uintSize = 32 << (^uint(0) >> 63) // 32 or 64 + +// UintSize is the size of a uint in bits. +const UintSize = uintSize + +// --- LeadingZeros --- + +// LeadingZeros returns the number of leading zero bits in x; the result is UintSize for x == 0. +func LeadingZeros(x uint) int { return UintSize - Len(x) } + +// LeadingZeros8 returns the number of leading zero bits in x; the result is 8 for x == 0. +func LeadingZeros8(x uint8) int { return 8 - Len8(x) } + +// LeadingZeros16 returns the number of leading zero bits in x; the result is 16 for x == 0. +func LeadingZeros16(x uint16) int { return 16 - Len16(x) } + +// LeadingZeros32 returns the number of leading zero bits in x; the result is 32 for x == 0. +func LeadingZeros32(x uint32) int { return 32 - Len32(x) } + +// LeadingZeros64 returns the number of leading zero bits in x; the result is 64 for x == 0. +func LeadingZeros64(x uint64) int { return 64 - Len64(x) } + +// --- TrailingZeros --- + +// See http://supertech.csail.mit.edu/papers/debruijn.pdf +const deBruijn32 = 0x077CB531 + +var deBruijn32tab = [32]byte{ + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9, +} + +const deBruijn64 = 0x03f79d71b4ca8b09 + +var deBruijn64tab = [64]byte{ + 0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4, + 62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5, + 63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11, + 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6, +} + +// TrailingZeros returns the number of trailing zero bits in x; the result is UintSize for x == 0. +func TrailingZeros(x uint) int { + if UintSize == 32 { + return TrailingZeros32(uint32(x)) + } + return TrailingZeros64(uint64(x)) +} + +// TrailingZeros8 returns the number of trailing zero bits in x; the result is 8 for x == 0. +func TrailingZeros8(x uint8) int { + return int(ntz8tab[x]) +} + +// TrailingZeros16 returns the number of trailing zero bits in x; the result is 16 for x == 0. +func TrailingZeros16(x uint16) int { + if x == 0 { + return 16 + } + // see comment in TrailingZeros64 + return int(deBruijn32tab[uint32(x&-x)*deBruijn32>>(32-5)]) +} + +// TrailingZeros32 returns the number of trailing zero bits in x; the result is 32 for x == 0. +func TrailingZeros32(x uint32) int { + if x == 0 { + return 32 + } + // see comment in TrailingZeros64 + return int(deBruijn32tab[(x&-x)*deBruijn32>>(32-5)]) +} + +// TrailingZeros64 returns the number of trailing zero bits in x; the result is 64 for x == 0. +func TrailingZeros64(x uint64) int { + if x == 0 { + return 64 + } + // If popcount is fast, replace code below with return popcount(^x & (x - 1)). + // + // x & -x leaves only the right-most bit set in the word. Let k be the + // index of that bit. Since only a single bit is set, the value is two + // to the power of k. Multiplying by a power of two is equivalent to + // left shifting, in this case by k bits. The de Bruijn (64 bit) constant + // is such that all six bit, consecutive substrings are distinct. + // Therefore, if we have a left shifted version of this constant we can + // find by how many bits it was shifted by looking at which six bit + // substring ended up at the top of the word. + // (Knuth, volume 4, section 7.3.1) + return int(deBruijn64tab[(x&-x)*deBruijn64>>(64-6)]) +} + +// --- OnesCount --- + +const ( + m0 = 0x5555555555555555 // 01010101 ... + m1 = 0x3333333333333333 // 00110011 ... + m2 = 0x0f0f0f0f0f0f0f0f // 00001111 ... + m3 = 0x00ff00ff00ff00ff // etc. + m4 = 0x0000ffff0000ffff +) + +// OnesCount returns the number of one bits ("population count") in x. +func OnesCount(x uint) int { + if UintSize == 32 { + return OnesCount32(uint32(x)) + } + return OnesCount64(uint64(x)) +} + +// OnesCount8 returns the number of one bits ("population count") in x. +func OnesCount8(x uint8) int { + return int(pop8tab[x]) +} + +// OnesCount16 returns the number of one bits ("population count") in x. +func OnesCount16(x uint16) int { + return int(pop8tab[x>>8] + pop8tab[x&0xff]) +} + +// OnesCount32 returns the number of one bits ("population count") in x. +func OnesCount32(x uint32) int { + return int(pop8tab[x>>24] + pop8tab[x>>16&0xff] + pop8tab[x>>8&0xff] + pop8tab[x&0xff]) +} + +// OnesCount64 returns the number of one bits ("population count") in x. +func OnesCount64(x uint64) int { + // Implementation: Parallel summing of adjacent bits. + // See "Hacker's Delight", Chap. 5: Counting Bits. + // The following pattern shows the general approach: + // + // x = x>>1&(m0&m) + x&(m0&m) + // x = x>>2&(m1&m) + x&(m1&m) + // x = x>>4&(m2&m) + x&(m2&m) + // x = x>>8&(m3&m) + x&(m3&m) + // x = x>>16&(m4&m) + x&(m4&m) + // x = x>>32&(m5&m) + x&(m5&m) + // return int(x) + // + // Masking (& operations) can be left away when there's no + // danger that a field's sum will carry over into the next + // field: Since the result cannot be > 64, 8 bits is enough + // and we can ignore the masks for the shifts by 8 and up. + // Per "Hacker's Delight", the first line can be simplified + // more, but it saves at best one instruction, so we leave + // it alone for clarity. + const m = 1<<64 - 1 + x = x>>1&(m0&m) + x&(m0&m) + x = x>>2&(m1&m) + x&(m1&m) + x = (x>>4 + x) & (m2 & m) + x += x >> 8 + x += x >> 16 + x += x >> 32 + return int(x) & (1<<7 - 1) +} + +// --- RotateLeft --- + +// RotateLeft returns the value of x rotated left by (k mod UintSize) bits. +// To rotate x right by k bits, call RotateLeft(x, -k). +// +// This function's execution time does not depend on the inputs. +func RotateLeft(x uint, k int) uint { + if UintSize == 32 { + return uint(RotateLeft32(uint32(x), k)) + } + return uint(RotateLeft64(uint64(x), k)) +} + +// RotateLeft8 returns the value of x rotated left by (k mod 8) bits. +// To rotate x right by k bits, call RotateLeft8(x, -k). +// +// This function's execution time does not depend on the inputs. +func RotateLeft8(x uint8, k int) uint8 { + const n = 8 + s := uint(k) & (n - 1) + return x<>(n-s) +} + +// RotateLeft16 returns the value of x rotated left by (k mod 16) bits. +// To rotate x right by k bits, call RotateLeft16(x, -k). +// +// This function's execution time does not depend on the inputs. +func RotateLeft16(x uint16, k int) uint16 { + const n = 16 + s := uint(k) & (n - 1) + return x<>(n-s) +} + +// RotateLeft32 returns the value of x rotated left by (k mod 32) bits. +// To rotate x right by k bits, call RotateLeft32(x, -k). +// +// This function's execution time does not depend on the inputs. +func RotateLeft32(x uint32, k int) uint32 { + const n = 32 + s := uint(k) & (n - 1) + return x<>(n-s) +} + +// RotateLeft64 returns the value of x rotated left by (k mod 64) bits. +// To rotate x right by k bits, call RotateLeft64(x, -k). +// +// This function's execution time does not depend on the inputs. +func RotateLeft64(x uint64, k int) uint64 { + const n = 64 + s := uint(k) & (n - 1) + return x<>(n-s) +} + +// --- Reverse --- + +// Reverse returns the value of x with its bits in reversed order. +func Reverse(x uint) uint { + if UintSize == 32 { + return uint(Reverse32(uint32(x))) + } + return uint(Reverse64(uint64(x))) +} + +// Reverse8 returns the value of x with its bits in reversed order. +func Reverse8(x uint8) uint8 { + return rev8tab[x] +} + +// Reverse16 returns the value of x with its bits in reversed order. +func Reverse16(x uint16) uint16 { + return uint16(rev8tab[x>>8]) | uint16(rev8tab[x&0xff])<<8 +} + +// Reverse32 returns the value of x with its bits in reversed order. +func Reverse32(x uint32) uint32 { + const m = 1<<32 - 1 + x = x>>1&(m0&m) | x&(m0&m)<<1 + x = x>>2&(m1&m) | x&(m1&m)<<2 + x = x>>4&(m2&m) | x&(m2&m)<<4 + return ReverseBytes32(x) +} + +// Reverse64 returns the value of x with its bits in reversed order. +func Reverse64(x uint64) uint64 { + const m = 1<<64 - 1 + x = x>>1&(m0&m) | x&(m0&m)<<1 + x = x>>2&(m1&m) | x&(m1&m)<<2 + x = x>>4&(m2&m) | x&(m2&m)<<4 + return ReverseBytes64(x) +} + +// --- ReverseBytes --- + +// ReverseBytes returns the value of x with its bytes in reversed order. +// +// This function's execution time does not depend on the inputs. +func ReverseBytes(x uint) uint { + if UintSize == 32 { + return uint(ReverseBytes32(uint32(x))) + } + return uint(ReverseBytes64(uint64(x))) +} + +// ReverseBytes16 returns the value of x with its bytes in reversed order. +// +// This function's execution time does not depend on the inputs. +func ReverseBytes16(x uint16) uint16 { + return x>>8 | x<<8 +} + +// ReverseBytes32 returns the value of x with its bytes in reversed order. +// +// This function's execution time does not depend on the inputs. +func ReverseBytes32(x uint32) uint32 { + const m = 1<<32 - 1 + x = x>>8&(m3&m) | x&(m3&m)<<8 + return x>>16 | x<<16 +} + +// ReverseBytes64 returns the value of x with its bytes in reversed order. +// +// This function's execution time does not depend on the inputs. +func ReverseBytes64(x uint64) uint64 { + const m = 1<<64 - 1 + x = x>>8&(m3&m) | x&(m3&m)<<8 + x = x>>16&(m4&m) | x&(m4&m)<<16 + return x>>32 | x<<32 +} + +// --- Len --- + +// Len returns the minimum number of bits required to represent x; the result is 0 for x == 0. +func Len(x uint) int { + if UintSize == 32 { + return Len32(uint32(x)) + } + return Len64(uint64(x)) +} + +// Len8 returns the minimum number of bits required to represent x; the result is 0 for x == 0. +func Len8(x uint8) int { + return int(len8tab[x]) +} + +// Len16 returns the minimum number of bits required to represent x; the result is 0 for x == 0. +func Len16(x uint16) (n int) { + if x >= 1<<8 { + x >>= 8 + n = 8 + } + return n + int(len8tab[x]) +} + +// Len32 returns the minimum number of bits required to represent x; the result is 0 for x == 0. +func Len32(x uint32) (n int) { + if x >= 1<<16 { + x >>= 16 + n = 16 + } + if x >= 1<<8 { + x >>= 8 + n += 8 + } + return n + int(len8tab[x]) +} + +// Len64 returns the minimum number of bits required to represent x; the result is 0 for x == 0. +func Len64(x uint64) (n int) { + if x >= 1<<32 { + x >>= 32 + n = 32 + } + if x >= 1<<16 { + x >>= 16 + n += 16 + } + if x >= 1<<8 { + x >>= 8 + n += 8 + } + return n + int(len8tab[x]) +} + +// --- Add with carry --- + +// Add returns the sum with carry of x, y and carry: sum = x + y + carry. +// The carry input must be 0 or 1; otherwise the behavior is undefined. +// The carryOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Add(x, y, carry uint) (sum, carryOut uint) { + if UintSize == 32 { + s32, c32 := Add32(uint32(x), uint32(y), uint32(carry)) + return uint(s32), uint(c32) + } + s64, c64 := Add64(uint64(x), uint64(y), uint64(carry)) + return uint(s64), uint(c64) +} + +// Add32 returns the sum with carry of x, y and carry: sum = x + y + carry. +// The carry input must be 0 or 1; otherwise the behavior is undefined. +// The carryOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Add32(x, y, carry uint32) (sum, carryOut uint32) { + sum64 := uint64(x) + uint64(y) + uint64(carry) + sum = uint32(sum64) + carryOut = uint32(sum64 >> 32) + return +} + +// Add64 returns the sum with carry of x, y and carry: sum = x + y + carry. +// The carry input must be 0 or 1; otherwise the behavior is undefined. +// The carryOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Add64(x, y, carry uint64) (sum, carryOut uint64) { + sum = x + y + carry + // The sum will overflow if both top bits are set (x & y) or if one of them + // is (x | y), and a carry from the lower place happened. If such a carry + // happens, the top bit will be 1 + 0 + 1 = 0 (&^ sum). + carryOut = ((x & y) | ((x | y) &^ sum)) >> 63 + return +} + +// --- Subtract with borrow --- + +// Sub returns the difference of x, y and borrow: diff = x - y - borrow. +// The borrow input must be 0 or 1; otherwise the behavior is undefined. +// The borrowOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Sub(x, y, borrow uint) (diff, borrowOut uint) { + if UintSize == 32 { + d32, b32 := Sub32(uint32(x), uint32(y), uint32(borrow)) + return uint(d32), uint(b32) + } + d64, b64 := Sub64(uint64(x), uint64(y), uint64(borrow)) + return uint(d64), uint(b64) +} + +// Sub32 returns the difference of x, y and borrow, diff = x - y - borrow. +// The borrow input must be 0 or 1; otherwise the behavior is undefined. +// The borrowOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Sub32(x, y, borrow uint32) (diff, borrowOut uint32) { + diff = x - y - borrow + // The difference will underflow if the top bit of x is not set and the top + // bit of y is set (^x & y) or if they are the same (^(x ^ y)) and a borrow + // from the lower place happens. If that borrow happens, the result will be + // 1 - 1 - 1 = 0 - 0 - 1 = 1 (& diff). + borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 31 + return +} + +// Sub64 returns the difference of x, y and borrow: diff = x - y - borrow. +// The borrow input must be 0 or 1; otherwise the behavior is undefined. +// The borrowOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Sub64(x, y, borrow uint64) (diff, borrowOut uint64) { + diff = x - y - borrow + // See Sub32 for the bit logic. + borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 63 + return +} + +// --- Full-width multiply --- + +// Mul returns the full-width product of x and y: (hi, lo) = x * y +// with the product bits' upper half returned in hi and the lower +// half returned in lo. +// +// This function's execution time does not depend on the inputs. +func Mul(x, y uint) (hi, lo uint) { + if UintSize == 32 { + h, l := Mul32(uint32(x), uint32(y)) + return uint(h), uint(l) + } + h, l := Mul64(uint64(x), uint64(y)) + return uint(h), uint(l) +} + +// Mul32 returns the 64-bit product of x and y: (hi, lo) = x * y +// with the product bits' upper half returned in hi and the lower +// half returned in lo. +// +// This function's execution time does not depend on the inputs. +func Mul32(x, y uint32) (hi, lo uint32) { + tmp := uint64(x) * uint64(y) + hi, lo = uint32(tmp>>32), uint32(tmp) + return +} + +// Mul64 returns the 128-bit product of x and y: (hi, lo) = x * y +// with the product bits' upper half returned in hi and the lower +// half returned in lo. +// +// This function's execution time does not depend on the inputs. +func Mul64(x, y uint64) (hi, lo uint64) { + const mask32 = 1<<32 - 1 + x0 := x & mask32 + x1 := x >> 32 + y0 := y & mask32 + y1 := y >> 32 + w0 := x0 * y0 + t := x1*y0 + w0>>32 + w1 := t & mask32 + w2 := t >> 32 + w1 += x0 * y1 + hi = x1*y1 + w2 + w1>>32 + lo = x * y + return +} + +// --- Full-width divide --- + +// Div returns the quotient and remainder of (hi, lo) divided by y: +// quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper +// half in parameter hi and the lower half in parameter lo. +// Div panics for y == 0 (division by zero) or y <= hi (quotient overflow). +func Div(hi, lo, y uint) (quo, rem uint) { + if UintSize == 32 { + q, r := Div32(uint32(hi), uint32(lo), uint32(y)) + return uint(q), uint(r) + } + q, r := Div64(uint64(hi), uint64(lo), uint64(y)) + return uint(q), uint(r) +} + +// Div32 returns the quotient and remainder of (hi, lo) divided by y: +// quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper +// half in parameter hi and the lower half in parameter lo. +// Div32 panics for y == 0 (division by zero) or y <= hi (quotient overflow). +func Div32(hi, lo, y uint32) (quo, rem uint32) { + if y != 0 && y <= hi { + panic(overflowError) + } + if y == 0 { + panic(divideError) + } + z := uint64(hi)<<32 | uint64(lo) + quo, rem = uint32(z/uint64(y)), uint32(z%uint64(y)) + return +} + +// Div64 returns the quotient and remainder of (hi, lo) divided by y: +// quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper +// half in parameter hi and the lower half in parameter lo. +// Div64 panics for y == 0 (division by zero) or y <= hi (quotient overflow). +func Div64(hi, lo, y uint64) (quo, rem uint64) { + if y == 0 { + panic(divideError) + } + if y <= hi { + panic(overflowError) + } + + // If high part is zero, we can directly return the results. + if hi == 0 { + return lo / y, lo % y + } + + s := uint(LeadingZeros64(y)) + y <<= s + + const ( + two32 = 1 << 32 + mask32 = two32 - 1 + ) + yn1 := y >> 32 + yn0 := y & mask32 + un32 := hi<>(64-s) + un10 := lo << s + un1 := un10 >> 32 + un0 := un10 & mask32 + q1 := un32 / yn1 + rhat := un32 - q1*yn1 + + for q1 >= two32 || q1*yn0 > two32*rhat+un1 { + q1-- + rhat += yn1 + if rhat >= two32 { + break + } + } + + un21 := un32*two32 + un1 - q1*y + q0 := un21 / yn1 + rhat = un21 - q0*yn1 + + for q0 >= two32 || q0*yn0 > two32*rhat+un0 { + q0-- + rhat += yn1 + if rhat >= two32 { + break + } + } + + return q1*two32 + q0, (un21*two32 + un0 - q0*y) >> s +} + +// Rem returns the remainder of (hi, lo) divided by y. Rem panics for +// y == 0 (division by zero) but, unlike Div, it doesn't panic on a +// quotient overflow. +func Rem(hi, lo, y uint) uint { + if UintSize == 32 { + return uint(Rem32(uint32(hi), uint32(lo), uint32(y))) + } + return uint(Rem64(uint64(hi), uint64(lo), uint64(y))) +} + +// Rem32 returns the remainder of (hi, lo) divided by y. Rem32 panics +// for y == 0 (division by zero) but, unlike Div32, it doesn't panic +// on a quotient overflow. +func Rem32(hi, lo, y uint32) uint32 { + return uint32((uint64(hi)<<32 | uint64(lo)) % uint64(y)) +} + +// Rem64 returns the remainder of (hi, lo) divided by y. Rem64 panics +// for y == 0 (division by zero) but, unlike Div64, it doesn't panic +// on a quotient overflow. +func Rem64(hi, lo, y uint64) uint64 { + // We scale down hi so that hi < y, then use Div64 to compute the + // rem with the guarantee that it won't panic on quotient overflow. + // Given that + // hi ≡ hi%y (mod y) + // we have + // hi<<64 + lo ≡ (hi%y)<<64 + lo (mod y) + _, rem := Div64(hi%y, lo, y) + return rem +} diff --git a/gnovm/stdlibs/math/bits/bits_errors.gno b/gnovm/stdlibs/math/bits/bits_errors.gno new file mode 100644 index 00000000000..cf8a5198b2f --- /dev/null +++ b/gnovm/stdlibs/math/bits/bits_errors.gno @@ -0,0 +1,13 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bits + +import ( + "errors" +) + +var overflowError = errors.New("math/bits: integer overflow") + +var divideError = errors.New("math/bits: integer divide by zero") diff --git a/gnovm/stdlibs/math/bits/bits_tables.gno b/gnovm/stdlibs/math/bits/bits_tables.gno new file mode 100644 index 00000000000..f869b8d5c37 --- /dev/null +++ b/gnovm/stdlibs/math/bits/bits_tables.gno @@ -0,0 +1,79 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated by go run make_tables.go. DO NOT EDIT. + +package bits + +const ntz8tab = "" + + "\x08\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x06\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x07\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x06\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + +const pop8tab = "" + + "\x00\x01\x01\x02\x01\x02\x02\x03\x01\x02\x02\x03\x02\x03\x03\x04" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x04\x05\x05\x06\x05\x06\x06\x07\x05\x06\x06\x07\x06\x07\x07\x08" + +const rev8tab = "" + + "\x00\x80\x40\xc0\x20\xa0\x60\xe0\x10\x90\x50\xd0\x30\xb0\x70\xf0" + + "\x08\x88\x48\xc8\x28\xa8\x68\xe8\x18\x98\x58\xd8\x38\xb8\x78\xf8" + + "\x04\x84\x44\xc4\x24\xa4\x64\xe4\x14\x94\x54\xd4\x34\xb4\x74\xf4" + + "\x0c\x8c\x4c\xcc\x2c\xac\x6c\xec\x1c\x9c\x5c\xdc\x3c\xbc\x7c\xfc" + + "\x02\x82\x42\xc2\x22\xa2\x62\xe2\x12\x92\x52\xd2\x32\xb2\x72\xf2" + + "\x0a\x8a\x4a\xca\x2a\xaa\x6a\xea\x1a\x9a\x5a\xda\x3a\xba\x7a\xfa" + + "\x06\x86\x46\xc6\x26\xa6\x66\xe6\x16\x96\x56\xd6\x36\xb6\x76\xf6" + + "\x0e\x8e\x4e\xce\x2e\xae\x6e\xee\x1e\x9e\x5e\xde\x3e\xbe\x7e\xfe" + + "\x01\x81\x41\xc1\x21\xa1\x61\xe1\x11\x91\x51\xd1\x31\xb1\x71\xf1" + + "\x09\x89\x49\xc9\x29\xa9\x69\xe9\x19\x99\x59\xd9\x39\xb9\x79\xf9" + + "\x05\x85\x45\xc5\x25\xa5\x65\xe5\x15\x95\x55\xd5\x35\xb5\x75\xf5" + + "\x0d\x8d\x4d\xcd\x2d\xad\x6d\xed\x1d\x9d\x5d\xdd\x3d\xbd\x7d\xfd" + + "\x03\x83\x43\xc3\x23\xa3\x63\xe3\x13\x93\x53\xd3\x33\xb3\x73\xf3" + + "\x0b\x8b\x4b\xcb\x2b\xab\x6b\xeb\x1b\x9b\x5b\xdb\x3b\xbb\x7b\xfb" + + "\x07\x87\x47\xc7\x27\xa7\x67\xe7\x17\x97\x57\xd7\x37\xb7\x77\xf7" + + "\x0f\x8f\x4f\xcf\x2f\xaf\x6f\xef\x1f\x9f\x5f\xdf\x3f\xbf\x7f\xff" + +const len8tab = "" + + "\x00\x01\x02\x02\x03\x03\x03\x03\x04\x04\x04\x04\x04\x04\x04\x04" + + "\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05" + + "\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06" + + "\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" diff --git a/gnovm/stdlibs/math/bits/bits_test.gno b/gnovm/stdlibs/math/bits/bits_test.gno new file mode 100644 index 00000000000..55b0e3c117b --- /dev/null +++ b/gnovm/stdlibs/math/bits/bits_test.gno @@ -0,0 +1,1332 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bits + +import ( + "testing" +) + +func TestLeadingZeros(t *testing.T) { + for i := 0; i < 256; i++ { + nlz := tab[i].nlz + for k := 0; k < 64-8; k++ { + x := uint64(i) << uint(k) + if x <= 1<<8-1 { + got := LeadingZeros8(uint8(x)) + want := nlz - k + (8 - 8) + if x == 0 { + want = 8 + } + if got != want { + t.Fatalf("LeadingZeros8(%#02x) == %d; want %d", x, got, want) + } + } + + if x <= 1<<16-1 { + got := LeadingZeros16(uint16(x)) + want := nlz - k + (16 - 8) + if x == 0 { + want = 16 + } + if got != want { + t.Fatalf("LeadingZeros16(%#04x) == %d; want %d", x, got, want) + } + } + + if x <= 1<<32-1 { + got := LeadingZeros32(uint32(x)) + want := nlz - k + (32 - 8) + if x == 0 { + want = 32 + } + if got != want { + t.Fatalf("LeadingZeros32(%#08x) == %d; want %d", x, got, want) + } + if UintSize == 32 { + got = LeadingZeros(uint(x)) + if got != want { + t.Fatalf("LeadingZeros(%#08x) == %d; want %d", x, got, want) + } + } + } + + if x <= 1<<64-1 { + got := LeadingZeros64(uint64(x)) + want := nlz - k + (64 - 8) + if x == 0 { + want = 64 + } + if got != want { + t.Fatalf("LeadingZeros64(%#016x) == %d; want %d", x, got, want) + } + if UintSize == 64 { + got = LeadingZeros(uint(x)) + if got != want { + t.Fatalf("LeadingZeros(%#016x) == %d; want %d", x, got, want) + } + } + } + } + } +} + +// Exported (global) variable serving as input for some +// of the benchmarks to ensure side-effect free calls +// are not optimized away. +var Input uint64 = DeBruijn64 + +// Exported (global) variable to store function results +// during benchmarking to ensure side-effect free calls +// are not optimized away. +var Output int + +func BenchmarkLeadingZeros(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += LeadingZeros(uint(Input) >> (uint(i) % UintSize)) + } + Output = s +} + +func BenchmarkLeadingZeros8(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += LeadingZeros8(uint8(Input) >> (uint(i) % 8)) + } + Output = s +} + +func BenchmarkLeadingZeros16(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += LeadingZeros16(uint16(Input) >> (uint(i) % 16)) + } + Output = s +} + +func BenchmarkLeadingZeros32(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += LeadingZeros32(uint32(Input) >> (uint(i) % 32)) + } + Output = s +} + +func BenchmarkLeadingZeros64(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += LeadingZeros64(uint64(Input) >> (uint(i) % 64)) + } + Output = s +} + +func TestTrailingZeros(t *testing.T) { + for i := 0; i < 256; i++ { + ntz := tab[i].ntz + for k := 0; k < 64-8; k++ { + x := uint64(i) << uint(k) + want := ntz + k + if x <= 1<<8-1 { + got := TrailingZeros8(uint8(x)) + if x == 0 { + want = 8 + } + if got != want { + t.Fatalf("TrailingZeros8(%#02x) == %d; want %d", x, got, want) + } + } + + if x <= 1<<16-1 { + got := TrailingZeros16(uint16(x)) + if x == 0 { + want = 16 + } + if got != want { + t.Fatalf("TrailingZeros16(%#04x) == %d; want %d", x, got, want) + } + } + + if x <= 1<<32-1 { + got := TrailingZeros32(uint32(x)) + if x == 0 { + want = 32 + } + if got != want { + t.Fatalf("TrailingZeros32(%#08x) == %d; want %d", x, got, want) + } + if UintSize == 32 { + got = TrailingZeros(uint(x)) + if got != want { + t.Fatalf("TrailingZeros(%#08x) == %d; want %d", x, got, want) + } + } + } + + if x <= 1<<64-1 { + got := TrailingZeros64(uint64(x)) + if x == 0 { + want = 64 + } + if got != want { + t.Fatalf("TrailingZeros64(%#016x) == %d; want %d", x, got, want) + } + if UintSize == 64 { + got = TrailingZeros(uint(x)) + if got != want { + t.Fatalf("TrailingZeros(%#016x) == %d; want %d", x, got, want) + } + } + } + } + } +} + +func BenchmarkTrailingZeros(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += TrailingZeros(uint(Input) << (uint(i) % UintSize)) + } + Output = s +} + +func BenchmarkTrailingZeros8(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += TrailingZeros8(uint8(Input) << (uint(i) % 8)) + } + Output = s +} + +func BenchmarkTrailingZeros16(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += TrailingZeros16(uint16(Input) << (uint(i) % 16)) + } + Output = s +} + +func BenchmarkTrailingZeros32(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += TrailingZeros32(uint32(Input) << (uint(i) % 32)) + } + Output = s +} + +func BenchmarkTrailingZeros64(b *testing.B) { + var s int + for i := 0; i < b.N; i++ { + s += TrailingZeros64(uint64(Input) << (uint(i) % 64)) + } + Output = s +} + +func TestOnesCount(t *testing.T) { + var x uint64 + for i := 0; i <= 64; i++ { + testOnesCount(t, x, i) + x = x<<1 | 1 + } + + for i := 64; i >= 0; i-- { + testOnesCount(t, x, i) + x = x << 1 + } + + for i := 0; i < 256; i++ { + for k := 0; k < 64-8; k++ { + testOnesCount(t, uint64(i)<>(8-k&0x7) + if got8 != want8 { + t.Fatalf("RotateLeft8(%#02x, %d) == %#02x; want %#02x", x8, k, got8, want8) + } + got8 = RotateLeft8(want8, -int(k)) + if got8 != x8 { + t.Fatalf("RotateLeft8(%#02x, -%d) == %#02x; want %#02x", want8, k, got8, x8) + } + + x16 := uint16(m) + got16 := RotateLeft16(x16, int(k)) + want16 := x16<<(k&0xf) | x16>>(16-k&0xf) + if got16 != want16 { + t.Fatalf("RotateLeft16(%#04x, %d) == %#04x; want %#04x", x16, k, got16, want16) + } + got16 = RotateLeft16(want16, -int(k)) + if got16 != x16 { + t.Fatalf("RotateLeft16(%#04x, -%d) == %#04x; want %#04x", want16, k, got16, x16) + } + + x32 := uint32(m) + got32 := RotateLeft32(x32, int(k)) + want32 := x32<<(k&0x1f) | x32>>(32-k&0x1f) + if got32 != want32 { + t.Fatalf("RotateLeft32(%#08x, %d) == %#08x; want %#08x", x32, k, got32, want32) + } + got32 = RotateLeft32(want32, -int(k)) + if got32 != x32 { + t.Fatalf("RotateLeft32(%#08x, -%d) == %#08x; want %#08x", want32, k, got32, x32) + } + if UintSize == 32 { + x := uint(m) + got := RotateLeft(x, int(k)) + want := x<<(k&0x1f) | x>>(32-k&0x1f) + if got != want { + t.Fatalf("RotateLeft(%#08x, %d) == %#08x; want %#08x", x, k, got, want) + } + got = RotateLeft(want, -int(k)) + if got != x { + t.Fatalf("RotateLeft(%#08x, -%d) == %#08x; want %#08x", want, k, got, x) + } + } + + x64 := uint64(m) + got64 := RotateLeft64(x64, int(k)) + want64 := x64<<(k&0x3f) | x64>>(64-k&0x3f) + if got64 != want64 { + t.Fatalf("RotateLeft64(%#016x, %d) == %#016x; want %#016x", x64, k, got64, want64) + } + got64 = RotateLeft64(want64, -int(k)) + if got64 != x64 { + t.Fatalf("RotateLeft64(%#016x, -%d) == %#016x; want %#016x", want64, k, got64, x64) + } + if UintSize == 64 { + x := uint(m) + got := RotateLeft(x, int(k)) + want := x<<(k&0x3f) | x>>(64-k&0x3f) + if got != want { + t.Fatalf("RotateLeft(%#016x, %d) == %#016x; want %#016x", x, k, got, want) + } + got = RotateLeft(want, -int(k)) + if got != x { + t.Fatalf("RotateLeft(%#08x, -%d) == %#08x; want %#08x", want, k, got, x) + } + } + } +} + +func BenchmarkRotateLeft(b *testing.B) { + var s uint + for i := 0; i < b.N; i++ { + s += RotateLeft(uint(Input), i) + } + Output = int(s) +} + +func BenchmarkRotateLeft8(b *testing.B) { + var s uint8 + for i := 0; i < b.N; i++ { + s += RotateLeft8(uint8(Input), i) + } + Output = int(s) +} + +func BenchmarkRotateLeft16(b *testing.B) { + var s uint16 + for i := 0; i < b.N; i++ { + s += RotateLeft16(uint16(Input), i) + } + Output = int(s) +} + +func BenchmarkRotateLeft32(b *testing.B) { + var s uint32 + for i := 0; i < b.N; i++ { + s += RotateLeft32(uint32(Input), i) + } + Output = int(s) +} + +func BenchmarkRotateLeft64(b *testing.B) { + var s uint64 + for i := 0; i < b.N; i++ { + s += RotateLeft64(uint64(Input), i) + } + Output = int(s) +} + +func TestReverse(t *testing.T) { + // test each bit + for i := uint(0); i < 64; i++ { + testReverse(t, uint64(1)<> (64 - 8)) + if got8 != want8 { + t.Fatalf("Reverse8(%#02x) == %#02x; want %#02x", x8, got8, want8) + } + + x16 := uint16(x64) + got16 := Reverse16(x16) + want16 := uint16(want64 >> (64 - 16)) + if got16 != want16 { + t.Fatalf("Reverse16(%#04x) == %#04x; want %#04x", x16, got16, want16) + } + + x32 := uint32(x64) + got32 := Reverse32(x32) + want32 := uint32(want64 >> (64 - 32)) + if got32 != want32 { + t.Fatalf("Reverse32(%#08x) == %#08x; want %#08x", x32, got32, want32) + } + if UintSize == 32 { + x := uint(x32) + got := Reverse(x) + want := uint(want32) + if got != want { + t.Fatalf("Reverse(%#08x) == %#08x; want %#08x", x, got, want) + } + } + + got64 := Reverse64(x64) + if got64 != want64 { + t.Fatalf("Reverse64(%#016x) == %#016x; want %#016x", x64, got64, want64) + } + if UintSize == 64 { + x := uint(x64) + got := Reverse(x) + want := uint(want64) + if got != want { + t.Fatalf("Reverse(%#08x) == %#016x; want %#016x", x, got, want) + } + } +} + +func BenchmarkReverse(b *testing.B) { + var s uint + for i := 0; i < b.N; i++ { + s += Reverse(uint(i)) + } + Output = int(s) +} + +func BenchmarkReverse8(b *testing.B) { + var s uint8 + for i := 0; i < b.N; i++ { + s += Reverse8(uint8(i)) + } + Output = int(s) +} + +func BenchmarkReverse16(b *testing.B) { + var s uint16 + for i := 0; i < b.N; i++ { + s += Reverse16(uint16(i)) + } + Output = int(s) +} + +func BenchmarkReverse32(b *testing.B) { + var s uint32 + for i := 0; i < b.N; i++ { + s += Reverse32(uint32(i)) + } + Output = int(s) +} + +func BenchmarkReverse64(b *testing.B) { + var s uint64 + for i := 0; i < b.N; i++ { + s += Reverse64(uint64(i)) + } + Output = int(s) +} + +func TestReverseBytes(t *testing.T) { + for _, test := range []struct { + x, r uint64 + }{ + {0, 0}, + {0x01, 0x01 << 56}, + {0x0123, 0x2301 << 48}, + {0x012345, 0x452301 << 40}, + {0x01234567, 0x67452301 << 32}, + {0x0123456789, 0x8967452301 << 24}, + {0x0123456789ab, 0xab8967452301 << 16}, + {0x0123456789abcd, 0xcdab8967452301 << 8}, + {0x0123456789abcdef, 0xefcdab8967452301 << 0}, + } { + testReverseBytes(t, test.x, test.r) + testReverseBytes(t, test.r, test.x) + } +} + +func testReverseBytes(t *testing.T, x64, want64 uint64) { + x16 := uint16(x64) + got16 := ReverseBytes16(x16) + want16 := uint16(want64 >> (64 - 16)) + if got16 != want16 { + t.Fatalf("ReverseBytes16(%#04x) == %#04x; want %#04x", x16, got16, want16) + } + + x32 := uint32(x64) + got32 := ReverseBytes32(x32) + want32 := uint32(want64 >> (64 - 32)) + if got32 != want32 { + t.Fatalf("ReverseBytes32(%#08x) == %#08x; want %#08x", x32, got32, want32) + } + if UintSize == 32 { + x := uint(x32) + got := ReverseBytes(x) + want := uint(want32) + if got != want { + t.Fatalf("ReverseBytes(%#08x) == %#08x; want %#08x", x, got, want) + } + } + + got64 := ReverseBytes64(x64) + if got64 != want64 { + t.Fatalf("ReverseBytes64(%#016x) == %#016x; want %#016x", x64, got64, want64) + } + if UintSize == 64 { + x := uint(x64) + got := ReverseBytes(x) + want := uint(want64) + if got != want { + t.Fatalf("ReverseBytes(%#016x) == %#016x; want %#016x", x, got, want) + } + } +} + +func BenchmarkReverseBytes(b *testing.B) { + var s uint + for i := 0; i < b.N; i++ { + s += ReverseBytes(uint(i)) + } + Output = int(s) +} + +func BenchmarkReverseBytes16(b *testing.B) { + var s uint16 + for i := 0; i < b.N; i++ { + s += ReverseBytes16(uint16(i)) + } + Output = int(s) +} + +func BenchmarkReverseBytes32(b *testing.B) { + var s uint32 + for i := 0; i < b.N; i++ { + s += ReverseBytes32(uint32(i)) + } + Output = int(s) +} + +func BenchmarkReverseBytes64(b *testing.B) { + var s uint64 + for i := 0; i < b.N; i++ { + s += ReverseBytes64(uint64(i)) + } + Output = int(s) +} + +func TestLen(t *testing.T) { + for i := 0; i < 256; i++ { + len := 8 - tab[i].nlz + for k := 0; k < 64-8; k++ { + x := uint64(i) << uint(k) + want := 0 + if x != 0 { + want = len + k + } + if x <= 1<<8-1 { + got := Len8(uint8(x)) + if got != want { + t.Fatalf("Len8(%#02x) == %d; want %d", x, got, want) + } + } + + if x <= 1<<16-1 { + got := Len16(uint16(x)) + if got != want { + t.Fatalf("Len16(%#04x) == %d; want %d", x, got, want) + } + } + + if x <= 1<<32-1 { + got := Len32(uint32(x)) + if got != want { + t.Fatalf("Len32(%#08x) == %d; want %d", x, got, want) + } + if UintSize == 32 { + got := Len(uint(x)) + if got != want { + t.Fatalf("Len(%#08x) == %d; want %d", x, got, want) + } + } + } + + if x <= 1<<64-1 { + got := Len64(uint64(x)) + if got != want { + t.Fatalf("Len64(%#016x) == %d; want %d", x, got, want) + } + if UintSize == 64 { + got := Len(uint(x)) + if got != want { + t.Fatalf("Len(%#016x) == %d; want %d", x, got, want) + } + } + } + } + } +} + +const ( + _M = 1< 0 { + panic("overflow") + } + return x + }, + func(a, b uint64) uint64 { + x, c := Add64(a, b, 0) + if c != 0 { + panic("overflow") + } + return x + }, + func(a, b uint64) uint64 { + x, c := Add64(a, b, 0) + if c == 1 { + panic("overflow") + } + return x + }, + func(a, b uint64) uint64 { + x, c := Add64(a, b, 0) + if c != 1 { + return x + } + panic("overflow") + }, + func(a, b uint64) uint64 { + x, c := Add64(a, b, 0) + if c == 0 { + return x + } + panic("overflow") + }, + } + for _, test := range tests { + shouldPanic := func(f func()) { + defer func() { + if err := recover(); err == nil { + t.Fatalf("expected panic") + } + }() + f() + } + + // overflow + shouldPanic(func() { test(_M64, 1) }) + shouldPanic(func() { test(1, _M64) }) + shouldPanic(func() { test(_M64, _M64) }) + + // no overflow + test(_M64, 0) + test(0, 0) + test(1, 1) + } +} + +func TestSub64OverflowPanic(t *testing.T) { + // Test that 64-bit overflow panics fire correctly. + // These are designed to improve coverage of compiler intrinsics. + tests := []func(uint64, uint64) uint64{ + func(a, b uint64) uint64 { + x, c := Sub64(a, b, 0) + if c > 0 { + panic("overflow") + } + return x + }, + func(a, b uint64) uint64 { + x, c := Sub64(a, b, 0) + if c != 0 { + panic("overflow") + } + return x + }, + func(a, b uint64) uint64 { + x, c := Sub64(a, b, 0) + if c == 1 { + panic("overflow") + } + return x + }, + func(a, b uint64) uint64 { + x, c := Sub64(a, b, 0) + if c != 1 { + return x + } + panic("overflow") + }, + func(a, b uint64) uint64 { + x, c := Sub64(a, b, 0) + if c == 0 { + return x + } + panic("overflow") + }, + } + for _, test := range tests { + shouldPanic := func(f func()) { + defer func() { + if err := recover(); err == nil { + t.Fatalf("expected panic") + } + }() + f() + } + + // overflow + shouldPanic(func() { test(0, 1) }) + shouldPanic(func() { test(1, _M64) }) + shouldPanic(func() { test(_M64-1, _M64) }) + + // no overflow + test(_M64, 0) + test(0, 0) + test(1, 1) + } +} + +func TestMulDiv(t *testing.T) { + testMul := func(msg string, f func(x, y uint) (hi, lo uint), x, y, hi, lo uint) { + hi1, lo1 := f(x, y) + if hi1 != hi || lo1 != lo { + t.Errorf("%s: got hi:lo = %#x:%#x; want %#x:%#x", msg, hi1, lo1, hi, lo) + } + } + testDiv := func(msg string, f func(hi, lo, y uint) (q, r uint), hi, lo, y, q, r uint) { + q1, r1 := f(hi, lo, y) + if q1 != q || r1 != r { + t.Errorf("%s: got q:r = %#x:%#x; want %#x:%#x", msg, q1, r1, q, r) + } + } + for _, a := range []struct { + x, y uint + hi, lo, r uint + }{ + {1 << (UintSize - 1), 2, 1, 0, 1}, + {_M, _M, _M - 1, 1, 42}, + } { + testMul("Mul", Mul, a.x, a.y, a.hi, a.lo) + testMul("Mul symmetric", Mul, a.y, a.x, a.hi, a.lo) + testDiv("Div", Div, a.hi, a.lo+a.r, a.y, a.x, a.r) + testDiv("Div symmetric", Div, a.hi, a.lo+a.r, a.x, a.y, a.r) + // The above code can't test intrinsic implementation, because the passed function is not called directly. + // The following code uses a closure to test the intrinsic version in case the function is intrinsified. + testMul("Mul intrinsic", func(x, y uint) (uint, uint) { return Mul(x, y) }, a.x, a.y, a.hi, a.lo) + testMul("Mul intrinsic symmetric", func(x, y uint) (uint, uint) { return Mul(x, y) }, a.y, a.x, a.hi, a.lo) + testDiv("Div intrinsic", func(hi, lo, y uint) (uint, uint) { return Div(hi, lo, y) }, a.hi, a.lo+a.r, a.y, a.x, a.r) + testDiv("Div intrinsic symmetric", func(hi, lo, y uint) (uint, uint) { return Div(hi, lo, y) }, a.hi, a.lo+a.r, a.x, a.y, a.r) + } +} + +func TestMulDiv32(t *testing.T) { + testMul := func(msg string, f func(x, y uint32) (hi, lo uint32), x, y, hi, lo uint32) { + hi1, lo1 := f(x, y) + if hi1 != hi || lo1 != lo { + t.Errorf("%s: got hi:lo = %#x:%#x; want %#x:%#x", msg, hi1, lo1, hi, lo) + } + } + testDiv := func(msg string, f func(hi, lo, y uint32) (q, r uint32), hi, lo, y, q, r uint32) { + q1, r1 := f(hi, lo, y) + if q1 != q || r1 != r { + t.Errorf("%s: got q:r = %#x:%#x; want %#x:%#x", msg, q1, r1, q, r) + } + } + for _, a := range []struct { + x, y uint32 + hi, lo, r uint32 + }{ + {1 << 31, 2, 1, 0, 1}, + {0xc47dfa8c, 50911, 0x98a4, 0x998587f4, 13}, + {_M32, _M32, _M32 - 1, 1, 42}, + } { + testMul("Mul32", Mul32, a.x, a.y, a.hi, a.lo) + testMul("Mul32 symmetric", Mul32, a.y, a.x, a.hi, a.lo) + testDiv("Div32", Div32, a.hi, a.lo+a.r, a.y, a.x, a.r) + testDiv("Div32 symmetric", Div32, a.hi, a.lo+a.r, a.x, a.y, a.r) + } +} + +func TestMulDiv64(t *testing.T) { + testMul := func(msg string, f func(x, y uint64) (hi, lo uint64), x, y, hi, lo uint64) { + hi1, lo1 := f(x, y) + if hi1 != hi || lo1 != lo { + t.Errorf("%s: got hi:lo = %#x:%#x; want %#x:%#x", msg, hi1, lo1, hi, lo) + } + } + testDiv := func(msg string, f func(hi, lo, y uint64) (q, r uint64), hi, lo, y, q, r uint64) { + q1, r1 := f(hi, lo, y) + if q1 != q || r1 != r { + t.Errorf("%s: got q:r = %#x:%#x; want %#x:%#x", msg, q1, r1, q, r) + } + } + for _, a := range []struct { + x, y uint64 + hi, lo, r uint64 + }{ + {1 << 63, 2, 1, 0, 1}, + {0x3626229738a3b9, 0xd8988a9f1cc4a61, 0x2dd0712657fe8, 0x9dd6a3364c358319, 13}, + {_M64, _M64, _M64 - 1, 1, 42}, + } { + testMul("Mul64", Mul64, a.x, a.y, a.hi, a.lo) + testMul("Mul64 symmetric", Mul64, a.y, a.x, a.hi, a.lo) + testDiv("Div64", Div64, a.hi, a.lo+a.r, a.y, a.x, a.r) + testDiv("Div64 symmetric", Div64, a.hi, a.lo+a.r, a.x, a.y, a.r) + // The above code can't test intrinsic implementation, because the passed function is not called directly. + // The following code uses a closure to test the intrinsic version in case the function is intrinsified. + testMul("Mul64 intrinsic", func(x, y uint64) (uint64, uint64) { return Mul64(x, y) }, a.x, a.y, a.hi, a.lo) + testMul("Mul64 intrinsic symmetric", func(x, y uint64) (uint64, uint64) { return Mul64(x, y) }, a.y, a.x, a.hi, a.lo) + testDiv("Div64 intrinsic", func(hi, lo, y uint64) (uint64, uint64) { return Div64(hi, lo, y) }, a.hi, a.lo+a.r, a.y, a.x, a.r) + testDiv("Div64 intrinsic symmetric", func(hi, lo, y uint64) (uint64, uint64) { return Div64(hi, lo, y) }, a.hi, a.lo+a.r, a.x, a.y, a.r) + } +} + +func TestDivPanicOverflow(t *testing.T) { + // Expect a panic + defer func() { + if err := recover(); err == nil { + t.Error("Div should have panicked when y<=hi") + } else if err != overflowError { + t.Errorf("Div expected panic: %q, got: %v ", overflowError, err) + } + }() + q, r := Div(1, 0, 1) + t.Errorf("undefined q, r = %v, %v calculated when Div should have panicked", q, r) +} + +func TestDiv32PanicOverflow(t *testing.T) { + // Expect a panic + defer func() { + if err := recover(); err == nil { + t.Error("Div32 should have panicked when y<=hi") + } else if err != overflowError { + t.Errorf("Div32 expected panic: %q, got: %v ", overflowError, err) + } + }() + q, r := Div32(1, 0, 1) + t.Errorf("undefined q, r = %v, %v calculated when Div32 should have panicked", q, r) +} + +func TestDiv64PanicOverflow(t *testing.T) { + // Expect a panic + defer func() { + if err := recover(); err == nil { + t.Error("Div64 should have panicked when y<=hi") + } else if err != overflowError { + t.Errorf("Div64 expected panic: %q, got: %v ", overflowError, err) + } + }() + q, r := Div64(1, 0, 1) + t.Errorf("undefined q, r = %v, %v calculated when Div64 should have panicked", q, r) +} + +func TestDivPanicZero(t *testing.T) { + // Expect a panic + defer func() { + if err := recover(); err == nil { + t.Error("Div should have panicked when y==0") + } else if err != divideError { + t.Errorf("Div expected panic: %q, got: %q ", divideError, err) + } + }() + q, r := Div(1, 1, 0) + t.Errorf("undefined q, r = %v, %v calculated when Div should have panicked", q, r) +} + +func TestDiv32PanicZero(t *testing.T) { + // Expect a panic + defer func() { + if err := recover(); err == nil { + t.Error("Div32 should have panicked when y==0") + } else if err != divideError { + t.Errorf("Div32 expected panic: %q, got: %q ", divideError, err) + } + }() + q, r := Div32(1, 1, 0) + t.Errorf("undefined q, r = %v, %v calculated when Div32 should have panicked", q, r) +} + +func TestDiv64PanicZero(t *testing.T) { + // Expect a panic + defer func() { + if err := recover(); err == nil { + t.Error("Div64 should have panicked when y==0") + } else if err != divideError { + t.Errorf("Div64 expected panic: %q, got: %q ", divideError, err) + } + }() + q, r := Div64(1, 1, 0) + t.Errorf("undefined q, r = %v, %v calculated when Div64 should have panicked", q, r) +} + +func TestRem32(t *testing.T) { + // Sanity check: for non-overflowing dividends, the result is the + // same as the rem returned by Div32 + hi, lo, y := uint32(510510), uint32(9699690), uint32(510510+1) // ensure hi < y + for i := 0; i < 1000; i++ { + r := Rem32(hi, lo, y) + _, r2 := Div32(hi, lo, y) + if r != r2 { + t.Errorf("Rem32(%v, %v, %v) returned %v, but Div32 returned rem %v", hi, lo, y, r, r2) + } + y += 13 + } +} + +func TestRem32Overflow(t *testing.T) { + // To trigger a quotient overflow, we need y <= hi + hi, lo, y := uint32(510510), uint32(9699690), uint32(7) + for i := 0; i < 1000; i++ { + r := Rem32(hi, lo, y) + _, r2 := Div64(0, uint64(hi)<<32|uint64(lo), uint64(y)) + if r != uint32(r2) { + t.Errorf("Rem32(%v, %v, %v) returned %v, but Div64 returned rem %v", hi, lo, y, r, r2) + } + y += 13 + } +} + +func TestRem64(t *testing.T) { + // Sanity check: for non-overflowing dividends, the result is the + // same as the rem returned by Div64 + hi, lo, y := uint64(510510), uint64(9699690), uint64(510510+1) // ensure hi < y + for i := 0; i < 1000; i++ { + r := Rem64(hi, lo, y) + _, r2 := Div64(hi, lo, y) + if r != r2 { + t.Errorf("Rem64(%v, %v, %v) returned %v, but Div64 returned rem %v", hi, lo, y, r, r2) + } + y += 13 + } +} + +func TestRem64Overflow(t *testing.T) { + Rem64Tests := []struct { + hi, lo, y uint64 + rem uint64 + }{ + // Testcases computed using Python 3, as: + // >>> hi = 42; lo = 1119; y = 42 + // >>> ((hi<<64)+lo) % y + {42, 1119, 42, 27}, + {42, 1119, 38, 9}, + {42, 1119, 26, 23}, + {469, 0, 467, 271}, + {469, 0, 113, 58}, + {111111, 111111, 1171, 803}, + {3968194946088682615, 3192705705065114702, 1000037, 56067}, + } + + for _, rt := range Rem64Tests { + if rt.hi < rt.y { + t.Fatalf("Rem64(%v, %v, %v) is not a test with quo overflow", rt.hi, rt.lo, rt.y) + } + rem := Rem64(rt.hi, rt.lo, rt.y) + if rem != rt.rem { + t.Errorf("Rem64(%v, %v, %v) returned %v, wanted %v", + rt.hi, rt.lo, rt.y, rem, rt.rem) + } + } +} + +func BenchmarkAdd(b *testing.B) { + var z, c uint + for i := 0; i < b.N; i++ { + z, c = Add(uint(Input), uint(i), c) + } + Output = int(z + c) +} + +func BenchmarkAdd32(b *testing.B) { + var z, c uint32 + for i := 0; i < b.N; i++ { + z, c = Add32(uint32(Input), uint32(i), c) + } + Output = int(z + c) +} + +func BenchmarkAdd64(b *testing.B) { + var z, c uint64 + for i := 0; i < b.N; i++ { + z, c = Add64(uint64(Input), uint64(i), c) + } + Output = int(z + c) +} + +func BenchmarkAdd64multiple(b *testing.B) { + z0 := uint64(Input) + z1 := uint64(Input) + z2 := uint64(Input) + z3 := uint64(Input) + for i := 0; i < b.N; i++ { + var c uint64 + z0, c = Add64(z0, uint64(i), c) + z1, c = Add64(z1, uint64(i), c) + z2, c = Add64(z2, uint64(i), c) + z3, _ = Add64(z3, uint64(i), c) + } + Output = int(z0 + z1 + z2 + z3) +} + +func BenchmarkSub(b *testing.B) { + var z, c uint + for i := 0; i < b.N; i++ { + z, c = Sub(uint(Input), uint(i), c) + } + Output = int(z + c) +} + +func BenchmarkSub32(b *testing.B) { + var z, c uint32 + for i := 0; i < b.N; i++ { + z, c = Sub32(uint32(Input), uint32(i), c) + } + Output = int(z + c) +} + +func BenchmarkSub64(b *testing.B) { + var z, c uint64 + for i := 0; i < b.N; i++ { + z, c = Sub64(uint64(Input), uint64(i), c) + } + Output = int(z + c) +} + +func BenchmarkSub64multiple(b *testing.B) { + z0 := uint64(Input) + z1 := uint64(Input) + z2 := uint64(Input) + z3 := uint64(Input) + for i := 0; i < b.N; i++ { + var c uint64 + z0, c = Sub64(z0, uint64(i), c) + z1, c = Sub64(z1, uint64(i), c) + z2, c = Sub64(z2, uint64(i), c) + z3, _ = Sub64(z3, uint64(i), c) + } + Output = int(z0 + z1 + z2 + z3) +} + +func BenchmarkMul(b *testing.B) { + var hi, lo uint + for i := 0; i < b.N; i++ { + hi, lo = Mul(uint(Input), uint(i)) + } + Output = int(hi + lo) +} + +func BenchmarkMul32(b *testing.B) { + var hi, lo uint32 + for i := 0; i < b.N; i++ { + hi, lo = Mul32(uint32(Input), uint32(i)) + } + Output = int(hi + lo) +} + +func BenchmarkMul64(b *testing.B) { + var hi, lo uint64 + for i := 0; i < b.N; i++ { + hi, lo = Mul64(uint64(Input), uint64(i)) + } + Output = int(hi + lo) +} + +func BenchmarkDiv(b *testing.B) { + var q, r uint + for i := 0; i < b.N; i++ { + q, r = Div(1, uint(i), uint(Input)) + } + Output = int(q + r) +} + +func BenchmarkDiv32(b *testing.B) { + var q, r uint32 + for i := 0; i < b.N; i++ { + q, r = Div32(1, uint32(i), uint32(Input)) + } + Output = int(q + r) +} + +func BenchmarkDiv64(b *testing.B) { + var q, r uint64 + for i := 0; i < b.N; i++ { + q, r = Div64(1, uint64(i), uint64(Input)) + } + Output = int(q + r) +} + +// ---------------------------------------------------------------------------- +// Testing support + +type entry = struct { + nlz, ntz, pop int +} + +// tab contains results for all uint8 values +var tab [256]entry + +func init() { + tab[0] = entry{8, 8, 0} + for i := 1; i < len(tab); i++ { + // nlz + x := i // x != 0 + n := 0 + for x&0x80 == 0 { + n++ + x <<= 1 + } + tab[i].nlz = n + + // ntz + x = i // x != 0 + n = 0 + for x&1 == 0 { + n++ + x >>= 1 + } + tab[i].ntz = n + + // pop + x = i // x != 0 + n = 0 + for x != 0 { + n += int(x & 1) + x >>= 1 + } + tab[i].pop = n + } +} diff --git a/gnovm/stdlibs/math/bits/export_test.gno b/gnovm/stdlibs/math/bits/export_test.gno new file mode 100644 index 00000000000..8c6f9332cca --- /dev/null +++ b/gnovm/stdlibs/math/bits/export_test.gno @@ -0,0 +1,7 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bits + +const DeBruijn64 = deBruijn64 diff --git a/gnovm/stdlibs/math/cbrt.gno b/gnovm/stdlibs/math/cbrt.gno new file mode 100644 index 00000000000..b8e6c3e8012 --- /dev/null +++ b/gnovm/stdlibs/math/cbrt.gno @@ -0,0 +1,82 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The go code is a modified version of the original C code from +// http://www.netlib.org/fdlibm/s_cbrt.c and came with this notice. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunSoft, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== + +// Cbrt returns the cube root of x. +// +// Special cases are: +// +// Cbrt(±0) = ±0 +// Cbrt(±Inf) = ±Inf +// Cbrt(NaN) = NaN +func Cbrt(x float64) float64 { + return cbrt(x) +} + +func cbrt(x float64) float64 { + const ( + B1 = 715094163 // (682-0.03306235651)*2**20 + B2 = 696219795 // (664-0.03306235651)*2**20 + C = 5.42857142857142815906e-01 // 19/35 = 0x3FE15F15F15F15F1 + D = -7.05306122448979611050e-01 // -864/1225 = 0xBFE691DE2532C834 + E = 1.41428571428571436819e+00 // 99/70 = 0x3FF6A0EA0EA0EA0F + F = 1.60714285714285720630e+00 // 45/28 = 0x3FF9B6DB6DB6DB6E + G = 3.57142857142857150787e-01 // 5/14 = 0x3FD6DB6DB6DB6DB7 + SmallestNormal = 2.22507385850720138309e-308 // 2**-1022 = 0x0010000000000000 + ) + // special cases + switch { + case x == 0 || IsNaN(x) || IsInf(x, 0): + return x + } + + sign := false + if x < 0 { + x = -x + sign = true + } + + // rough cbrt to 5 bits + t := Float64frombits(Float64bits(x)/3 + B1<<32) + if x < SmallestNormal { + // subnormal number + t = float64(1 << 54) // set t= 2**54 + t *= x + t = Float64frombits(Float64bits(t)/3 + B2<<32) + } + + // new cbrt to 23 bits + r := t * t / x + s := C + r*t + t *= G + F/(s+E+D/s) + + // chop to 22 bits, make larger than cbrt(x) + t = Float64frombits(Float64bits(t)&(0xFFFFFFFFC<<28) + 1<<30) + + // one step newton iteration to 53 bits with error less than 0.667ulps + s = t * t // t*t is exact + r = x / s + w := t + t + r = (r - t) / (w + r) // r-s is exact + t = t + t*r + + // restore the sign bit + if sign { + t = -t + } + return t +} diff --git a/gnovm/stdlibs/math/const.gno b/gnovm/stdlibs/math/const.gno index 5ea935fb425..ad13d53477e 100644 --- a/gnovm/stdlibs/math/const.gno +++ b/gnovm/stdlibs/math/const.gno @@ -39,19 +39,19 @@ const ( const ( intSize = 32 << (^uint(0) >> 63) // 32 or 64 - MaxInt = 1<<(intSize-1) - 1 - MinInt = -1 << (intSize - 1) - MaxInt8 = 1<<7 - 1 - MinInt8 = -1 << 7 - MaxInt16 = 1<<15 - 1 - MinInt16 = -1 << 15 - MaxInt32 = 1<<31 - 1 - MinInt32 = -1 << 31 - MaxInt64 = 1<<63 - 1 - MinInt64 = -1 << 63 - MaxUint = 1< y { + return x + } + return y +} + +// Min returns the smaller of x or y. +// +// Special cases are: +// +// Min(x, -Inf) = Min(-Inf, x) = -Inf +// Min(x, NaN) = Min(NaN, x) = NaN +// Min(-0, ±0) = Min(±0, -0) = -0 +func Min(x, y float64) float64 { + return min(x, y) +} + +func min(x, y float64) float64 { + // special cases + switch { + case IsInf(x, -1) || IsInf(y, -1): + return Inf(-1) + case IsNaN(x) || IsNaN(y): + return NaN() + case x == 0 && x == y: + if Signbit(x) { + return x + } + return y + } + if x < y { + return x + } + return y +} diff --git a/gnovm/stdlibs/math/erf.gno b/gnovm/stdlibs/math/erf.gno new file mode 100644 index 00000000000..717a812caa1 --- /dev/null +++ b/gnovm/stdlibs/math/erf.gno @@ -0,0 +1,345 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Floating-point error function and complementary error function. +*/ + +// The original C code and the long comment below are +// from FreeBSD's /usr/src/lib/msun/src/s_erf.c and +// came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// +// double erf(double x) +// double erfc(double x) +// x +// 2 |\ +// erf(x) = --------- | exp(-t*t)dt +// sqrt(pi) \| +// 0 +// +// erfc(x) = 1-erf(x) +// Note that +// erf(-x) = -erf(x) +// erfc(-x) = 2 - erfc(x) +// +// Method: +// 1. For |x| in [0, 0.84375] +// erf(x) = x + x*R(x**2) +// erfc(x) = 1 - erf(x) if x in [-.84375,0.25] +// = 0.5 + ((0.5-x)-x*R) if x in [0.25,0.84375] +// where R = P/Q where P is an odd poly of degree 8 and +// Q is an odd poly of degree 10. +// -57.90 +// | R - (erf(x)-x)/x | <= 2 +// +// +// Remark. The formula is derived by noting +// erf(x) = (2/sqrt(pi))*(x - x**3/3 + x**5/10 - x**7/42 + ....) +// and that +// 2/sqrt(pi) = 1.128379167095512573896158903121545171688 +// is close to one. The interval is chosen because the fix +// point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is +// near 0.6174), and by some experiment, 0.84375 is chosen to +// guarantee the error is less than one ulp for erf. +// +// 2. For |x| in [0.84375,1.25], let s = |x| - 1, and +// c = 0.84506291151 rounded to single (24 bits) +// erf(x) = sign(x) * (c + P1(s)/Q1(s)) +// erfc(x) = (1-c) - P1(s)/Q1(s) if x > 0 +// 1+(c+P1(s)/Q1(s)) if x < 0 +// |P1/Q1 - (erf(|x|)-c)| <= 2**-59.06 +// Remark: here we use the taylor series expansion at x=1. +// erf(1+s) = erf(1) + s*Poly(s) +// = 0.845.. + P1(s)/Q1(s) +// That is, we use rational approximation to approximate +// erf(1+s) - (c = (single)0.84506291151) +// Note that |P1/Q1|< 0.078 for x in [0.84375,1.25] +// where +// P1(s) = degree 6 poly in s +// Q1(s) = degree 6 poly in s +// +// 3. For x in [1.25,1/0.35(~2.857143)], +// erfc(x) = (1/x)*exp(-x*x-0.5625+R1/S1) +// erf(x) = 1 - erfc(x) +// where +// R1(z) = degree 7 poly in z, (z=1/x**2) +// S1(z) = degree 8 poly in z +// +// 4. For x in [1/0.35,28] +// erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0 +// = 2.0 - (1/x)*exp(-x*x-0.5625+R2/S2) if -6 x >= 28 +// erf(x) = sign(x) *(1 - tiny) (raise inexact) +// erfc(x) = tiny*tiny (raise underflow) if x > 0 +// = 2 - tiny if x<0 +// +// 7. Special case: +// erf(0) = 0, erf(inf) = 1, erf(-inf) = -1, +// erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2, +// erfc/erf(NaN) is NaN + +const ( + erx = 8.45062911510467529297e-01 // 0x3FEB0AC160000000 + // Coefficients for approximation to erf in [0, 0.84375] + efx = 1.28379167095512586316e-01 // 0x3FC06EBA8214DB69 + efx8 = 1.02703333676410069053e+00 // 0x3FF06EBA8214DB69 + pp0 = 1.28379167095512558561e-01 // 0x3FC06EBA8214DB68 + pp1 = -3.25042107247001499370e-01 // 0xBFD4CD7D691CB913 + pp2 = -2.84817495755985104766e-02 // 0xBF9D2A51DBD7194F + pp3 = -5.77027029648944159157e-03 // 0xBF77A291236668E4 + pp4 = -2.37630166566501626084e-05 // 0xBEF8EAD6120016AC + qq1 = 3.97917223959155352819e-01 // 0x3FD97779CDDADC09 + qq2 = 6.50222499887672944485e-02 // 0x3FB0A54C5536CEBA + qq3 = 5.08130628187576562776e-03 // 0x3F74D022C4D36B0F + qq4 = 1.32494738004321644526e-04 // 0x3F215DC9221C1A10 + qq5 = -3.96022827877536812320e-06 // 0xBED09C4342A26120 + // Coefficients for approximation to erf in [0.84375, 1.25] + pa0 = -2.36211856075265944077e-03 // 0xBF6359B8BEF77538 + pa1 = 4.14856118683748331666e-01 // 0x3FDA8D00AD92B34D + pa2 = -3.72207876035701323847e-01 // 0xBFD7D240FBB8C3F1 + pa3 = 3.18346619901161753674e-01 // 0x3FD45FCA805120E4 + pa4 = -1.10894694282396677476e-01 // 0xBFBC63983D3E28EC + pa5 = 3.54783043256182359371e-02 // 0x3FA22A36599795EB + pa6 = -2.16637559486879084300e-03 // 0xBF61BF380A96073F + qa1 = 1.06420880400844228286e-01 // 0x3FBB3E6618EEE323 + qa2 = 5.40397917702171048937e-01 // 0x3FE14AF092EB6F33 + qa3 = 7.18286544141962662868e-02 // 0x3FB2635CD99FE9A7 + qa4 = 1.26171219808761642112e-01 // 0x3FC02660E763351F + qa5 = 1.36370839120290507362e-02 // 0x3F8BEDC26B51DD1C + qa6 = 1.19844998467991074170e-02 // 0x3F888B545735151D + // Coefficients for approximation to erfc in [1.25, 1/0.35] + ra0 = -9.86494403484714822705e-03 // 0xBF843412600D6435 + ra1 = -6.93858572707181764372e-01 // 0xBFE63416E4BA7360 + ra2 = -1.05586262253232909814e+01 // 0xC0251E0441B0E726 + ra3 = -6.23753324503260060396e+01 // 0xC04F300AE4CBA38D + ra4 = -1.62396669462573470355e+02 // 0xC0644CB184282266 + ra5 = -1.84605092906711035994e+02 // 0xC067135CEBCCABB2 + ra6 = -8.12874355063065934246e+01 // 0xC054526557E4D2F2 + ra7 = -9.81432934416914548592e+00 // 0xC023A0EFC69AC25C + sa1 = 1.96512716674392571292e+01 // 0x4033A6B9BD707687 + sa2 = 1.37657754143519042600e+02 // 0x4061350C526AE721 + sa3 = 4.34565877475229228821e+02 // 0x407B290DD58A1A71 + sa4 = 6.45387271733267880336e+02 // 0x40842B1921EC2868 + sa5 = 4.29008140027567833386e+02 // 0x407AD02157700314 + sa6 = 1.08635005541779435134e+02 // 0x405B28A3EE48AE2C + sa7 = 6.57024977031928170135e+00 // 0x401A47EF8E484A93 + sa8 = -6.04244152148580987438e-02 // 0xBFAEEFF2EE749A62 + // Coefficients for approximation to erfc in [1/.35, 28] + rb0 = -9.86494292470009928597e-03 // 0xBF84341239E86F4A + rb1 = -7.99283237680523006574e-01 // 0xBFE993BA70C285DE + rb2 = -1.77579549177547519889e+01 // 0xC031C209555F995A + rb3 = -1.60636384855821916062e+02 // 0xC064145D43C5ED98 + rb4 = -6.37566443368389627722e+02 // 0xC083EC881375F228 + rb5 = -1.02509513161107724954e+03 // 0xC09004616A2E5992 + rb6 = -4.83519191608651397019e+02 // 0xC07E384E9BDC383F + sb1 = 3.03380607434824582924e+01 // 0x403E568B261D5190 + sb2 = 3.25792512996573918826e+02 // 0x40745CAE221B9F0A + sb3 = 1.53672958608443695994e+03 // 0x409802EB189D5118 + sb4 = 3.19985821950859553908e+03 // 0x40A8FFB7688C246A + sb5 = 2.55305040643316442583e+03 // 0x40A3F219CEDF3BE6 + sb6 = 4.74528541206955367215e+02 // 0x407DA874E79FE763 + sb7 = -2.24409524465858183362e+01 // 0xC03670E242712D62 +) + +// Erf returns the error function of x. +// +// Special cases are: +// +// Erf(+Inf) = 1 +// Erf(-Inf) = -1 +// Erf(NaN) = NaN +func Erf(x float64) float64 { + return erf(x) +} + +func erf(x float64) float64 { + const ( + VeryTiny = 2.848094538889218e-306 // 0x0080000000000000 + Small = 1.0 / (1 << 28) // 2**-28 + ) + // special cases + switch { + case IsNaN(x): + return NaN() + case IsInf(x, 1): + return 1 + case IsInf(x, -1): + return -1 + } + sign := false + if x < 0 { + x = -x + sign = true + } + if x < 0.84375 { // |x| < 0.84375 + var temp float64 + if x < Small { // |x| < 2**-28 + if x < VeryTiny { + temp = 0.125 * (8.0*x + efx8*x) // avoid underflow + } else { + temp = x + efx*x + } + } else { + z := x * x + r := pp0 + z*(pp1+z*(pp2+z*(pp3+z*pp4))) + s := 1 + z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5)))) + y := r / s + temp = x + x*y + } + if sign { + return -temp + } + return temp + } + if x < 1.25 { // 0.84375 <= |x| < 1.25 + s := x - 1 + P := pa0 + s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))) + Q := 1 + s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))) + if sign { + return -erx - P/Q + } + return erx + P/Q + } + if x >= 6 { // inf > |x| >= 6 + if sign { + return -1 + } + return 1 + } + s := 1 / (x * x) + var R, S float64 + if x < 1/0.35 { // |x| < 1 / 0.35 ~ 2.857143 + R = ra0 + s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*(ra5+s*(ra6+s*ra7)))))) + S = 1 + s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(sa5+s*(sa6+s*(sa7+s*sa8))))))) + } else { // |x| >= 1 / 0.35 ~ 2.857143 + R = rb0 + s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*(rb5+s*rb6))))) + S = 1 + s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*(sb5+s*(sb6+s*sb7)))))) + } + z := Float64frombits(Float64bits(x) & 0xffffffff00000000) // pseudo-single (20-bit) precision x + r := Exp(-z*z-0.5625) * Exp((z-x)*(z+x)+R/S) + if sign { + return r/x - 1 + } + return 1 - r/x +} + +// Erfc returns the complementary error function of x. +// +// Special cases are: +// +// Erfc(+Inf) = 0 +// Erfc(-Inf) = 2 +// Erfc(NaN) = NaN +func Erfc(x float64) float64 { + return erfc(x) +} + +func erfc(x float64) float64 { + const Tiny = 1.0 / (1 << 56) // 2**-56 + // special cases + switch { + case IsNaN(x): + return NaN() + case IsInf(x, 1): + return 0 + case IsInf(x, -1): + return 2 + } + sign := false + if x < 0 { + x = -x + sign = true + } + if x < 0.84375 { // |x| < 0.84375 + var temp float64 + if x < Tiny { // |x| < 2**-56 + temp = x + } else { + z := x * x + r := pp0 + z*(pp1+z*(pp2+z*(pp3+z*pp4))) + s := 1 + z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5)))) + y := r / s + if x < 0.25 { // |x| < 1/4 + temp = x + x*y + } else { + temp = 0.5 + (x*y + (x - 0.5)) + } + } + if sign { + return 1 + temp + } + return 1 - temp + } + if x < 1.25 { // 0.84375 <= |x| < 1.25 + s := x - 1 + P := pa0 + s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))) + Q := 1 + s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))) + if sign { + return 1 + erx + P/Q + } + return 1 - erx - P/Q + + } + if x < 28 { // |x| < 28 + s := 1 / (x * x) + var R, S float64 + if x < 1/0.35 { // |x| < 1 / 0.35 ~ 2.857143 + R = ra0 + s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*(ra5+s*(ra6+s*ra7)))))) + S = 1 + s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(sa5+s*(sa6+s*(sa7+s*sa8))))))) + } else { // |x| >= 1 / 0.35 ~ 2.857143 + if sign && x > 6 { + return 2 // x < -6 + } + R = rb0 + s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*(rb5+s*rb6))))) + S = 1 + s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*(sb5+s*(sb6+s*sb7)))))) + } + z := Float64frombits(Float64bits(x) & 0xffffffff00000000) // pseudo-single (20-bit) precision x + r := Exp(-z*z-0.5625) * Exp((z-x)*(z+x)+R/S) + if sign { + return 2 - r/x + } + return r / x + } + if sign { + return 2 + } + return 0 +} diff --git a/gnovm/stdlibs/math/erfinv.gno b/gnovm/stdlibs/math/erfinv.gno new file mode 100644 index 00000000000..eed0feb42dd --- /dev/null +++ b/gnovm/stdlibs/math/erfinv.gno @@ -0,0 +1,129 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Inverse of the floating-point error function. +*/ + +// This implementation is based on the rational approximation +// of percentage points of normal distribution available from +// https://www.jstor.org/stable/2347330. + +const ( + // Coefficients for approximation to erf in |x| <= 0.85 + a0 = 1.1975323115670912564578e0 + a1 = 4.7072688112383978012285e1 + a2 = 6.9706266534389598238465e2 + a3 = 4.8548868893843886794648e3 + a4 = 1.6235862515167575384252e4 + a5 = 2.3782041382114385731252e4 + a6 = 1.1819493347062294404278e4 + a7 = 8.8709406962545514830200e2 + b0 = 1.0000000000000000000e0 + b1 = 4.2313330701600911252e1 + b2 = 6.8718700749205790830e2 + b3 = 5.3941960214247511077e3 + b4 = 2.1213794301586595867e4 + b5 = 3.9307895800092710610e4 + b6 = 2.8729085735721942674e4 + b7 = 5.2264952788528545610e3 + // Coefficients for approximation to erf in 0.85 < |x| <= 1-2*exp(-25) + c0 = 1.42343711074968357734e0 + c1 = 4.63033784615654529590e0 + c2 = 5.76949722146069140550e0 + c3 = 3.64784832476320460504e0 + c4 = 1.27045825245236838258e0 + c5 = 2.41780725177450611770e-1 + c6 = 2.27238449892691845833e-2 + c7 = 7.74545014278341407640e-4 + d0 = 1.4142135623730950488016887e0 + d1 = 2.9036514445419946173133295e0 + d2 = 2.3707661626024532365971225e0 + d3 = 9.7547832001787427186894837e-1 + d4 = 2.0945065210512749128288442e-1 + d5 = 2.1494160384252876777097297e-2 + d6 = 7.7441459065157709165577218e-4 + d7 = 1.4859850019840355905497876e-9 + // Coefficients for approximation to erf in 1-2*exp(-25) < |x| < 1 + e0 = 6.65790464350110377720e0 + e1 = 5.46378491116411436990e0 + e2 = 1.78482653991729133580e0 + e3 = 2.96560571828504891230e-1 + e4 = 2.65321895265761230930e-2 + e5 = 1.24266094738807843860e-3 + e6 = 2.71155556874348757815e-5 + e7 = 2.01033439929228813265e-7 + f0 = 1.414213562373095048801689e0 + f1 = 8.482908416595164588112026e-1 + f2 = 1.936480946950659106176712e-1 + f3 = 2.103693768272068968719679e-2 + f4 = 1.112800997078859844711555e-3 + f5 = 2.611088405080593625138020e-5 + f6 = 2.010321207683943062279931e-7 + f7 = 2.891024605872965461538222e-15 +) + +// Erfinv returns the inverse error function of x. +// +// Special cases are: +// +// Erfinv(1) = +Inf +// Erfinv(-1) = -Inf +// Erfinv(x) = NaN if x < -1 or x > 1 +// Erfinv(NaN) = NaN +func Erfinv(x float64) float64 { + // special cases + if IsNaN(x) || x <= -1 || x >= 1 { + if x == -1 || x == 1 { + return Inf(int(x)) + } + return NaN() + } + + sign := false + if x < 0 { + x = -x + sign = true + } + + var ans float64 + if x <= 0.85 { // |x| <= 0.85 + r := 0.180625 - 0.25*x*x + z1 := ((((((a7*r+a6)*r+a5)*r+a4)*r+a3)*r+a2)*r+a1)*r + a0 + z2 := ((((((b7*r+b6)*r+b5)*r+b4)*r+b3)*r+b2)*r+b1)*r + b0 + ans = (x * z1) / z2 + } else { + var z1, z2 float64 + r := Sqrt(Ln2 - Log(1.0-x)) + if r <= 5.0 { + r -= 1.6 + z1 = ((((((c7*r+c6)*r+c5)*r+c4)*r+c3)*r+c2)*r+c1)*r + c0 + z2 = ((((((d7*r+d6)*r+d5)*r+d4)*r+d3)*r+d2)*r+d1)*r + d0 + } else { + r -= 5.0 + z1 = ((((((e7*r+e6)*r+e5)*r+e4)*r+e3)*r+e2)*r+e1)*r + e0 + z2 = ((((((f7*r+f6)*r+f5)*r+f4)*r+f3)*r+f2)*r+f1)*r + f0 + } + ans = z1 / z2 + } + + if sign { + return -ans + } + return ans +} + +// Erfcinv returns the inverse of Erfc(x). +// +// Special cases are: +// +// Erfcinv(0) = +Inf +// Erfcinv(2) = -Inf +// Erfcinv(x) = NaN if x < 0 or x > 2 +// Erfcinv(NaN) = NaN +func Erfcinv(x float64) float64 { + return Erfinv(1 - x) +} diff --git a/gnovm/stdlibs/math/exp.gno b/gnovm/stdlibs/math/exp.gno index a4112208b62..4ddec051cc7 100644 --- a/gnovm/stdlibs/math/exp.gno +++ b/gnovm/stdlibs/math/exp.gno @@ -14,11 +14,6 @@ package math // Very large values overflow to 0 or +Inf. // Very small values underflow to 1. func Exp(x float64) float64 { - /* XXX - if haveArchExp { - return archExp(x) - } - */ return exp(x) } @@ -142,11 +137,6 @@ func exp(x float64) float64 { // // Special cases are the same as Exp. func Exp2(x float64) float64 { - /* XXX - if haveArchExp2 { - return archExp2(x) - } - */ return exp2(x) } diff --git a/gnovm/stdlibs/math/expm1.gno b/gnovm/stdlibs/math/expm1.gno new file mode 100644 index 00000000000..b1291f7b1ad --- /dev/null +++ b/gnovm/stdlibs/math/expm1.gno @@ -0,0 +1,241 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code, the long comment, and the constants +// below are from FreeBSD's /usr/src/lib/msun/src/s_expm1.c +// and came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// expm1(x) +// Returns exp(x)-1, the exponential of x minus 1. +// +// Method +// 1. Argument reduction: +// Given x, find r and integer k such that +// +// x = k*ln2 + r, |r| <= 0.5*ln2 ~ 0.34658 +// +// Here a correction term c will be computed to compensate +// the error in r when rounded to a floating-point number. +// +// 2. Approximating expm1(r) by a special rational function on +// the interval [0,0.34658]: +// Since +// r*(exp(r)+1)/(exp(r)-1) = 2+ r**2/6 - r**4/360 + ... +// we define R1(r*r) by +// r*(exp(r)+1)/(exp(r)-1) = 2+ r**2/6 * R1(r*r) +// That is, +// R1(r**2) = 6/r *((exp(r)+1)/(exp(r)-1) - 2/r) +// = 6/r * ( 1 + 2.0*(1/(exp(r)-1) - 1/r)) +// = 1 - r**2/60 + r**4/2520 - r**6/100800 + ... +// We use a special Reme algorithm on [0,0.347] to generate +// a polynomial of degree 5 in r*r to approximate R1. The +// maximum error of this polynomial approximation is bounded +// by 2**-61. In other words, +// R1(z) ~ 1.0 + Q1*z + Q2*z**2 + Q3*z**3 + Q4*z**4 + Q5*z**5 +// where Q1 = -1.6666666666666567384E-2, +// Q2 = 3.9682539681370365873E-4, +// Q3 = -9.9206344733435987357E-6, +// Q4 = 2.5051361420808517002E-7, +// Q5 = -6.2843505682382617102E-9; +// (where z=r*r, and the values of Q1 to Q5 are listed below) +// with error bounded by +// | 5 | -61 +// | 1.0+Q1*z+...+Q5*z - R1(z) | <= 2 +// | | +// +// expm1(r) = exp(r)-1 is then computed by the following +// specific way which minimize the accumulation rounding error: +// 2 3 +// r r [ 3 - (R1 + R1*r/2) ] +// expm1(r) = r + --- + --- * [--------------------] +// 2 2 [ 6 - r*(3 - R1*r/2) ] +// +// To compensate the error in the argument reduction, we use +// expm1(r+c) = expm1(r) + c + expm1(r)*c +// ~ expm1(r) + c + r*c +// Thus c+r*c will be added in as the correction terms for +// expm1(r+c). Now rearrange the term to avoid optimization +// screw up: +// ( 2 2 ) +// ({ ( r [ R1 - (3 - R1*r/2) ] ) } r ) +// expm1(r+c)~r - ({r*(--- * [--------------------]-c)-c} - --- ) +// ({ ( 2 [ 6 - r*(3 - R1*r/2) ] ) } 2 ) +// ( ) +// +// = r - E +// 3. Scale back to obtain expm1(x): +// From step 1, we have +// expm1(x) = either 2**k*[expm1(r)+1] - 1 +// = or 2**k*[expm1(r) + (1-2**-k)] +// 4. Implementation notes: +// (A). To save one multiplication, we scale the coefficient Qi +// to Qi*2**i, and replace z by (x**2)/2. +// (B). To achieve maximum accuracy, we compute expm1(x) by +// (i) if x < -56*ln2, return -1.0, (raise inexact if x!=inf) +// (ii) if k=0, return r-E +// (iii) if k=-1, return 0.5*(r-E)-0.5 +// (iv) if k=1 if r < -0.25, return 2*((r+0.5)- E) +// else return 1.0+2.0*(r-E); +// (v) if (k<-2||k>56) return 2**k(1-(E-r)) - 1 (or exp(x)-1) +// (vi) if k <= 20, return 2**k((1-2**-k)-(E-r)), else +// (vii) return 2**k(1-((E+2**-k)-r)) +// +// Special cases: +// expm1(INF) is INF, expm1(NaN) is NaN; +// expm1(-INF) is -1, and +// for finite argument, only expm1(0)=0 is exact. +// +// Accuracy: +// according to an error analysis, the error is always less than +// 1 ulp (unit in the last place). +// +// Misc. info. +// For IEEE double +// if x > 7.09782712893383973096e+02 then expm1(x) overflow +// +// Constants: +// The hexadecimal values are the intended ones for the following +// constants. The decimal values may be used, provided that the +// compiler will convert from decimal to binary accurately enough +// to produce the hexadecimal values shown. +// + +// Expm1 returns e**x - 1, the base-e exponential of x minus 1. +// It is more accurate than Exp(x) - 1 when x is near zero. +// +// Special cases are: +// +// Expm1(+Inf) = +Inf +// Expm1(-Inf) = -1 +// Expm1(NaN) = NaN +// +// Very large values overflow to -1 or +Inf. +func Expm1(x float64) float64 { + return expm1(x) +} + +func expm1(x float64) float64 { + const ( + Othreshold = 7.09782712893383973096e+02 // 0x40862E42FEFA39EF + Ln2X56 = 3.88162421113569373274e+01 // 0x4043687a9f1af2b1 + Ln2HalfX3 = 1.03972077083991796413e+00 // 0x3ff0a2b23f3bab73 + Ln2Half = 3.46573590279972654709e-01 // 0x3fd62e42fefa39ef + Ln2Hi = 6.93147180369123816490e-01 // 0x3fe62e42fee00000 + Ln2Lo = 1.90821492927058770002e-10 // 0x3dea39ef35793c76 + InvLn2 = 1.44269504088896338700e+00 // 0x3ff71547652b82fe + Tiny = 1.0 / (1 << 54) // 2**-54 = 0x3c90000000000000 + // scaled coefficients related to expm1 + Q1 = -3.33333333333331316428e-02 // 0xBFA11111111110F4 + Q2 = 1.58730158725481460165e-03 // 0x3F5A01A019FE5585 + Q3 = -7.93650757867487942473e-05 // 0xBF14CE199EAADBB7 + Q4 = 4.00821782732936239552e-06 // 0x3ED0CFCA86E65239 + Q5 = -2.01099218183624371326e-07 // 0xBE8AFDB76E09C32D + ) + + // special cases + switch { + case IsInf(x, 1) || IsNaN(x): + return x + case IsInf(x, -1): + return -1 + } + + absx := x + sign := false + if x < 0 { + absx = -absx + sign = true + } + + // filter out huge argument + if absx >= Ln2X56 { // if |x| >= 56 * ln2 + if sign { + return -1 // x < -56*ln2, return -1 + } + if absx >= Othreshold { // if |x| >= 709.78... + return Inf(1) + } + } + + // argument reduction + var c float64 + var k int + if absx > Ln2Half { // if |x| > 0.5 * ln2 + var hi, lo float64 + if absx < Ln2HalfX3 { // and |x| < 1.5 * ln2 + if !sign { + hi = x - Ln2Hi + lo = Ln2Lo + k = 1 + } else { + hi = x + Ln2Hi + lo = -Ln2Lo + k = -1 + } + } else { + if !sign { + k = int(InvLn2*x + 0.5) + } else { + k = int(InvLn2*x - 0.5) + } + t := float64(k) + hi = x - t*Ln2Hi // t * Ln2Hi is exact here + lo = t * Ln2Lo + } + x = hi - lo + c = (hi - x) - lo + } else if absx < Tiny { // when |x| < 2**-54, return x + return x + } else { + k = 0 + } + + // x is now in primary range + hfx := 0.5 * x + hxs := x * hfx + r1 := 1 + hxs*(Q1+hxs*(Q2+hxs*(Q3+hxs*(Q4+hxs*Q5)))) + t := 3 - r1*hfx + e := hxs * ((r1 - t) / (6.0 - x*t)) + if k == 0 { + return x - (x*e - hxs) // c is 0 + } + e = (x*(e-c) - c) + e -= hxs + switch { + case k == -1: + return 0.5*(x-e) - 0.5 + case k == 1: + if x < -0.25 { + return -2 * (e - (x + 0.5)) + } + return 1 + 2*(x-e) + case k <= -2 || k > 56: // suffice to return exp(x)-1 + y := 1 - (e - x) + y = Float64frombits(Float64bits(y) + uint64(k)<<52) // add k to y's exponent + return y - 1 + } + if k < 20 { + t := Float64frombits(0x3ff0000000000000 - (0x20000000000000 >> uint(k))) // t=1-2**-k + y := t - (e - x) + y = Float64frombits(Float64bits(y) + uint64(k)<<52) // add k to y's exponent + return y + } + t = Float64frombits(uint64(0x3ff-k) << 52) // 2**-k + y := x - (e + t) + y++ + y = Float64frombits(Float64bits(y) + uint64(k)<<52) // add k to y's exponent + return y +} diff --git a/gnovm/stdlibs/math/export_test.gno b/gnovm/stdlibs/math/export_test.gno new file mode 100644 index 00000000000..8b2e36d0788 --- /dev/null +++ b/gnovm/stdlibs/math/export_test.gno @@ -0,0 +1,16 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// Export internal functions for testing. +var ( + ExpGo = exp + Exp2Go = exp2 + HypotGo = hypot + SqrtGo = sqrt + TrigReduce = trigReduce +) + +const ReduceThreshold = reduceThreshold diff --git a/gnovm/stdlibs/math/floor.gno b/gnovm/stdlibs/math/floor.gno index 7d6320a1983..a3b98644856 100644 --- a/gnovm/stdlibs/math/floor.gno +++ b/gnovm/stdlibs/math/floor.gno @@ -4,10 +4,6 @@ package math -import ( - imath "internal/math" -) - // Floor returns the greatest integer value less than or equal to x. // // Special cases are: @@ -16,11 +12,6 @@ import ( // Floor(±Inf) = ±Inf // Floor(NaN) = NaN func Floor(x float64) float64 { - /* XXX - if haveArchFloor { - return archFloor(x) - } - */ return floor(x) } @@ -47,11 +38,6 @@ func floor(x float64) float64 { // Ceil(±Inf) = ±Inf // Ceil(NaN) = NaN func Ceil(x float64) float64 { - /* XXX - if haveArchCeil { - return archCeil(x) - } - */ return ceil(x) } @@ -67,11 +53,6 @@ func ceil(x float64) float64 { // Trunc(±Inf) = ±Inf // Trunc(NaN) = NaN func Trunc(x float64) float64 { - /* XXX - if haveArchTrunc { - return archTrunc(x) - } - */ return trunc(x) } @@ -100,7 +81,7 @@ func Round(x float64) float64 { // } // return t // } - bits := imath.Float64bits(x) + bits := Float64bits(x) e := uint(bits>>shift) & mask if e < bias { // Round abs(x) < 1 including denormals. @@ -118,7 +99,7 @@ func Round(x float64) float64 { bits += half >> e bits &^= fracMask >> e } - return imath.Float64frombits(bits) + return Float64frombits(bits) } // RoundToEven returns the nearest integer, rounding ties to even. @@ -139,7 +120,7 @@ func RoundToEven(x float64) float64 { // } // return t // } - bits := imath.Float64bits(x) + bits := Float64bits(x) e := uint(bits>>shift) & mask if e >= bias { // Round abs(x) >= 1. @@ -157,5 +138,5 @@ func RoundToEven(x float64) float64 { // Round abs(x) <= 0.5 including denormals. bits &= signMask // +-0 } - return imath.Float64frombits(bits) + return Float64frombits(bits) } diff --git a/gnovm/stdlibs/math/fma.gno b/gnovm/stdlibs/math/fma.gno new file mode 100644 index 00000000000..ba03fbe8a93 --- /dev/null +++ b/gnovm/stdlibs/math/fma.gno @@ -0,0 +1,175 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +import "math/bits" + +func zero(x uint64) uint64 { + if x == 0 { + return 1 + } + return 0 + // branchless: + // return ((x>>1 | x&1) - 1) >> 63 +} + +func nonzero(x uint64) uint64 { + if x != 0 { + return 1 + } + return 0 + // branchless: + // return 1 - ((x>>1|x&1)-1)>>63 +} + +func shl(u1, u2 uint64, n uint) (r1, r2 uint64) { + r1 = u1<>(64-n) | u2<<(n-64) + r2 = u2 << n + return +} + +func shr(u1, u2 uint64, n uint) (r1, r2 uint64) { + r2 = u2>>n | u1<<(64-n) | u1>>(n-64) + r1 = u1 >> n + return +} + +// shrcompress compresses the bottom n+1 bits of the two-word +// value into a single bit. the result is equal to the value +// shifted to the right by n, except the result's 0th bit is +// set to the bitwise OR of the bottom n+1 bits. +func shrcompress(u1, u2 uint64, n uint) (r1, r2 uint64) { + // TODO: Performance here is really sensitive to the + // order/placement of these branches. n == 0 is common + // enough to be in the fast path. Perhaps more measurement + // needs to be done to find the optimal order/placement? + switch { + case n == 0: + return u1, u2 + case n == 64: + return 0, u1 | nonzero(u2) + case n >= 128: + return 0, nonzero(u1 | u2) + case n < 64: + r1, r2 = shr(u1, u2, n) + r2 |= nonzero(u2 & (1<> 63) + exp = int32(b>>52) & mask + mantissa = b & fracMask + + if exp == 0 { + // Normalize value if subnormal. + shift := uint(bits.LeadingZeros64(mantissa) - 11) + mantissa <<= shift + exp = 1 - int32(shift) + } else { + // Add implicit 1 bit + mantissa |= 1 << 52 + } + return +} + +// FMA returns x * y + z, computed with only one rounding. +// (That is, FMA returns the fused multiply-add of x, y, and z.) +func FMA(x, y, z float64) float64 { + bx, by, bz := Float64bits(x), Float64bits(y), Float64bits(z) + + // Inf or NaN or zero involved. At most one rounding will occur. + if x == 0.0 || y == 0.0 || z == 0.0 || bx&uvinf == uvinf || by&uvinf == uvinf { + return x*y + z + } + // Handle non-finite z separately. Evaluating x*y+z where + // x and y are finite, but z is infinite, should always result in z. + if bz&uvinf == uvinf { + return z + } + + // Inputs are (sub)normal. + // Split x, y, z into sign, exponent, mantissa. + xs, xe, xm := split(bx) + ys, ye, ym := split(by) + zs, ze, zm := split(bz) + + // Compute product p = x*y as sign, exponent, two-word mantissa. + // Start with exponent. "is normal" bit isn't subtracted yet. + pe := xe + ye - bias + 1 + + // pm1:pm2 is the double-word mantissa for the product p. + // Shift left to leave top bit in product. Effectively + // shifts the 106-bit product to the left by 21. + pm1, pm2 := bits.Mul64(xm<<10, ym<<11) + zm1, zm2 := zm<<10, uint64(0) + ps := xs ^ ys // product sign + + // normalize to 62nd bit + is62zero := uint((^pm1 >> 62) & 1) + pm1, pm2 = shl(pm1, pm2, is62zero) + pe -= int32(is62zero) + + // Swap addition operands so |p| >= |z| + if pe < ze || pe == ze && pm1 < zm1 { + ps, pe, pm1, pm2, zs, ze, zm1, zm2 = zs, ze, zm1, zm2, ps, pe, pm1, pm2 + } + + // Special case: if p == -z the result is always +0 since neither operand is zero. + if ps != zs && pe == ze && pm1 == zm1 && pm2 == zm2 { + return 0 + } + + // Align significands + zm1, zm2 = shrcompress(zm1, zm2, uint(pe-ze)) + + // Compute resulting significands, normalizing if necessary. + var m, c uint64 + if ps == zs { + // Adding (pm1:pm2) + (zm1:zm2) + pm2, c = bits.Add64(pm2, zm2, 0) + pm1, _ = bits.Add64(pm1, zm1, c) + pe -= int32(^pm1 >> 63) + pm1, m = shrcompress(pm1, pm2, uint(64+pm1>>63)) + } else { + // Subtracting (pm1:pm2) - (zm1:zm2) + // TODO: should we special-case cancellation? + pm2, c = bits.Sub64(pm2, zm2, 0) + pm1, _ = bits.Sub64(pm1, zm1, c) + nz := lz(pm1, pm2) + pe -= nz + m, pm2 = shl(pm1, pm2, uint(nz-1)) + m |= nonzero(pm2) + } + + // Round and break ties to even + if pe > 1022+bias || pe == 1022+bias && (m+1<<9)>>63 == 1 { + // rounded value overflows exponent range + return Float64frombits(uint64(ps)<<63 | uvinf) + } + if pe < 0 { + n := uint(-pe) + m = m>>n | nonzero(m&(1<> 10) & ^zero((m&(1<<10-1))^1<<9) + pe &= -int32(nonzero(m)) + return Float64frombits(uint64(ps)<<63 + uint64(pe)<<52 + m) +} diff --git a/gnovm/stdlibs/math/frexp.gno b/gnovm/stdlibs/math/frexp.gno new file mode 100644 index 00000000000..7a196abd01d --- /dev/null +++ b/gnovm/stdlibs/math/frexp.gno @@ -0,0 +1,36 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// Frexp breaks f into a normalized fraction +// and an integral power of two. +// It returns frac and exp satisfying f == frac × 2**exp, +// with the absolute value of frac in the interval [½, 1). +// +// Special cases are: +// +// Frexp(±0) = ±0, 0 +// Frexp(±Inf) = ±Inf, 0 +// Frexp(NaN) = NaN, 0 +func Frexp(f float64) (frac float64, exp int) { + return frexp(f) +} + +func frexp(f float64) (frac float64, exp int) { + // special cases + switch { + case f == 0: + return f, 0 // correctly return -0 + case IsInf(f, 0) || IsNaN(f): + return f, 0 + } + f, exp = normalize(f) + x := Float64bits(f) + exp += int((x>>shift)&mask) - bias + 1 + x &^= mask << shift + x |= (-1 + bias) << shift + frac = Float64frombits(x) + return +} diff --git a/gnovm/stdlibs/math/gamma.gno b/gnovm/stdlibs/math/gamma.gno new file mode 100644 index 00000000000..99f3bd0241f --- /dev/null +++ b/gnovm/stdlibs/math/gamma.gno @@ -0,0 +1,224 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code, the long comment, and the constants +// below are from http://netlib.sandia.gov/cephes/cprob/gamma.c. +// The go code is a simplified version of the original C. +// +// tgamma.c +// +// Gamma function +// +// SYNOPSIS: +// +// double x, y, tgamma(); +// extern int signgam; +// +// y = tgamma( x ); +// +// DESCRIPTION: +// +// Returns gamma function of the argument. The result is +// correctly signed, and the sign (+1 or -1) is also +// returned in a global (extern) variable named signgam. +// This variable is also filled in by the logarithmic gamma +// function lgamma(). +// +// Arguments |x| <= 34 are reduced by recurrence and the function +// approximated by a rational function of degree 6/7 in the +// interval (2,3). Large arguments are handled by Stirling's +// formula. Large negative arguments are made positive using +// a reflection formula. +// +// ACCURACY: +// +// Relative error: +// arithmetic domain # trials peak rms +// DEC -34, 34 10000 1.3e-16 2.5e-17 +// IEEE -170,-33 20000 2.3e-15 3.3e-16 +// IEEE -33, 33 20000 9.4e-16 2.2e-16 +// IEEE 33, 171.6 20000 2.3e-15 3.2e-16 +// +// Error for arguments outside the test range will be larger +// owing to error amplification by the exponential function. +// +// Cephes Math Library Release 2.8: June, 2000 +// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier +// +// The readme file at http://netlib.sandia.gov/cephes/ says: +// Some software in this archive may be from the book _Methods and +// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +// International, 1989) or from the Cephes Mathematical Library, a +// commercial product. In either event, it is copyrighted by the author. +// What you see here may be used freely but it comes with no support or +// guarantee. +// +// The two known misprints in the book are repaired here in the +// source listings for the gamma function and the incomplete beta +// integral. +// +// Stephen L. Moshier +// moshier@na-net.ornl.gov + +var _gamP = [...]float64{ + 1.60119522476751861407e-04, + 1.19135147006586384913e-03, + 1.04213797561761569935e-02, + 4.76367800457137231464e-02, + 2.07448227648435975150e-01, + 4.94214826801497100753e-01, + 9.99999999999999996796e-01, +} + +var _gamQ = [...]float64{ + -2.31581873324120129819e-05, + 5.39605580493303397842e-04, + -4.45641913851797240494e-03, + 1.18139785222060435552e-02, + 3.58236398605498653373e-02, + -2.34591795718243348568e-01, + 7.14304917030273074085e-02, + 1.00000000000000000320e+00, +} + +var _gamS = [...]float64{ + 7.87311395793093628397e-04, + -2.29549961613378126380e-04, + -2.68132617805781232825e-03, + 3.47222221605458667310e-03, + 8.33333333333482257126e-02, +} + +// Gamma function computed by Stirling's formula. +// The pair of results must be multiplied together to get the actual answer. +// The multiplication is left to the caller so that, if careful, the caller can avoid +// infinity for 172 <= x <= 180. +// The polynomial is valid for 33 <= x <= 172; larger values are only used +// in reciprocal and produce denormalized floats. The lower precision there +// masks any imprecision in the polynomial. +func stirling(x float64) (float64, float64) { + if x > 200 { + return Inf(1), 1 + } + const ( + SqrtTwoPi = 2.506628274631000502417 + MaxStirling = 143.01608 + ) + w := 1 / x + w = 1 + w*((((_gamS[0]*w+_gamS[1])*w+_gamS[2])*w+_gamS[3])*w+_gamS[4]) + y1 := Exp(x) + y2 := 1.0 + if x > MaxStirling { // avoid Pow() overflow + v := Pow(x, 0.5*x-0.25) + y1, y2 = v, v/y1 + } else { + y1 = Pow(x, x-0.5) / y1 + } + return y1, SqrtTwoPi * w * y2 +} + +// Gamma returns the Gamma function of x. +// +// Special cases are: +// +// Gamma(+Inf) = +Inf +// Gamma(+0) = +Inf +// Gamma(-0) = -Inf +// Gamma(x) = NaN for integer x < 0 +// Gamma(-Inf) = NaN +// Gamma(NaN) = NaN +func Gamma(x float64) float64 { + const Euler = 0.57721566490153286060651209008240243104215933593992 // A001620 + // special cases + switch { + case isNegInt(x) || IsInf(x, -1) || IsNaN(x): + return NaN() + case IsInf(x, 1): + return Inf(1) + case x == 0: + if Signbit(x) { + return Inf(-1) + } + return Inf(1) + } + q := Abs(x) + p := Floor(q) + if q > 33 { + if x >= 0 { + y1, y2 := stirling(x) + return y1 * y2 + } + // Note: x is negative but (checked above) not a negative integer, + // so x must be small enough to be in range for conversion to int64. + // If |x| were >= 2⁶³ it would have to be an integer. + signgam := 1 + if ip := int64(p); ip&1 == 0 { + signgam = -1 + } + z := q - p + if z > 0.5 { + p = p + 1 + z = q - p + } + z = q * Sin(Pi*z) + if z == 0 { + return Inf(signgam) + } + sq1, sq2 := stirling(q) + absz := Abs(z) + d := absz * sq1 * sq2 + if IsInf(d, 0) { + z = Pi / absz / sq1 / sq2 + } else { + z = Pi / d + } + return float64(signgam) * z + } + + // Reduce argument + z := 1.0 + for x >= 3 { + x = x - 1 + z = z * x + } + for x < 0 { + if x > -1e-09 { + goto small + } + z = z / x + x = x + 1 + } + for x < 2 { + if x < 1e-09 { + goto small + } + z = z / x + x = x + 1 + } + + if x == 2 { + return z + } + + x = x - 2 + p = (((((x*_gamP[0]+_gamP[1])*x+_gamP[2])*x+_gamP[3])*x+_gamP[4])*x+_gamP[5])*x + _gamP[6] + q = ((((((x*_gamQ[0]+_gamQ[1])*x+_gamQ[2])*x+_gamQ[3])*x+_gamQ[4])*x+_gamQ[5])*x+_gamQ[6])*x + _gamQ[7] + return z * p / q + +small: + if x == 0 { + return Inf(1) + } + return z / ((1 + Euler*x) * x) +} + +func isNegInt(x float64) bool { + if x < 0 { + _, xf := Modf(x) + return xf == 0 + } + return false +} diff --git a/gnovm/stdlibs/math/hypot.gno b/gnovm/stdlibs/math/hypot.gno new file mode 100644 index 00000000000..a57835a2601 --- /dev/null +++ b/gnovm/stdlibs/math/hypot.gno @@ -0,0 +1,41 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Hypot -- sqrt(p*p + q*q), but overflows only if the result does. +*/ + +// Hypot returns Sqrt(p*p + q*q), taking care to avoid +// unnecessary overflow and underflow. +// +// Special cases are: +// +// Hypot(±Inf, q) = +Inf +// Hypot(p, ±Inf) = +Inf +// Hypot(NaN, q) = NaN +// Hypot(p, NaN) = NaN +func Hypot(p, q float64) float64 { + return hypot(p, q) +} + +func hypot(p, q float64) float64 { + p, q = Abs(p), Abs(q) + // special cases + switch { + case IsInf(p, 1) || IsInf(q, 1): + return Inf(1) + case IsNaN(p) || IsNaN(q): + return NaN() + } + if p < q { + p, q = q, p + } + if p == 0 { + return 0 + } + q = q / p + return p * Sqrt(1+q*q) +} diff --git a/gnovm/stdlibs/math/j0.gno b/gnovm/stdlibs/math/j0.gno new file mode 100644 index 00000000000..a99e629f042 --- /dev/null +++ b/gnovm/stdlibs/math/j0.gno @@ -0,0 +1,437 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Bessel function of the first and second kinds of order zero. +*/ + +// The original C code and the long comment below are +// from FreeBSD's /usr/src/lib/msun/src/e_j0.c and +// came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// __ieee754_j0(x), __ieee754_y0(x) +// Bessel function of the first and second kinds of order zero. +// Method -- j0(x): +// 1. For tiny x, we use j0(x) = 1 - x**2/4 + x**4/64 - ... +// 2. Reduce x to |x| since j0(x)=j0(-x), and +// for x in (0,2) +// j0(x) = 1-z/4+ z**2*R0/S0, where z = x*x; +// (precision: |j0-1+z/4-z**2R0/S0 |<2**-63.67 ) +// for x in (2,inf) +// j0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x0)-q0(x)*sin(x0)) +// where x0 = x-pi/4. It is better to compute sin(x0),cos(x0) +// as follow: +// cos(x0) = cos(x)cos(pi/4)+sin(x)sin(pi/4) +// = 1/sqrt(2) * (cos(x) + sin(x)) +// sin(x0) = sin(x)cos(pi/4)-cos(x)sin(pi/4) +// = 1/sqrt(2) * (sin(x) - cos(x)) +// (To avoid cancellation, use +// sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) +// to compute the worse one.) +// +// 3 Special cases +// j0(nan)= nan +// j0(0) = 1 +// j0(inf) = 0 +// +// Method -- y0(x): +// 1. For x<2. +// Since +// y0(x) = 2/pi*(j0(x)*(ln(x/2)+Euler) + x**2/4 - ...) +// therefore y0(x)-2/pi*j0(x)*ln(x) is an even function. +// We use the following function to approximate y0, +// y0(x) = U(z)/V(z) + (2/pi)*(j0(x)*ln(x)), z= x**2 +// where +// U(z) = u00 + u01*z + ... + u06*z**6 +// V(z) = 1 + v01*z + ... + v04*z**4 +// with absolute approximation error bounded by 2**-72. +// Note: For tiny x, U/V = u0 and j0(x)~1, hence +// y0(tiny) = u0 + (2/pi)*ln(tiny), (choose tiny<2**-27) +// 2. For x>=2. +// y0(x) = sqrt(2/(pi*x))*(p0(x)*cos(x0)+q0(x)*sin(x0)) +// where x0 = x-pi/4. It is better to compute sin(x0),cos(x0) +// by the method mentioned above. +// 3. Special cases: y0(0)=-inf, y0(x<0)=NaN, y0(inf)=0. +// + +// J0 returns the order-zero Bessel function of the first kind. +// +// Special cases are: +// +// J0(±Inf) = 0 +// J0(0) = 1 +// J0(NaN) = NaN +func J0(x float64) float64 { + const ( + Huge = 1e300 + TwoM27 = 1.0 / (1 << 27) // 2**-27 0x3e40000000000000 + TwoM13 = 1.0 / (1 << 13) // 2**-13 0x3f20000000000000 + Two129 = 1 << 129 // 2**129 0x4800000000000000 + // R0/S0 on [0, 2] + R02 = 1.56249999999999947958e-02 // 0x3F8FFFFFFFFFFFFD + R03 = -1.89979294238854721751e-04 // 0xBF28E6A5B61AC6E9 + R04 = 1.82954049532700665670e-06 // 0x3EBEB1D10C503919 + R05 = -4.61832688532103189199e-09 // 0xBE33D5E773D63FCE + S01 = 1.56191029464890010492e-02 // 0x3F8FFCE882C8C2A4 + S02 = 1.16926784663337450260e-04 // 0x3F1EA6D2DD57DBF4 + S03 = 5.13546550207318111446e-07 // 0x3EA13B54CE84D5A9 + S04 = 1.16614003333790000205e-09 // 0x3E1408BCF4745D8F + ) + // special cases + switch { + case IsNaN(x): + return x + case IsInf(x, 0): + return 0 + case x == 0: + return 1 + } + + x = Abs(x) + if x >= 2 { + s, c := Sincos(x) + ss := s - c + cc := s + c + + // make sure x+x does not overflow + if x < MaxFloat64/2 { + z := -Cos(x + x) + if s*c < 0 { + cc = z / ss + } else { + ss = z / cc + } + } + + // j0(x) = 1/sqrt(pi) * (P(0,x)*cc - Q(0,x)*ss) / sqrt(x) + // y0(x) = 1/sqrt(pi) * (P(0,x)*ss + Q(0,x)*cc) / sqrt(x) + + var z float64 + if x > Two129 { // |x| > ~6.8056e+38 + z = (1 / SqrtPi) * cc / Sqrt(x) + } else { + u := pzero(x) + v := qzero(x) + z = (1 / SqrtPi) * (u*cc - v*ss) / Sqrt(x) + } + return z // |x| >= 2.0 + } + if x < TwoM13 { // |x| < ~1.2207e-4 + if x < TwoM27 { + return 1 // |x| < ~7.4506e-9 + } + return 1 - 0.25*x*x // ~7.4506e-9 < |x| < ~1.2207e-4 + } + z := x * x + r := z * (R02 + z*(R03+z*(R04+z*R05))) + s := 1 + z*(S01+z*(S02+z*(S03+z*S04))) + if x < 1 { + return 1 + z*(-0.25+(r/s)) // |x| < 1.00 + } + u := 0.5 * x + return (1+u)*(1-u) + z*(r/s) // 1.0 < |x| < 2.0 +} + +// Y0 returns the order-zero Bessel function of the second kind. +// +// Special cases are: +// +// Y0(+Inf) = 0 +// Y0(0) = -Inf +// Y0(x < 0) = NaN +// Y0(NaN) = NaN +func Y0(x float64) float64 { + const ( + TwoM27 = 1.0 / (1 << 27) // 2**-27 0x3e40000000000000 + Two129 = 1 << 129 // 2**129 0x4800000000000000 + U00 = -7.38042951086872317523e-02 // 0xBFB2E4D699CBD01F + U01 = 1.76666452509181115538e-01 // 0x3FC69D019DE9E3FC + U02 = -1.38185671945596898896e-02 // 0xBF8C4CE8B16CFA97 + U03 = 3.47453432093683650238e-04 // 0x3F36C54D20B29B6B + U04 = -3.81407053724364161125e-06 // 0xBECFFEA773D25CAD + U05 = 1.95590137035022920206e-08 // 0x3E5500573B4EABD4 + U06 = -3.98205194132103398453e-11 // 0xBDC5E43D693FB3C8 + V01 = 1.27304834834123699328e-02 // 0x3F8A127091C9C71A + V02 = 7.60068627350353253702e-05 // 0x3F13ECBBF578C6C1 + V03 = 2.59150851840457805467e-07 // 0x3E91642D7FF202FD + V04 = 4.41110311332675467403e-10 // 0x3DFE50183BD6D9EF + ) + // special cases + switch { + case x < 0 || IsNaN(x): + return NaN() + case IsInf(x, 1): + return 0 + case x == 0: + return Inf(-1) + } + + if x >= 2 { // |x| >= 2.0 + + // y0(x) = sqrt(2/(pi*x))*(p0(x)*sin(x0)+q0(x)*cos(x0)) + // where x0 = x-pi/4 + // Better formula: + // cos(x0) = cos(x)cos(pi/4)+sin(x)sin(pi/4) + // = 1/sqrt(2) * (sin(x) + cos(x)) + // sin(x0) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) + // = 1/sqrt(2) * (sin(x) - cos(x)) + // To avoid cancellation, use + // sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + // to compute the worse one. + + s, c := Sincos(x) + ss := s - c + cc := s + c + + // j0(x) = 1/sqrt(pi) * (P(0,x)*cc - Q(0,x)*ss) / sqrt(x) + // y0(x) = 1/sqrt(pi) * (P(0,x)*ss + Q(0,x)*cc) / sqrt(x) + + // make sure x+x does not overflow + if x < MaxFloat64/2 { + z := -Cos(x + x) + if s*c < 0 { + cc = z / ss + } else { + ss = z / cc + } + } + var z float64 + if x > Two129 { // |x| > ~6.8056e+38 + z = (1 / SqrtPi) * ss / Sqrt(x) + } else { + u := pzero(x) + v := qzero(x) + z = (1 / SqrtPi) * (u*ss + v*cc) / Sqrt(x) + } + return z // |x| >= 2.0 + } + if x <= TwoM27 { + return U00 + (2/Pi)*Log(x) // |x| < ~7.4506e-9 + } + z := x * x + u := U00 + z*(U01+z*(U02+z*(U03+z*(U04+z*(U05+z*U06))))) + v := 1 + z*(V01+z*(V02+z*(V03+z*V04))) + return u/v + (2/Pi)*J0(x)*Log(x) // ~7.4506e-9 < |x| < 2.0 +} + +// The asymptotic expansions of pzero is +// 1 - 9/128 s**2 + 11025/98304 s**4 - ..., where s = 1/x. +// For x >= 2, We approximate pzero by +// pzero(x) = 1 + (R/S) +// where R = pR0 + pR1*s**2 + pR2*s**4 + ... + pR5*s**10 +// S = 1 + pS0*s**2 + ... + pS4*s**10 +// and +// | pzero(x)-1-R/S | <= 2 ** ( -60.26) + +// for x in [inf, 8]=1/[0,0.125] +var p0R8 = [6]float64{ + 0.00000000000000000000e+00, // 0x0000000000000000 + -7.03124999999900357484e-02, // 0xBFB1FFFFFFFFFD32 + -8.08167041275349795626e+00, // 0xC02029D0B44FA779 + -2.57063105679704847262e+02, // 0xC07011027B19E863 + -2.48521641009428822144e+03, // 0xC0A36A6ECD4DCAFC + -5.25304380490729545272e+03, // 0xC0B4850B36CC643D +} + +var p0S8 = [5]float64{ + 1.16534364619668181717e+02, // 0x405D223307A96751 + 3.83374475364121826715e+03, // 0x40ADF37D50596938 + 4.05978572648472545552e+04, // 0x40E3D2BB6EB6B05F + 1.16752972564375915681e+05, // 0x40FC810F8F9FA9BD + 4.76277284146730962675e+04, // 0x40E741774F2C49DC +} + +// for x in [8,4.5454]=1/[0.125,0.22001] +var p0R5 = [6]float64{ + -1.14125464691894502584e-11, // 0xBDA918B147E495CC + -7.03124940873599280078e-02, // 0xBFB1FFFFE69AFBC6 + -4.15961064470587782438e+00, // 0xC010A370F90C6BBF + -6.76747652265167261021e+01, // 0xC050EB2F5A7D1783 + -3.31231299649172967747e+02, // 0xC074B3B36742CC63 + -3.46433388365604912451e+02, // 0xC075A6EF28A38BD7 +} + +var p0S5 = [5]float64{ + 6.07539382692300335975e+01, // 0x404E60810C98C5DE + 1.05125230595704579173e+03, // 0x40906D025C7E2864 + 5.97897094333855784498e+03, // 0x40B75AF88FBE1D60 + 9.62544514357774460223e+03, // 0x40C2CCB8FA76FA38 + 2.40605815922939109441e+03, // 0x40A2CC1DC70BE864 +} + +// for x in [4.547,2.8571]=1/[0.2199,0.35001] +var p0R3 = [6]float64{ + -2.54704601771951915620e-09, // 0xBE25E1036FE1AA86 + -7.03119616381481654654e-02, // 0xBFB1FFF6F7C0E24B + -2.40903221549529611423e+00, // 0xC00345B2AEA48074 + -2.19659774734883086467e+01, // 0xC035F74A4CB94E14 + -5.80791704701737572236e+01, // 0xC04D0A22420A1A45 + -3.14479470594888503854e+01, // 0xC03F72ACA892D80F +} + +var p0S3 = [5]float64{ + 3.58560338055209726349e+01, // 0x4041ED9284077DD3 + 3.61513983050303863820e+02, // 0x40769839464A7C0E + 1.19360783792111533330e+03, // 0x4092A66E6D1061D6 + 1.12799679856907414432e+03, // 0x40919FFCB8C39B7E + 1.73580930813335754692e+02, // 0x4065B296FC379081 +} + +// for x in [2.8570,2]=1/[0.3499,0.5] +var p0R2 = [6]float64{ + -8.87534333032526411254e-08, // 0xBE77D316E927026D + -7.03030995483624743247e-02, // 0xBFB1FF62495E1E42 + -1.45073846780952986357e+00, // 0xBFF736398A24A843 + -7.63569613823527770791e+00, // 0xC01E8AF3EDAFA7F3 + -1.11931668860356747786e+01, // 0xC02662E6C5246303 + -3.23364579351335335033e+00, // 0xC009DE81AF8FE70F +} + +var p0S2 = [5]float64{ + 2.22202997532088808441e+01, // 0x40363865908B5959 + 1.36206794218215208048e+02, // 0x4061069E0EE8878F + 2.70470278658083486789e+02, // 0x4070E78642EA079B + 1.53875394208320329881e+02, // 0x40633C033AB6FAFF + 1.46576176948256193810e+01, // 0x402D50B344391809 +} + +func pzero(x float64) float64 { + var p *[6]float64 + var q *[5]float64 + if x >= 8 { + p = &p0R8 + q = &p0S8 + } else if x >= 4.5454 { + p = &p0R5 + q = &p0S5 + } else if x >= 2.8571 { + p = &p0R3 + q = &p0S3 + } else if x >= 2 { + p = &p0R2 + q = &p0S2 + } + z := 1 / (x * x) + r := p[0] + z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))) + s := 1 + z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4])))) + return 1 + r/s +} + +// For x >= 8, the asymptotic expansions of qzero is +// -1/8 s + 75/1024 s**3 - ..., where s = 1/x. +// We approximate pzero by +// qzero(x) = s*(-1.25 + (R/S)) +// where R = qR0 + qR1*s**2 + qR2*s**4 + ... + qR5*s**10 +// S = 1 + qS0*s**2 + ... + qS5*s**12 +// and +// | qzero(x)/s +1.25-R/S | <= 2**(-61.22) + +// for x in [inf, 8]=1/[0,0.125] +var q0R8 = [6]float64{ + 0.00000000000000000000e+00, // 0x0000000000000000 + 7.32421874999935051953e-02, // 0x3FB2BFFFFFFFFE2C + 1.17682064682252693899e+01, // 0x402789525BB334D6 + 5.57673380256401856059e+02, // 0x40816D6315301825 + 8.85919720756468632317e+03, // 0x40C14D993E18F46D + 3.70146267776887834771e+04, // 0x40E212D40E901566 +} + +var q0S8 = [6]float64{ + 1.63776026895689824414e+02, // 0x406478D5365B39BC + 8.09834494656449805916e+03, // 0x40BFA2584E6B0563 + 1.42538291419120476348e+05, // 0x4101665254D38C3F + 8.03309257119514397345e+05, // 0x412883DA83A52B43 + 8.40501579819060512818e+05, // 0x4129A66B28DE0B3D + -3.43899293537866615225e+05, // 0xC114FD6D2C9530C5 +} + +// for x in [8,4.5454]=1/[0.125,0.22001] +var q0R5 = [6]float64{ + 1.84085963594515531381e-11, // 0x3DB43D8F29CC8CD9 + 7.32421766612684765896e-02, // 0x3FB2BFFFD172B04C + 5.83563508962056953777e+00, // 0x401757B0B9953DD3 + 1.35111577286449829671e+02, // 0x4060E3920A8788E9 + 1.02724376596164097464e+03, // 0x40900CF99DC8C481 + 1.98997785864605384631e+03, // 0x409F17E953C6E3A6 +} + +var q0S5 = [6]float64{ + 8.27766102236537761883e+01, // 0x4054B1B3FB5E1543 + 2.07781416421392987104e+03, // 0x40A03BA0DA21C0CE + 1.88472887785718085070e+04, // 0x40D267D27B591E6D + 5.67511122894947329769e+04, // 0x40EBB5E397E02372 + 3.59767538425114471465e+04, // 0x40E191181F7A54A0 + -5.35434275601944773371e+03, // 0xC0B4EA57BEDBC609 +} + +// for x in [4.547,2.8571]=1/[0.2199,0.35001] +var q0R3 = [6]float64{ + 4.37741014089738620906e-09, // 0x3E32CD036ADECB82 + 7.32411180042911447163e-02, // 0x3FB2BFEE0E8D0842 + 3.34423137516170720929e+00, // 0x400AC0FC61149CF5 + 4.26218440745412650017e+01, // 0x40454F98962DAEDD + 1.70808091340565596283e+02, // 0x406559DBE25EFD1F + 1.66733948696651168575e+02, // 0x4064D77C81FA21E0 +} + +var q0S3 = [6]float64{ + 4.87588729724587182091e+01, // 0x40486122BFE343A6 + 7.09689221056606015736e+02, // 0x40862D8386544EB3 + 3.70414822620111362994e+03, // 0x40ACF04BE44DFC63 + 6.46042516752568917582e+03, // 0x40B93C6CD7C76A28 + 2.51633368920368957333e+03, // 0x40A3A8AAD94FB1C0 + -1.49247451836156386662e+02, // 0xC062A7EB201CF40F +} + +// for x in [2.8570,2]=1/[0.3499,0.5] +var q0R2 = [6]float64{ + 1.50444444886983272379e-07, // 0x3E84313B54F76BDB + 7.32234265963079278272e-02, // 0x3FB2BEC53E883E34 + 1.99819174093815998816e+00, // 0x3FFFF897E727779C + 1.44956029347885735348e+01, // 0x402CFDBFAAF96FE5 + 3.16662317504781540833e+01, // 0x403FAA8E29FBDC4A + 1.62527075710929267416e+01, // 0x403040B171814BB4 +} + +var q0S2 = [6]float64{ + 3.03655848355219184498e+01, // 0x403E5D96F7C07AED + 2.69348118608049844624e+02, // 0x4070D591E4D14B40 + 8.44783757595320139444e+02, // 0x408A664522B3BF22 + 8.82935845112488550512e+02, // 0x408B977C9C5CC214 + 2.12666388511798828631e+02, // 0x406A95530E001365 + -5.31095493882666946917e+00, // 0xC0153E6AF8B32931 +} + +func qzero(x float64) float64 { + var p, q *[6]float64 + if x >= 8 { + p = &q0R8 + q = &q0S8 + } else if x >= 4.5454 { + p = &q0R5 + q = &q0S5 + } else if x >= 2.8571 { + p = &q0R3 + q = &q0S3 + } else if x >= 2 { + p = &q0R2 + q = &q0S2 + } + z := 1 / (x * x) + r := p[0] + z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))) + s := 1 + z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5]))))) + return (-0.125 + r/s) / x +} diff --git a/gnovm/stdlibs/math/j1.gno b/gnovm/stdlibs/math/j1.gno new file mode 100644 index 00000000000..1e30a24ab4f --- /dev/null +++ b/gnovm/stdlibs/math/j1.gno @@ -0,0 +1,432 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Bessel function of the first and second kinds of order one. +*/ + +// The original C code and the long comment below are +// from FreeBSD's /usr/src/lib/msun/src/e_j1.c and +// came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// __ieee754_j1(x), __ieee754_y1(x) +// Bessel function of the first and second kinds of order one. +// Method -- j1(x): +// 1. For tiny x, we use j1(x) = x/2 - x**3/16 + x**5/384 - ... +// 2. Reduce x to |x| since j1(x)=-j1(-x), and +// for x in (0,2) +// j1(x) = x/2 + x*z*R0/S0, where z = x*x; +// (precision: |j1/x - 1/2 - R0/S0 |<2**-61.51 ) +// for x in (2,inf) +// j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1)) +// y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x1)+q1(x)*cos(x1)) +// where x1 = x-3*pi/4. It is better to compute sin(x1),cos(x1) +// as follow: +// cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4) +// = 1/sqrt(2) * (sin(x) - cos(x)) +// sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) +// = -1/sqrt(2) * (sin(x) + cos(x)) +// (To avoid cancellation, use +// sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) +// to compute the worse one.) +// +// 3 Special cases +// j1(nan)= nan +// j1(0) = 0 +// j1(inf) = 0 +// +// Method -- y1(x): +// 1. screen out x<=0 cases: y1(0)=-inf, y1(x<0)=NaN +// 2. For x<2. +// Since +// y1(x) = 2/pi*(j1(x)*(ln(x/2)+Euler)-1/x-x/2+5/64*x**3-...) +// therefore y1(x)-2/pi*j1(x)*ln(x)-1/x is an odd function. +// We use the following function to approximate y1, +// y1(x) = x*U(z)/V(z) + (2/pi)*(j1(x)*ln(x)-1/x), z= x**2 +// where for x in [0,2] (abs err less than 2**-65.89) +// U(z) = U0[0] + U0[1]*z + ... + U0[4]*z**4 +// V(z) = 1 + v0[0]*z + ... + v0[4]*z**5 +// Note: For tiny x, 1/x dominate y1 and hence +// y1(tiny) = -2/pi/tiny, (choose tiny<2**-54) +// 3. For x>=2. +// y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x1)+q1(x)*cos(x1)) +// where x1 = x-3*pi/4. It is better to compute sin(x1),cos(x1) +// by method mentioned above. + +// J1 returns the order-one Bessel function of the first kind. +// +// Special cases are: +// +// J1(±Inf) = 0 +// J1(NaN) = NaN +func J1(x float64) float64 { + const ( + TwoM27 = 1.0 / (1 << 27) // 2**-27 0x3e40000000000000 + Two129 = 1 << 129 // 2**129 0x4800000000000000 + // R0/S0 on [0, 2] + R00 = -6.25000000000000000000e-02 // 0xBFB0000000000000 + R01 = 1.40705666955189706048e-03 // 0x3F570D9F98472C61 + R02 = -1.59955631084035597520e-05 // 0xBEF0C5C6BA169668 + R03 = 4.96727999609584448412e-08 // 0x3E6AAAFA46CA0BD9 + S01 = 1.91537599538363460805e-02 // 0x3F939D0B12637E53 + S02 = 1.85946785588630915560e-04 // 0x3F285F56B9CDF664 + S03 = 1.17718464042623683263e-06 // 0x3EB3BFF8333F8498 + S04 = 5.04636257076217042715e-09 // 0x3E35AC88C97DFF2C + S05 = 1.23542274426137913908e-11 // 0x3DAB2ACFCFB97ED8 + ) + // special cases + switch { + case IsNaN(x): + return x + case IsInf(x, 0) || x == 0: + return 0 + } + + sign := false + if x < 0 { + x = -x + sign = true + } + if x >= 2 { + s, c := Sincos(x) + ss := -s - c + cc := s - c + + // make sure x+x does not overflow + if x < MaxFloat64/2 { + z := Cos(x + x) + if s*c > 0 { + cc = z / ss + } else { + ss = z / cc + } + } + + // j1(x) = 1/sqrt(pi) * (P(1,x)*cc - Q(1,x)*ss) / sqrt(x) + // y1(x) = 1/sqrt(pi) * (P(1,x)*ss + Q(1,x)*cc) / sqrt(x) + + var z float64 + if x > Two129 { + z = (1 / SqrtPi) * cc / Sqrt(x) + } else { + u := pone(x) + v := qone(x) + z = (1 / SqrtPi) * (u*cc - v*ss) / Sqrt(x) + } + if sign { + return -z + } + return z + } + if x < TwoM27 { // |x|<2**-27 + return 0.5 * x // inexact if x!=0 necessary + } + z := x * x + r := z * (R00 + z*(R01+z*(R02+z*R03))) + s := 1.0 + z*(S01+z*(S02+z*(S03+z*(S04+z*S05)))) + r *= x + z = 0.5*x + r/s + if sign { + return -z + } + return z +} + +// Y1 returns the order-one Bessel function of the second kind. +// +// Special cases are: +// +// Y1(+Inf) = 0 +// Y1(0) = -Inf +// Y1(x < 0) = NaN +// Y1(NaN) = NaN +func Y1(x float64) float64 { + const ( + TwoM54 = 1.0 / (1 << 54) // 2**-54 0x3c90000000000000 + Two129 = 1 << 129 // 2**129 0x4800000000000000 + U00 = -1.96057090646238940668e-01 // 0xBFC91866143CBC8A + U01 = 5.04438716639811282616e-02 // 0x3FA9D3C776292CD1 + U02 = -1.91256895875763547298e-03 // 0xBF5F55E54844F50F + U03 = 2.35252600561610495928e-05 // 0x3EF8AB038FA6B88E + U04 = -9.19099158039878874504e-08 // 0xBE78AC00569105B8 + V00 = 1.99167318236649903973e-02 // 0x3F94650D3F4DA9F0 + V01 = 2.02552581025135171496e-04 // 0x3F2A8C896C257764 + V02 = 1.35608801097516229404e-06 // 0x3EB6C05A894E8CA6 + V03 = 6.22741452364621501295e-09 // 0x3E3ABF1D5BA69A86 + V04 = 1.66559246207992079114e-11 // 0x3DB25039DACA772A + ) + // special cases + switch { + case x < 0 || IsNaN(x): + return NaN() + case IsInf(x, 1): + return 0 + case x == 0: + return Inf(-1) + } + + if x >= 2 { + s, c := Sincos(x) + ss := -s - c + cc := s - c + + // make sure x+x does not overflow + if x < MaxFloat64/2 { + z := Cos(x + x) + if s*c > 0 { + cc = z / ss + } else { + ss = z / cc + } + } + // y1(x) = sqrt(2/(pi*x))*(p1(x)*sin(x0)+q1(x)*cos(x0)) + // where x0 = x-3pi/4 + // Better formula: + // cos(x0) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4) + // = 1/sqrt(2) * (sin(x) - cos(x)) + // sin(x0) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4) + // = -1/sqrt(2) * (cos(x) + sin(x)) + // To avoid cancellation, use + // sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) + // to compute the worse one. + + var z float64 + if x > Two129 { + z = (1 / SqrtPi) * ss / Sqrt(x) + } else { + u := pone(x) + v := qone(x) + z = (1 / SqrtPi) * (u*ss + v*cc) / Sqrt(x) + } + return z + } + if x <= TwoM54 { // x < 2**-54 + return -(2 / Pi) / x + } + z := x * x + u := U00 + z*(U01+z*(U02+z*(U03+z*U04))) + v := 1 + z*(V00+z*(V01+z*(V02+z*(V03+z*V04)))) + return x*(u/v) + (2/Pi)*(J1(x)*Log(x)-1/x) +} + +// For x >= 8, the asymptotic expansions of pone is +// 1 + 15/128 s**2 - 4725/2**15 s**4 - ..., where s = 1/x. +// We approximate pone by +// pone(x) = 1 + (R/S) +// where R = pr0 + pr1*s**2 + pr2*s**4 + ... + pr5*s**10 +// S = 1 + ps0*s**2 + ... + ps4*s**10 +// and +// | pone(x)-1-R/S | <= 2**(-60.06) + +// for x in [inf, 8]=1/[0,0.125] +var p1R8 = [6]float64{ + 0.00000000000000000000e+00, // 0x0000000000000000 + 1.17187499999988647970e-01, // 0x3FBDFFFFFFFFFCCE + 1.32394806593073575129e+01, // 0x402A7A9D357F7FCE + 4.12051854307378562225e+02, // 0x4079C0D4652EA590 + 3.87474538913960532227e+03, // 0x40AE457DA3A532CC + 7.91447954031891731574e+03, // 0x40BEEA7AC32782DD +} + +var p1S8 = [5]float64{ + 1.14207370375678408436e+02, // 0x405C8D458E656CAC + 3.65093083420853463394e+03, // 0x40AC85DC964D274F + 3.69562060269033463555e+04, // 0x40E20B8697C5BB7F + 9.76027935934950801311e+04, // 0x40F7D42CB28F17BB + 3.08042720627888811578e+04, // 0x40DE1511697A0B2D +} + +// for x in [8,4.5454] = 1/[0.125,0.22001] +var p1R5 = [6]float64{ + 1.31990519556243522749e-11, // 0x3DAD0667DAE1CA7D + 1.17187493190614097638e-01, // 0x3FBDFFFFE2C10043 + 6.80275127868432871736e+00, // 0x401B36046E6315E3 + 1.08308182990189109773e+02, // 0x405B13B9452602ED + 5.17636139533199752805e+02, // 0x40802D16D052D649 + 5.28715201363337541807e+02, // 0x408085B8BB7E0CB7 +} + +var p1S5 = [5]float64{ + 5.92805987221131331921e+01, // 0x404DA3EAA8AF633D + 9.91401418733614377743e+02, // 0x408EFB361B066701 + 5.35326695291487976647e+03, // 0x40B4E9445706B6FB + 7.84469031749551231769e+03, // 0x40BEA4B0B8A5BB15 + 1.50404688810361062679e+03, // 0x40978030036F5E51 +} + +// for x in[4.5453,2.8571] = 1/[0.2199,0.35001] +var p1R3 = [6]float64{ + 3.02503916137373618024e-09, // 0x3E29FC21A7AD9EDD + 1.17186865567253592491e-01, // 0x3FBDFFF55B21D17B + 3.93297750033315640650e+00, // 0x400F76BCE85EAD8A + 3.51194035591636932736e+01, // 0x40418F489DA6D129 + 9.10550110750781271918e+01, // 0x4056C3854D2C1837 + 4.85590685197364919645e+01, // 0x4048478F8EA83EE5 +} + +var p1S3 = [5]float64{ + 3.47913095001251519989e+01, // 0x40416549A134069C + 3.36762458747825746741e+02, // 0x40750C3307F1A75F + 1.04687139975775130551e+03, // 0x40905B7C5037D523 + 8.90811346398256432622e+02, // 0x408BD67DA32E31E9 + 1.03787932439639277504e+02, // 0x4059F26D7C2EED53 +} + +// for x in [2.8570,2] = 1/[0.3499,0.5] +var p1R2 = [6]float64{ + 1.07710830106873743082e-07, // 0x3E7CE9D4F65544F4 + 1.17176219462683348094e-01, // 0x3FBDFF42BE760D83 + 2.36851496667608785174e+00, // 0x4002F2B7F98FAEC0 + 1.22426109148261232917e+01, // 0x40287C377F71A964 + 1.76939711271687727390e+01, // 0x4031B1A8177F8EE2 + 5.07352312588818499250e+00, // 0x40144B49A574C1FE +} + +var p1S2 = [5]float64{ + 2.14364859363821409488e+01, // 0x40356FBD8AD5ECDC + 1.25290227168402751090e+02, // 0x405F529314F92CD5 + 2.32276469057162813669e+02, // 0x406D08D8D5A2DBD9 + 1.17679373287147100768e+02, // 0x405D6B7ADA1884A9 + 8.36463893371618283368e+00, // 0x4020BAB1F44E5192 +} + +func pone(x float64) float64 { + var p *[6]float64 + var q *[5]float64 + if x >= 8 { + p = &p1R8 + q = &p1S8 + } else if x >= 4.5454 { + p = &p1R5 + q = &p1S5 + } else if x >= 2.8571 { + p = &p1R3 + q = &p1S3 + } else if x >= 2 { + p = &p1R2 + q = &p1S2 + } + z := 1 / (x * x) + r := p[0] + z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))) + s := 1.0 + z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*q[4])))) + return 1 + r/s +} + +// For x >= 8, the asymptotic expansions of qone is +// 3/8 s - 105/1024 s**3 - ..., where s = 1/x. +// We approximate qone by +// qone(x) = s*(0.375 + (R/S)) +// where R = qr1*s**2 + qr2*s**4 + ... + qr5*s**10 +// S = 1 + qs1*s**2 + ... + qs6*s**12 +// and +// | qone(x)/s -0.375-R/S | <= 2**(-61.13) + +// for x in [inf, 8] = 1/[0,0.125] +var q1R8 = [6]float64{ + 0.00000000000000000000e+00, // 0x0000000000000000 + -1.02539062499992714161e-01, // 0xBFBA3FFFFFFFFDF3 + -1.62717534544589987888e+01, // 0xC0304591A26779F7 + -7.59601722513950107896e+02, // 0xC087BCD053E4B576 + -1.18498066702429587167e+04, // 0xC0C724E740F87415 + -4.84385124285750353010e+04, // 0xC0E7A6D065D09C6A +} + +var q1S8 = [6]float64{ + 1.61395369700722909556e+02, // 0x40642CA6DE5BCDE5 + 7.82538599923348465381e+03, // 0x40BE9162D0D88419 + 1.33875336287249578163e+05, // 0x4100579AB0B75E98 + 7.19657723683240939863e+05, // 0x4125F65372869C19 + 6.66601232617776375264e+05, // 0x412457D27719AD5C + -2.94490264303834643215e+05, // 0xC111F9690EA5AA18 +} + +// for x in [8,4.5454] = 1/[0.125,0.22001] +var q1R5 = [6]float64{ + -2.08979931141764104297e-11, // 0xBDB6FA431AA1A098 + -1.02539050241375426231e-01, // 0xBFBA3FFFCB597FEF + -8.05644828123936029840e+00, // 0xC0201CE6CA03AD4B + -1.83669607474888380239e+02, // 0xC066F56D6CA7B9B0 + -1.37319376065508163265e+03, // 0xC09574C66931734F + -2.61244440453215656817e+03, // 0xC0A468E388FDA79D +} + +var q1S5 = [6]float64{ + 8.12765501384335777857e+01, // 0x405451B2FF5A11B2 + 1.99179873460485964642e+03, // 0x409F1F31E77BF839 + 1.74684851924908907677e+04, // 0x40D10F1F0D64CE29 + 4.98514270910352279316e+04, // 0x40E8576DAABAD197 + 2.79480751638918118260e+04, // 0x40DB4B04CF7C364B + -4.71918354795128470869e+03, // 0xC0B26F2EFCFFA004 +} + +// for x in [4.5454,2.8571] = 1/[0.2199,0.35001] ??? +var q1R3 = [6]float64{ + -5.07831226461766561369e-09, // 0xBE35CFA9D38FC84F + -1.02537829820837089745e-01, // 0xBFBA3FEB51AEED54 + -4.61011581139473403113e+00, // 0xC01270C23302D9FF + -5.78472216562783643212e+01, // 0xC04CEC71C25D16DA + -2.28244540737631695038e+02, // 0xC06C87D34718D55F + -2.19210128478909325622e+02, // 0xC06B66B95F5C1BF6 +} + +var q1S3 = [6]float64{ + 4.76651550323729509273e+01, // 0x4047D523CCD367E4 + 6.73865112676699709482e+02, // 0x40850EEBC031EE3E + 3.38015286679526343505e+03, // 0x40AA684E448E7C9A + 5.54772909720722782367e+03, // 0x40B5ABBAA61D54A6 + 1.90311919338810798763e+03, // 0x409DBC7A0DD4DF4B + -1.35201191444307340817e+02, // 0xC060E670290A311F +} + +// for x in [2.8570,2] = 1/[0.3499,0.5] +var q1R2 = [6]float64{ + -1.78381727510958865572e-07, // 0xBE87F12644C626D2 + -1.02517042607985553460e-01, // 0xBFBA3E8E9148B010 + -2.75220568278187460720e+00, // 0xC006048469BB4EDA + -1.96636162643703720221e+01, // 0xC033A9E2C168907F + -4.23253133372830490089e+01, // 0xC04529A3DE104AAA + -2.13719211703704061733e+01, // 0xC0355F3639CF6E52 +} + +var q1S2 = [6]float64{ + 2.95333629060523854548e+01, // 0x403D888A78AE64FF + 2.52981549982190529136e+02, // 0x406F9F68DB821CBA + 7.57502834868645436472e+02, // 0x4087AC05CE49A0F7 + 7.39393205320467245656e+02, // 0x40871B2548D4C029 + 1.55949003336666123687e+02, // 0x40637E5E3C3ED8D4 + -4.95949898822628210127e+00, // 0xC013D686E71BE86B +} + +func qone(x float64) float64 { + var p, q *[6]float64 + if x >= 8 { + p = &q1R8 + q = &q1S8 + } else if x >= 4.5454 { + p = &q1R5 + q = &q1S5 + } else if x >= 2.8571 { + p = &q1R3 + q = &q1S3 + } else if x >= 2 { + p = &q1R2 + q = &q1S2 + } + z := 1 / (x * x) + r := p[0] + z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))) + s := 1 + z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5]))))) + return (0.375 + r/s) / x +} diff --git a/gnovm/stdlibs/math/jn.gno b/gnovm/stdlibs/math/jn.gno new file mode 100644 index 00000000000..3491692a96c --- /dev/null +++ b/gnovm/stdlibs/math/jn.gno @@ -0,0 +1,306 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Bessel function of the first and second kinds of order n. +*/ + +// The original C code and the long comment below are +// from FreeBSD's /usr/src/lib/msun/src/e_jn.c and +// came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// __ieee754_jn(n, x), __ieee754_yn(n, x) +// floating point Bessel's function of the 1st and 2nd kind +// of order n +// +// Special cases: +// y0(0)=y1(0)=yn(n,0) = -inf with division by zero signal; +// y0(-ve)=y1(-ve)=yn(n,-ve) are NaN with invalid signal. +// Note 2. About jn(n,x), yn(n,x) +// For n=0, j0(x) is called, +// for n=1, j1(x) is called, +// for nx, a continued fraction approximation to +// j(n,x)/j(n-1,x) is evaluated and then backward +// recursion is used starting from a supposed value +// for j(n,x). The resulting value of j(0,x) is +// compared with the actual value to correct the +// supposed value of j(n,x). +// +// yn(n,x) is similar in all respects, except +// that forward recursion is used for all +// values of n>1. + +// Jn returns the order-n Bessel function of the first kind. +// +// Special cases are: +// +// Jn(n, ±Inf) = 0 +// Jn(n, NaN) = NaN +func Jn(n int, x float64) float64 { + const ( + TwoM29 = 1.0 / (1 << 29) // 2**-29 0x3e10000000000000 + Two302 = 1 << 302 // 2**302 0x52D0000000000000 + ) + // special cases + switch { + case IsNaN(x): + return x + case IsInf(x, 0): + return 0 + } + // J(-n, x) = (-1)**n * J(n, x), J(n, -x) = (-1)**n * J(n, x) + // Thus, J(-n, x) = J(n, -x) + + if n == 0 { + return J0(x) + } + if x == 0 { + return 0 + } + if n < 0 { + n, x = -n, -x + } + if n == 1 { + return J1(x) + } + sign := false + if x < 0 { + x = -x + if n&1 == 1 { + sign = true // odd n and negative x + } + } + var b float64 + if float64(n) <= x { + // Safe to use J(n+1,x)=2n/x *J(n,x)-J(n-1,x) + if x >= Two302 { // x > 2**302 + + // (x >> n**2) + // Jn(x) = cos(x-(2n+1)*pi/4)*sqrt(2/x*pi) + // Yn(x) = sin(x-(2n+1)*pi/4)*sqrt(2/x*pi) + // Let s=sin(x), c=cos(x), + // xn=x-(2n+1)*pi/4, sqt2 = sqrt(2),then + // + // n sin(xn)*sqt2 cos(xn)*sqt2 + // ---------------------------------- + // 0 s-c c+s + // 1 -s-c -c+s + // 2 -s+c -c-s + // 3 s+c c-s + + var temp float64 + switch s, c := Sincos(x); n & 3 { + case 0: + temp = c + s + case 1: + temp = -c + s + case 2: + temp = -c - s + case 3: + temp = c - s + } + b = (1 / SqrtPi) * temp / Sqrt(x) + } else { + b = J1(x) + for i, a := 1, J0(x); i < n; i++ { + a, b = b, b*(float64(i+i)/x)-a // avoid underflow + } + } + } else { + if x < TwoM29 { // x < 2**-29 + // x is tiny, return the first Taylor expansion of J(n,x) + // J(n,x) = 1/n!*(x/2)**n - ... + + if n > 33 { // underflow + b = 0 + } else { + temp := x * 0.5 + b = temp + a := 1.0 + for i := 2; i <= n; i++ { + a *= float64(i) // a = n! + b *= temp // b = (x/2)**n + } + b /= a + } + } else { + // use backward recurrence + // x x**2 x**2 + // J(n,x)/J(n-1,x) = ---- ------ ------ ..... + // 2n - 2(n+1) - 2(n+2) + // + // 1 1 1 + // (for large x) = ---- ------ ------ ..... + // 2n 2(n+1) 2(n+2) + // -- - ------ - ------ - + // x x x + // + // Let w = 2n/x and h=2/x, then the above quotient + // is equal to the continued fraction: + // 1 + // = ----------------------- + // 1 + // w - ----------------- + // 1 + // w+h - --------- + // w+2h - ... + // + // To determine how many terms needed, let + // Q(0) = w, Q(1) = w(w+h) - 1, + // Q(k) = (w+k*h)*Q(k-1) - Q(k-2), + // When Q(k) > 1e4 good for single + // When Q(k) > 1e9 good for double + // When Q(k) > 1e17 good for quadruple + + // determine k + w := float64(n+n) / x + h := 2 / x + q0 := w + z := w + h + q1 := w*z - 1 + k := 1 + for q1 < 1e9 { + k++ + z += h + q0, q1 = q1, z*q1-q0 + } + m := n + n + t := 0.0 + for i := 2 * (n + k); i >= m; i -= 2 { + t = 1 / (float64(i)/x - t) + } + a := t + b = 1 + // estimate log((2/x)**n*n!) = n*log(2/x)+n*ln(n) + // Hence, if n*(log(2n/x)) > ... + // single 8.8722839355e+01 + // double 7.09782712893383973096e+02 + // long double 1.1356523406294143949491931077970765006170e+04 + // then recurrent value may overflow and the result is + // likely underflow to zero + + tmp := float64(n) + v := 2 / x + tmp = tmp * Log(Abs(v*tmp)) + if tmp < 7.09782712893383973096e+02 { + for i := n - 1; i > 0; i-- { + di := float64(i + i) + a, b = b, b*di/x-a + } + } else { + for i := n - 1; i > 0; i-- { + di := float64(i + i) + a, b = b, b*di/x-a + // scale b to avoid spurious overflow + if b > 1e100 { + a /= b + t /= b + b = 1 + } + } + } + b = t * J0(x) / b + } + } + if sign { + return -b + } + return b +} + +// Yn returns the order-n Bessel function of the second kind. +// +// Special cases are: +// +// Yn(n, +Inf) = 0 +// Yn(n ≥ 0, 0) = -Inf +// Yn(n < 0, 0) = +Inf if n is odd, -Inf if n is even +// Yn(n, x < 0) = NaN +// Yn(n, NaN) = NaN +func Yn(n int, x float64) float64 { + const Two302 = 1 << 302 // 2**302 0x52D0000000000000 + // special cases + switch { + case x < 0 || IsNaN(x): + return NaN() + case IsInf(x, 1): + return 0 + } + + if n == 0 { + return Y0(x) + } + if x == 0 { + if n < 0 && n&1 == 1 { + return Inf(1) + } + return Inf(-1) + } + sign := false + if n < 0 { + n = -n + if n&1 == 1 { + sign = true // sign true if n < 0 && |n| odd + } + } + if n == 1 { + if sign { + return -Y1(x) + } + return Y1(x) + } + var b float64 + if x >= Two302 { // x > 2**302 + // (x >> n**2) + // Jn(x) = cos(x-(2n+1)*pi/4)*sqrt(2/x*pi) + // Yn(x) = sin(x-(2n+1)*pi/4)*sqrt(2/x*pi) + // Let s=sin(x), c=cos(x), + // xn=x-(2n+1)*pi/4, sqt2 = sqrt(2),then + // + // n sin(xn)*sqt2 cos(xn)*sqt2 + // ---------------------------------- + // 0 s-c c+s + // 1 -s-c -c+s + // 2 -s+c -c-s + // 3 s+c c-s + + var temp float64 + switch s, c := Sincos(x); n & 3 { + case 0: + temp = s - c + case 1: + temp = -s - c + case 2: + temp = -s + c + case 3: + temp = s + c + } + b = (1 / SqrtPi) * temp / Sqrt(x) + } else { + a := Y0(x) + b = Y1(x) + // quit if b is -inf + for i := 1; i < n && !IsInf(b, -1); i++ { + a, b = b, (float64(i+i)/x)*b-a + } + } + if sign { + return -b + } + return b +} diff --git a/gnovm/stdlibs/math/ldexp.gno b/gnovm/stdlibs/math/ldexp.gno index 5af0cff2c3b..d82114d5181 100644 --- a/gnovm/stdlibs/math/ldexp.gno +++ b/gnovm/stdlibs/math/ldexp.gno @@ -4,10 +4,6 @@ package math -import ( - imath "internal/math" -) - // Ldexp is the inverse of Frexp. // It returns frac × 2**exp. // @@ -17,11 +13,6 @@ import ( // Ldexp(±Inf, exp) = ±Inf // Ldexp(NaN, exp) = NaN func Ldexp(frac float64, exp int) float64 { - /* XXX - if haveArchLdexp { - return archLdexp(frac, exp) - } - */ return ldexp(frac, exp) } @@ -35,7 +26,7 @@ func ldexp(frac float64, exp int) float64 { } frac, e := normalize(frac) exp += e - x := imath.Float64bits(frac) + x := Float64bits(frac) exp += int(x>>shift)&mask - bias if exp < -1075 { return Copysign(0, frac) // underflow @@ -53,5 +44,5 @@ func ldexp(frac float64, exp int) float64 { } x &^= mask << shift x |= uint64(exp+bias) << shift - return m * imath.Float64frombits(x) + return m * Float64frombits(x) } diff --git a/gnovm/stdlibs/math/lgamma.gno b/gnovm/stdlibs/math/lgamma.gno new file mode 100644 index 00000000000..1fa22f9d5d0 --- /dev/null +++ b/gnovm/stdlibs/math/lgamma.gno @@ -0,0 +1,372 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Floating-point logarithm of the Gamma function. +*/ + +// The original C code and the long comment below are +// from FreeBSD's /usr/src/lib/msun/src/e_lgamma_r.c and +// came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// __ieee754_lgamma_r(x, signgamp) +// Reentrant version of the logarithm of the Gamma function +// with user provided pointer for the sign of Gamma(x). +// +// Method: +// 1. Argument Reduction for 0 < x <= 8 +// Since gamma(1+s)=s*gamma(s), for x in [0,8], we may +// reduce x to a number in [1.5,2.5] by +// lgamma(1+s) = log(s) + lgamma(s) +// for example, +// lgamma(7.3) = log(6.3) + lgamma(6.3) +// = log(6.3*5.3) + lgamma(5.3) +// = log(6.3*5.3*4.3*3.3*2.3) + lgamma(2.3) +// 2. Polynomial approximation of lgamma around its +// minimum (ymin=1.461632144968362245) to maintain monotonicity. +// On [ymin-0.23, ymin+0.27] (i.e., [1.23164,1.73163]), use +// Let z = x-ymin; +// lgamma(x) = -1.214862905358496078218 + z**2*poly(z) +// poly(z) is a 14 degree polynomial. +// 2. Rational approximation in the primary interval [2,3] +// We use the following approximation: +// s = x-2.0; +// lgamma(x) = 0.5*s + s*P(s)/Q(s) +// with accuracy +// |P/Q - (lgamma(x)-0.5s)| < 2**-61.71 +// Our algorithms are based on the following observation +// +// zeta(2)-1 2 zeta(3)-1 3 +// lgamma(2+s) = s*(1-Euler) + --------- * s - --------- * s + ... +// 2 3 +// +// where Euler = 0.5772156649... is the Euler constant, which +// is very close to 0.5. +// +// 3. For x>=8, we have +// lgamma(x)~(x-0.5)log(x)-x+0.5*log(2pi)+1/(12x)-1/(360x**3)+.... +// (better formula: +// lgamma(x)~(x-0.5)*(log(x)-1)-.5*(log(2pi)-1) + ...) +// Let z = 1/x, then we approximation +// f(z) = lgamma(x) - (x-0.5)(log(x)-1) +// by +// 3 5 11 +// w = w0 + w1*z + w2*z + w3*z + ... + w6*z +// where +// |w - f(z)| < 2**-58.74 +// +// 4. For negative x, since (G is gamma function) +// -x*G(-x)*G(x) = pi/sin(pi*x), +// we have +// G(x) = pi/(sin(pi*x)*(-x)*G(-x)) +// since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0 +// Hence, for x<0, signgam = sign(sin(pi*x)) and +// lgamma(x) = log(|Gamma(x)|) +// = log(pi/(|x*sin(pi*x)|)) - lgamma(-x); +// Note: one should avoid computing pi*(-x) directly in the +// computation of sin(pi*(-x)). +// +// 5. Special Cases +// lgamma(2+s) ~ s*(1-Euler) for tiny s +// lgamma(1)=lgamma(2)=0 +// lgamma(x) ~ -log(x) for tiny x +// lgamma(0) = lgamma(inf) = inf +// lgamma(-integer) = +-inf +// +// + +var _lgamA = [...]float64{ + 7.72156649015328655494e-02, // 0x3FB3C467E37DB0C8 + 3.22467033424113591611e-01, // 0x3FD4A34CC4A60FAD + 6.73523010531292681824e-02, // 0x3FB13E001A5562A7 + 2.05808084325167332806e-02, // 0x3F951322AC92547B + 7.38555086081402883957e-03, // 0x3F7E404FB68FEFE8 + 2.89051383673415629091e-03, // 0x3F67ADD8CCB7926B + 1.19270763183362067845e-03, // 0x3F538A94116F3F5D + 5.10069792153511336608e-04, // 0x3F40B6C689B99C00 + 2.20862790713908385557e-04, // 0x3F2CF2ECED10E54D + 1.08011567247583939954e-04, // 0x3F1C5088987DFB07 + 2.52144565451257326939e-05, // 0x3EFA7074428CFA52 + 4.48640949618915160150e-05, // 0x3F07858E90A45837 +} + +var _lgamR = [...]float64{ + 1.0, // placeholder + 1.39200533467621045958e+00, // 0x3FF645A762C4AB74 + 7.21935547567138069525e-01, // 0x3FE71A1893D3DCDC + 1.71933865632803078993e-01, // 0x3FC601EDCCFBDF27 + 1.86459191715652901344e-02, // 0x3F9317EA742ED475 + 7.77942496381893596434e-04, // 0x3F497DDACA41A95B + 7.32668430744625636189e-06, // 0x3EDEBAF7A5B38140 +} + +var _lgamS = [...]float64{ + -7.72156649015328655494e-02, // 0xBFB3C467E37DB0C8 + 2.14982415960608852501e-01, // 0x3FCB848B36E20878 + 3.25778796408930981787e-01, // 0x3FD4D98F4F139F59 + 1.46350472652464452805e-01, // 0x3FC2BB9CBEE5F2F7 + 2.66422703033638609560e-02, // 0x3F9B481C7E939961 + 1.84028451407337715652e-03, // 0x3F5E26B67368F239 + 3.19475326584100867617e-05, // 0x3F00BFECDD17E945 +} + +var _lgamT = [...]float64{ + 4.83836122723810047042e-01, // 0x3FDEF72BC8EE38A2 + -1.47587722994593911752e-01, // 0xBFC2E4278DC6C509 + 6.46249402391333854778e-02, // 0x3FB08B4294D5419B + -3.27885410759859649565e-02, // 0xBFA0C9A8DF35B713 + 1.79706750811820387126e-02, // 0x3F9266E7970AF9EC + -1.03142241298341437450e-02, // 0xBF851F9FBA91EC6A + 6.10053870246291332635e-03, // 0x3F78FCE0E370E344 + -3.68452016781138256760e-03, // 0xBF6E2EFFB3E914D7 + 2.25964780900612472250e-03, // 0x3F6282D32E15C915 + -1.40346469989232843813e-03, // 0xBF56FE8EBF2D1AF1 + 8.81081882437654011382e-04, // 0x3F4CDF0CEF61A8E9 + -5.38595305356740546715e-04, // 0xBF41A6109C73E0EC + 3.15632070903625950361e-04, // 0x3F34AF6D6C0EBBF7 + -3.12754168375120860518e-04, // 0xBF347F24ECC38C38 + 3.35529192635519073543e-04, // 0x3F35FD3EE8C2D3F4 +} + +var _lgamU = [...]float64{ + -7.72156649015328655494e-02, // 0xBFB3C467E37DB0C8 + 6.32827064025093366517e-01, // 0x3FE4401E8B005DFF + 1.45492250137234768737e+00, // 0x3FF7475CD119BD6F + 9.77717527963372745603e-01, // 0x3FEF497644EA8450 + 2.28963728064692451092e-01, // 0x3FCD4EAEF6010924 + 1.33810918536787660377e-02, // 0x3F8B678BBF2BAB09 +} + +var _lgamV = [...]float64{ + 1.0, + 2.45597793713041134822e+00, // 0x4003A5D7C2BD619C + 2.12848976379893395361e+00, // 0x40010725A42B18F5 + 7.69285150456672783825e-01, // 0x3FE89DFBE45050AF + 1.04222645593369134254e-01, // 0x3FBAAE55D6537C88 + 3.21709242282423911810e-03, // 0x3F6A5ABB57D0CF61 +} + +var _lgamW = [...]float64{ + 4.18938533204672725052e-01, // 0x3FDACFE390C97D69 + 8.33333333333329678849e-02, // 0x3FB555555555553B + -2.77777777728775536470e-03, // 0xBF66C16C16B02E5C + 7.93650558643019558500e-04, // 0x3F4A019F98CF38B6 + -5.95187557450339963135e-04, // 0xBF4380CB8C0FE741 + 8.36339918996282139126e-04, // 0x3F4B67BA4CDAD5D1 + -1.63092934096575273989e-03, // 0xBF5AB89D0B9E43E4 +} + +// Lgamma returns the natural logarithm and sign (-1 or +1) of Gamma(x). +// +// Special cases are: +// +// Lgamma(+Inf) = +Inf +// Lgamma(0) = +Inf +// Lgamma(-integer) = +Inf +// Lgamma(-Inf) = -Inf +// Lgamma(NaN) = NaN +func Lgamma(x float64) (lgamma float64, sign int) { + const ( + Ymin = 1.461632144968362245 + Two52 = 1 << 52 // 0x4330000000000000 ~4.5036e+15 + Two53 = 1 << 53 // 0x4340000000000000 ~9.0072e+15 + Two58 = 1 << 58 // 0x4390000000000000 ~2.8823e+17 + Tiny = 1.0 / (1 << 70) // 0x3b90000000000000 ~8.47033e-22 + Tc = 1.46163214496836224576e+00 // 0x3FF762D86356BE3F + Tf = -1.21486290535849611461e-01 // 0xBFBF19B9BCC38A42 + // Tt = -(tail of Tf) + Tt = -3.63867699703950536541e-18 // 0xBC50C7CAA48A971F + ) + // special cases + sign = 1 + switch { + case IsNaN(x): + lgamma = x + return + case IsInf(x, 0): + lgamma = x + return + case x == 0: + lgamma = Inf(1) + return + } + + neg := false + if x < 0 { + x = -x + neg = true + } + + if x < Tiny { // if |x| < 2**-70, return -log(|x|) + if neg { + sign = -1 + } + lgamma = -Log(x) + return + } + var nadj float64 + if neg { + if x >= Two52 { // |x| >= 2**52, must be -integer + lgamma = Inf(1) + return + } + t := sinPi(x) + if t == 0 { + lgamma = Inf(1) // -integer + return + } + nadj = Log(Pi / Abs(t*x)) + if t < 0 { + sign = -1 + } + } + + switch { + case x == 1 || x == 2: // purge off 1 and 2 + lgamma = 0 + return + case x < 2: // use lgamma(x) = lgamma(x+1) - log(x) + var y float64 + var i int + if x <= 0.9 { + lgamma = -Log(x) + switch { + case x >= (Ymin - 1 + 0.27): // 0.7316 <= x <= 0.9 + y = 1 - x + i = 0 + case x >= (Ymin - 1 - 0.27): // 0.2316 <= x < 0.7316 + y = x - (Tc - 1) + i = 1 + default: // 0 < x < 0.2316 + y = x + i = 2 + } + } else { + lgamma = 0 + switch { + case x >= (Ymin + 0.27): // 1.7316 <= x < 2 + y = 2 - x + i = 0 + case x >= (Ymin - 0.27): // 1.2316 <= x < 1.7316 + y = x - Tc + i = 1 + default: // 0.9 < x < 1.2316 + y = x - 1 + i = 2 + } + } + switch i { + case 0: + z := y * y + p1 := _lgamA[0] + z*(_lgamA[2]+z*(_lgamA[4]+z*(_lgamA[6]+z*(_lgamA[8]+z*_lgamA[10])))) + p2 := z * (_lgamA[1] + z*(+_lgamA[3]+z*(_lgamA[5]+z*(_lgamA[7]+z*(_lgamA[9]+z*_lgamA[11]))))) + p := y*p1 + p2 + lgamma += (p - 0.5*y) + case 1: + z := y * y + w := z * y + p1 := _lgamT[0] + w*(_lgamT[3]+w*(_lgamT[6]+w*(_lgamT[9]+w*_lgamT[12]))) // parallel comp + p2 := _lgamT[1] + w*(_lgamT[4]+w*(_lgamT[7]+w*(_lgamT[10]+w*_lgamT[13]))) + p3 := _lgamT[2] + w*(_lgamT[5]+w*(_lgamT[8]+w*(_lgamT[11]+w*_lgamT[14]))) + p := z*p1 - (Tt - w*(p2+y*p3)) + lgamma += (Tf + p) + case 2: + p1 := y * (_lgamU[0] + y*(_lgamU[1]+y*(_lgamU[2]+y*(_lgamU[3]+y*(_lgamU[4]+y*_lgamU[5]))))) + p2 := 1 + y*(_lgamV[1]+y*(_lgamV[2]+y*(_lgamV[3]+y*(_lgamV[4]+y*_lgamV[5])))) + lgamma += (-0.5*y + p1/p2) + } + case x < 8: // 2 <= x < 8 + i := int(x) + y := x - float64(i) + p := y * (_lgamS[0] + y*(_lgamS[1]+y*(_lgamS[2]+y*(_lgamS[3]+y*(_lgamS[4]+y*(_lgamS[5]+y*_lgamS[6])))))) + q := 1 + y*(_lgamR[1]+y*(_lgamR[2]+y*(_lgamR[3]+y*(_lgamR[4]+y*(_lgamR[5]+y*_lgamR[6]))))) + lgamma = 0.5*y + p/q + z := 1.0 // Lgamma(1+s) = Log(s) + Lgamma(s) + switch i { + case 7: + z *= (y + 6) + fallthrough + case 6: + z *= (y + 5) + fallthrough + case 5: + z *= (y + 4) + fallthrough + case 4: + z *= (y + 3) + fallthrough + case 3: + z *= (y + 2) + lgamma += Log(z) + } + case x < Two58: // 8 <= x < 2**58 + t := Log(x) + z := 1 / x + y := z * z + w := _lgamW[0] + z*(_lgamW[1]+y*(_lgamW[2]+y*(_lgamW[3]+y*(_lgamW[4]+y*(_lgamW[5]+y*_lgamW[6]))))) + lgamma = (x-0.5)*(t-1) + w + default: // 2**58 <= x <= Inf + lgamma = x * (Log(x) - 1) + } + if neg { + lgamma = nadj - lgamma + } + return +} + +// sinPi(x) is a helper function for negative x +func sinPi(x float64) float64 { + const ( + Two52 = 1 << 52 // 0x4330000000000000 ~4.5036e+15 + Two53 = 1 << 53 // 0x4340000000000000 ~9.0072e+15 + ) + if x < 0.25 { + return -Sin(Pi * x) + } + + // argument reduction + z := Floor(x) + var n int + if z != x { // inexact + x = Mod(x, 2) + n = int(x * 4) + } else { + if x >= Two53 { // x must be even + x = 0 + n = 0 + } else { + if x < Two52 { + z = x + Two52 // exact + } + n = int(1 & Float64bits(z)) + x = float64(n) + n <<= 2 + } + } + switch n { + case 0: + x = Sin(Pi * x) + case 1, 2: + x = Cos(Pi * (0.5 - x)) + case 3, 4: + x = Sin(Pi * (1 - x)) + case 5, 6: + x = -Cos(Pi * (x - 1.5)) + default: + x = Sin(Pi * (x - 2)) + } + return -x +} diff --git a/gnovm/stdlibs/math/log.gno b/gnovm/stdlibs/math/log.gno new file mode 100644 index 00000000000..5bcb1600ff9 --- /dev/null +++ b/gnovm/stdlibs/math/log.gno @@ -0,0 +1,126 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Floating-point logarithm. +*/ + +// The original C code, the long comment, and the constants +// below are from FreeBSD's /usr/src/lib/msun/src/e_log.c +// and came with this notice. The go code is a simpler +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// __ieee754_log(x) +// Return the logarithm of x +// +// Method : +// 1. Argument Reduction: find k and f such that +// x = 2**k * (1+f), +// where sqrt(2)/2 < 1+f < sqrt(2) . +// +// 2. Approximation of log(1+f). +// Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) +// = 2s + 2/3 s**3 + 2/5 s**5 + ....., +// = 2s + s*R +// We use a special Reme algorithm on [0,0.1716] to generate +// a polynomial of degree 14 to approximate R. The maximum error +// of this polynomial approximation is bounded by 2**-58.45. In +// other words, +// 2 4 6 8 10 12 14 +// R(z) ~ L1*s +L2*s +L3*s +L4*s +L5*s +L6*s +L7*s +// (the values of L1 to L7 are listed in the program) and +// | 2 14 | -58.45 +// | L1*s +...+L7*s - R(z) | <= 2 +// | | +// Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. +// In order to guarantee error in log below 1ulp, we compute log by +// log(1+f) = f - s*(f - R) (if f is not too large) +// log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy) +// +// 3. Finally, log(x) = k*Ln2 + log(1+f). +// = k*Ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*Ln2_lo))) +// Here Ln2 is split into two floating point number: +// Ln2_hi + Ln2_lo, +// where n*Ln2_hi is always exact for |n| < 2000. +// +// Special cases: +// log(x) is NaN with signal if x < 0 (including -INF) ; +// log(+INF) is +INF; log(0) is -INF with signal; +// log(NaN) is that NaN with no signal. +// +// Accuracy: +// according to an error analysis, the error is always less than +// 1 ulp (unit in the last place). +// +// Constants: +// The hexadecimal values are the intended ones for the following +// constants. The decimal values may be used, provided that the +// compiler will convert from decimal to binary accurately enough +// to produce the hexadecimal values shown. + +// Log returns the natural logarithm of x. +// +// Special cases are: +// +// Log(+Inf) = +Inf +// Log(0) = -Inf +// Log(x < 0) = NaN +// Log(NaN) = NaN +func Log(x float64) float64 { + return log(x) +} + +func log(x float64) float64 { + const ( + Ln2Hi = 6.93147180369123816490e-01 /* 3fe62e42 fee00000 */ + Ln2Lo = 1.90821492927058770002e-10 /* 3dea39ef 35793c76 */ + L1 = 6.666666666666735130e-01 /* 3FE55555 55555593 */ + L2 = 3.999999999940941908e-01 /* 3FD99999 9997FA04 */ + L3 = 2.857142874366239149e-01 /* 3FD24924 94229359 */ + L4 = 2.222219843214978396e-01 /* 3FCC71C5 1D8E78AF */ + L5 = 1.818357216161805012e-01 /* 3FC74664 96CB03DE */ + L6 = 1.531383769920937332e-01 /* 3FC39A09 D078C69F */ + L7 = 1.479819860511658591e-01 /* 3FC2F112 DF3E5244 */ + ) + + // special cases + switch { + case IsNaN(x) || IsInf(x, 1): + return x + case x < 0: + return NaN() + case x == 0: + return Inf(-1) + } + + // reduce + f1, ki := Frexp(x) + if f1 < Sqrt2/2 { + f1 *= 2 + ki-- + } + f := f1 - 1 + k := float64(ki) + + // compute + s := f / (2 + f) + s2 := s * s + s4 := s2 * s2 + t1 := s2 * (L1 + s4*(L3+s4*(L5+s4*L7))) + t2 := s4 * (L2 + s4*(L4+s4*L6)) + R := t1 + t2 + hfsq := 0.5 * f * f + return k*Ln2Hi - ((hfsq - (s*(hfsq+R) + k*Ln2Lo)) - f) +} diff --git a/gnovm/stdlibs/math/log10.gno b/gnovm/stdlibs/math/log10.gno new file mode 100644 index 00000000000..7c7d6cf5d3c --- /dev/null +++ b/gnovm/stdlibs/math/log10.gno @@ -0,0 +1,31 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// Log10 returns the decimal logarithm of x. +// The special cases are the same as for Log. +func Log10(x float64) float64 { + return log10(x) +} + +func log10(x float64) float64 { + return Log(x) * (1 / Ln10) +} + +// Log2 returns the binary logarithm of x. +// The special cases are the same as for Log. +func Log2(x float64) float64 { + return log2(x) +} + +func log2(x float64) float64 { + frac, exp := Frexp(x) + // Make sure exact powers of two give an exact answer. + // Don't depend on Log(0.5)*(1/Ln2)+exp being exactly exp-1. + if frac == 0.5 { + return float64(exp - 1) + } + return Log(frac)*(1/Ln2) + float64(exp) +} diff --git a/gnovm/stdlibs/math/log1p.gno b/gnovm/stdlibs/math/log1p.gno new file mode 100644 index 00000000000..819bf123504 --- /dev/null +++ b/gnovm/stdlibs/math/log1p.gno @@ -0,0 +1,200 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code, the long comment, and the constants +// below are from FreeBSD's /usr/src/lib/msun/src/s_log1p.c +// and came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// +// double log1p(double x) +// +// Method : +// 1. Argument Reduction: find k and f such that +// 1+x = 2**k * (1+f), +// where sqrt(2)/2 < 1+f < sqrt(2) . +// +// Note. If k=0, then f=x is exact. However, if k!=0, then f +// may not be representable exactly. In that case, a correction +// term is need. Let u=1+x rounded. Let c = (1+x)-u, then +// log(1+x) - log(u) ~ c/u. Thus, we proceed to compute log(u), +// and add back the correction term c/u. +// (Note: when x > 2**53, one can simply return log(x)) +// +// 2. Approximation of log1p(f). +// Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) +// = 2s + 2/3 s**3 + 2/5 s**5 + ....., +// = 2s + s*R +// We use a special Reme algorithm on [0,0.1716] to generate +// a polynomial of degree 14 to approximate R The maximum error +// of this polynomial approximation is bounded by 2**-58.45. In +// other words, +// 2 4 6 8 10 12 14 +// R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s +Lp6*s +Lp7*s +// (the values of Lp1 to Lp7 are listed in the program) +// and +// | 2 14 | -58.45 +// | Lp1*s +...+Lp7*s - R(z) | <= 2 +// | | +// Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. +// In order to guarantee error in log below 1ulp, we compute log +// by +// log1p(f) = f - (hfsq - s*(hfsq+R)). +// +// 3. Finally, log1p(x) = k*ln2 + log1p(f). +// = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) +// Here ln2 is split into two floating point number: +// ln2_hi + ln2_lo, +// where n*ln2_hi is always exact for |n| < 2000. +// +// Special cases: +// log1p(x) is NaN with signal if x < -1 (including -INF) ; +// log1p(+INF) is +INF; log1p(-1) is -INF with signal; +// log1p(NaN) is that NaN with no signal. +// +// Accuracy: +// according to an error analysis, the error is always less than +// 1 ulp (unit in the last place). +// +// Constants: +// The hexadecimal values are the intended ones for the following +// constants. The decimal values may be used, provided that the +// compiler will convert from decimal to binary accurately enough +// to produce the hexadecimal values shown. +// +// Note: Assuming log() return accurate answer, the following +// algorithm can be used to compute log1p(x) to within a few ULP: +// +// u = 1+x; +// if(u==1.0) return x ; else +// return log(u)*(x/(u-1.0)); +// +// See HP-15C Advanced Functions Handbook, p.193. + +// Log1p returns the natural logarithm of 1 plus its argument x. +// It is more accurate than Log(1 + x) when x is near zero. +// +// Special cases are: +// +// Log1p(+Inf) = +Inf +// Log1p(±0) = ±0 +// Log1p(-1) = -Inf +// Log1p(x < -1) = NaN +// Log1p(NaN) = NaN +func Log1p(x float64) float64 { + return log1p(x) +} + +func log1p(x float64) float64 { + const ( + Sqrt2M1 = 4.142135623730950488017e-01 // Sqrt(2)-1 = 0x3fda827999fcef34 + Sqrt2HalfM1 = -2.928932188134524755992e-01 // Sqrt(2)/2-1 = 0xbfd2bec333018866 + Small = 1.0 / (1 << 29) // 2**-29 = 0x3e20000000000000 + Tiny = 1.0 / (1 << 54) // 2**-54 + Two53 = 1 << 53 // 2**53 + Ln2Hi = 6.93147180369123816490e-01 // 3fe62e42fee00000 + Ln2Lo = 1.90821492927058770002e-10 // 3dea39ef35793c76 + Lp1 = 6.666666666666735130e-01 // 3FE5555555555593 + Lp2 = 3.999999999940941908e-01 // 3FD999999997FA04 + Lp3 = 2.857142874366239149e-01 // 3FD2492494229359 + Lp4 = 2.222219843214978396e-01 // 3FCC71C51D8E78AF + Lp5 = 1.818357216161805012e-01 // 3FC7466496CB03DE + Lp6 = 1.531383769920937332e-01 // 3FC39A09D078C69F + Lp7 = 1.479819860511658591e-01 // 3FC2F112DF3E5244 + ) + + // special cases + switch { + case x < -1 || IsNaN(x): // includes -Inf + return NaN() + case x == -1: + return Inf(-1) + case IsInf(x, 1): + return Inf(1) + } + + absx := Abs(x) + + var f float64 + var iu uint64 + k := 1 + if absx < Sqrt2M1 { // |x| < Sqrt(2)-1 + if absx < Small { // |x| < 2**-29 + if absx < Tiny { // |x| < 2**-54 + return x + } + return x - x*x*0.5 + } + if x > Sqrt2HalfM1 { // Sqrt(2)/2-1 < x + // (Sqrt(2)/2-1) < x < (Sqrt(2)-1) + k = 0 + f = x + iu = 1 + } + } + var c float64 + if k != 0 { + var u float64 + if absx < Two53 { // 1<<53 + u = 1.0 + x + iu = Float64bits(u) + k = int((iu >> 52) - 1023) + // correction term + if k > 0 { + c = 1.0 - (u - x) + } else { + c = x - (u - 1.0) + } + c /= u + } else { + u = x + iu = Float64bits(u) + k = int((iu >> 52) - 1023) + c = 0 + } + iu &= 0x000fffffffffffff + if iu < 0x0006a09e667f3bcd { // mantissa of Sqrt(2) + u = Float64frombits(iu | 0x3ff0000000000000) // normalize u + } else { + k++ + u = Float64frombits(iu | 0x3fe0000000000000) // normalize u/2 + iu = (0x0010000000000000 - iu) >> 2 + } + f = u - 1.0 // Sqrt(2)/2 < u < Sqrt(2) + } + hfsq := 0.5 * f * f + var s, R, z float64 + if iu == 0 { // |f| < 2**-20 + if f == 0 { + if k == 0 { + return 0 + } + c += float64(k) * Ln2Lo + return float64(k)*Ln2Hi + c + } + R = hfsq * (1.0 - 0.66666666666666666*f) // avoid division + if k == 0 { + return f - R + } + return float64(k)*Ln2Hi - ((R - (float64(k)*Ln2Lo + c)) - f) + } + s = f / (2.0 + f) + z = s * s + R = z * (Lp1 + z*(Lp2+z*(Lp3+z*(Lp4+z*(Lp5+z*(Lp6+z*Lp7)))))) + if k == 0 { + return f - (hfsq - s*(hfsq+R)) + } + return float64(k)*Ln2Hi - ((hfsq - (s*(hfsq+R) + (float64(k)*Ln2Lo + c))) - f) +} diff --git a/gnovm/stdlibs/math/logb.gno b/gnovm/stdlibs/math/logb.gno new file mode 100644 index 00000000000..1a46464127c --- /dev/null +++ b/gnovm/stdlibs/math/logb.gno @@ -0,0 +1,52 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// Logb returns the binary exponent of x. +// +// Special cases are: +// +// Logb(±Inf) = +Inf +// Logb(0) = -Inf +// Logb(NaN) = NaN +func Logb(x float64) float64 { + // special cases + switch { + case x == 0: + return Inf(-1) + case IsInf(x, 0): + return Inf(1) + case IsNaN(x): + return x + } + return float64(ilogb(x)) +} + +// Ilogb returns the binary exponent of x as an integer. +// +// Special cases are: +// +// Ilogb(±Inf) = MaxInt32 +// Ilogb(0) = MinInt32 +// Ilogb(NaN) = MaxInt32 +func Ilogb(x float64) int { + // special cases + switch { + case x == 0: + return MinInt32 + case IsNaN(x): + return MaxInt32 + case IsInf(x, 0): + return MaxInt32 + } + return ilogb(x) +} + +// ilogb returns the binary exponent of x. It assumes x is finite and +// non-zero. +func ilogb(x float64) int { + x, exp := normalize(x) + return int((Float64bits(x)>>shift)&mask) - bias + exp +} diff --git a/gnovm/stdlibs/math/mod.gno b/gnovm/stdlibs/math/mod.gno new file mode 100644 index 00000000000..54cf0b88d2a --- /dev/null +++ b/gnovm/stdlibs/math/mod.gno @@ -0,0 +1,49 @@ +// Copyright 2009-2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Floating-point mod function. +*/ + +// Mod returns the floating-point remainder of x/y. +// The magnitude of the result is less than y and its +// sign agrees with that of x. +// +// Special cases are: +// +// Mod(±Inf, y) = NaN +// Mod(NaN, y) = NaN +// Mod(x, 0) = NaN +// Mod(x, ±Inf) = x +// Mod(x, NaN) = NaN +func Mod(x, y float64) float64 { + return mod(x, y) +} + +func mod(x, y float64) float64 { + if y == 0 || IsInf(x, 0) || IsNaN(x) || IsNaN(y) { + return NaN() + } + y = Abs(y) + + yfr, yexp := Frexp(y) + r := x + if x < 0 { + r = -x + } + + for r >= y { + rfr, rexp := Frexp(r) + if rfr < yfr { + rexp = rexp - 1 + } + r = r - Ldexp(y, rexp-yexp) + } + if x < 0 { + r = -r + } + return r +} diff --git a/gnovm/stdlibs/math/modf.gno b/gnovm/stdlibs/math/modf.gno index d0fb27ef007..868d2c6f5f9 100644 --- a/gnovm/stdlibs/math/modf.gno +++ b/gnovm/stdlibs/math/modf.gno @@ -4,10 +4,6 @@ package math -import ( - imath "internal/math" -) - // Modf returns integer and fractional floating-point numbers // that sum to f. Both values have the same sign as f. // @@ -16,11 +12,6 @@ import ( // Modf(±Inf) = ±Inf, NaN // Modf(NaN) = NaN, NaN func Modf(f float64) (int float64, frac float64) { - /* XXX - if haveArchModf { - return archModf(f) - } - */ return modf(f) } @@ -36,14 +27,14 @@ func modf(f float64) (int float64, frac float64) { return 0, f } - x := imath.Float64bits(f) + x := Float64bits(f) e := uint(x>>shift)&mask - bias // Keep the top 12+e bits, the integer part; clear the rest. if e < 64-12 { x &^= 1<<(64-12-e) - 1 } - int = imath.Float64frombits(x) + int = Float64frombits(x) frac = f - int return } diff --git a/gnovm/stdlibs/math/native.gno b/gnovm/stdlibs/math/native.gno new file mode 100644 index 00000000000..b1b5684f9af --- /dev/null +++ b/gnovm/stdlibs/math/native.gno @@ -0,0 +1,21 @@ +package math + +// Float32bits returns the IEEE 754 binary representation of f, with the sign +// bit of f and the result in the same bit position. +// Float32bits(Float32frombits(x)) == x. +func Float32bits(f float32) uint32 // injected + +// Float32frombits returns the floating-point number corresponding to the IEEE +// 754 binary representation b, with the sign bit of b and the result in the +// same bit position. Float32frombits(Float32bits(x)) == x. +func Float32frombits(b uint32) float32 // injected + +// Float64bits returns the IEEE 754 binary representation of f, with the sign +// bit of f and the result in the same bit position. +// Float64bits(Float64frombits(x)) == x. +func Float64bits(f float64) uint64 // injected + +// Float64frombits returns the floating-point number corresponding to the IEEE +// 754 binary representation b, with the sign bit of b and the result in the +// same bit position. Float64frombits(Float64bits(x)) == x. +func Float64frombits(b uint64) float64 // injected diff --git a/gnovm/stdlibs/math/native.go b/gnovm/stdlibs/math/native.go new file mode 100644 index 00000000000..21021085f6d --- /dev/null +++ b/gnovm/stdlibs/math/native.go @@ -0,0 +1,8 @@ +package math + +import "math" + +func Float32bits(f float32) uint32 { return math.Float32bits(f) } +func Float32frombits(b uint32) float32 { return math.Float32frombits(b) } +func Float64bits(f float64) uint64 { return math.Float64bits(f) } +func Float64frombits(b uint64) float64 { return math.Float64frombits(b) } diff --git a/gnovm/stdlibs/math/nextafter.gno b/gnovm/stdlibs/math/nextafter.gno new file mode 100644 index 00000000000..ec18d542d9c --- /dev/null +++ b/gnovm/stdlibs/math/nextafter.gno @@ -0,0 +1,51 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// Nextafter32 returns the next representable float32 value after x towards y. +// +// Special cases are: +// +// Nextafter32(x, x) = x +// Nextafter32(NaN, y) = NaN +// Nextafter32(x, NaN) = NaN +func Nextafter32(x, y float32) (r float32) { + switch { + case IsNaN(float64(x)) || IsNaN(float64(y)): // special case + r = float32(NaN()) + case x == y: + r = x + case x == 0: + r = float32(Copysign(float64(Float32frombits(1)), float64(y))) + case (y > x) == (x > 0): + r = Float32frombits(Float32bits(x) + 1) + default: + r = Float32frombits(Float32bits(x) - 1) + } + return +} + +// Nextafter returns the next representable float64 value after x towards y. +// +// Special cases are: +// +// Nextafter(x, x) = x +// Nextafter(NaN, y) = NaN +// Nextafter(x, NaN) = NaN +func Nextafter(x, y float64) (r float64) { + switch { + case IsNaN(x) || IsNaN(y): // special case + r = NaN() + case x == y: + r = x + case x == 0: + r = Copysign(Float64frombits(1), y) + case (y > x) == (x > 0): + r = Float64frombits(Float64bits(x) + 1) + default: + r = Float64frombits(Float64bits(x) - 1) + } + return +} diff --git a/gnovm/stdlibs/math/pow.gno b/gnovm/stdlibs/math/pow.gno new file mode 100644 index 00000000000..b8544e6831f --- /dev/null +++ b/gnovm/stdlibs/math/pow.gno @@ -0,0 +1,163 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +func isOddInt(x float64) bool { + if Abs(x) >= (1 << 53) { + // 1 << 53 is the largest exact integer in the float64 format. + // Any number outside this range will be truncated before the decimal point and therefore will always be + // an even integer. + // Without this check and if x overflows int64 the int64(xi) conversion below may produce incorrect results + // on some architectures (and does so on arm64). See issue #57465. + return false + } + + xi, xf := Modf(x) + return xf == 0 && int64(xi)&1 == 1 +} + +// Special cases taken from FreeBSD's /usr/src/lib/msun/src/e_pow.c +// updated by IEEE Std. 754-2008 "Section 9.2.1 Special values". + +// Pow returns x**y, the base-x exponential of y. +// +// Special cases are (in order): +// +// Pow(x, ±0) = 1 for any x +// Pow(1, y) = 1 for any y +// Pow(x, 1) = x for any x +// Pow(NaN, y) = NaN +// Pow(x, NaN) = NaN +// Pow(±0, y) = ±Inf for y an odd integer < 0 +// Pow(±0, -Inf) = +Inf +// Pow(±0, +Inf) = +0 +// Pow(±0, y) = +Inf for finite y < 0 and not an odd integer +// Pow(±0, y) = ±0 for y an odd integer > 0 +// Pow(±0, y) = +0 for finite y > 0 and not an odd integer +// Pow(-1, ±Inf) = 1 +// Pow(x, +Inf) = +Inf for |x| > 1 +// Pow(x, -Inf) = +0 for |x| > 1 +// Pow(x, +Inf) = +0 for |x| < 1 +// Pow(x, -Inf) = +Inf for |x| < 1 +// Pow(+Inf, y) = +Inf for y > 0 +// Pow(+Inf, y) = +0 for y < 0 +// Pow(-Inf, y) = Pow(-0, -y) +// Pow(x, y) = NaN for finite x < 0 and finite non-integer y +func Pow(x, y float64) float64 { + return pow(x, y) +} + +func pow(x, y float64) float64 { + switch { + case y == 0 || x == 1: + return 1 + case y == 1: + return x + case IsNaN(x) || IsNaN(y): + return NaN() + case x == 0: + switch { + case y < 0: + if Signbit(x) && isOddInt(y) { + return Inf(-1) + } + return Inf(1) + case y > 0: + if Signbit(x) && isOddInt(y) { + return x + } + return 0 + } + case IsInf(y, 0): + switch { + case x == -1: + return 1 + case (Abs(x) < 1) == IsInf(y, 1): + return 0 + default: + return Inf(1) + } + case IsInf(x, 0): + if IsInf(x, -1) { + return Pow(1/x, -y) // Pow(-0, -y) + } + switch { + case y < 0: + return 0 + case y > 0: + return Inf(1) + } + case y == 0.5: + return Sqrt(x) + case y == -0.5: + return 1 / Sqrt(x) + } + + yi, yf := Modf(Abs(y)) + if yf != 0 && x < 0 { + return NaN() + } + if yi >= 1<<63 { + // yi is a large even int that will lead to overflow (or underflow to 0) + // for all x except -1 (x == 1 was handled earlier) + switch { + case x == -1: + return 1 + case (Abs(x) < 1) == (y > 0): + return 0 + default: + return Inf(1) + } + } + + // ans = a1 * 2**ae (= 1 for now). + a1 := 1.0 + ae := 0 + + // ans *= x**yf + if yf != 0 { + if yf > 0.5 { + yf-- + yi++ + } + a1 = Exp(yf * Log(x)) + } + + // ans *= x**yi + // by multiplying in successive squarings + // of x according to bits of yi. + // accumulate powers of two into exp. + x1, xe := Frexp(x) + for i := int64(yi); i != 0; i >>= 1 { + if xe < -1<<12 || 1<<12 < xe { + // catch xe before it overflows the left shift below + // Since i !=0 it has at least one bit still set, so ae will accumulate xe + // on at least one more iteration, ae += xe is a lower bound on ae + // the lower bound on ae exceeds the size of a float64 exp + // so the final call to Ldexp will produce under/overflow (0/Inf) + ae += xe + break + } + if i&1 == 1 { + a1 *= x1 + ae += xe + } + x1 *= x1 + xe <<= 1 + if x1 < .5 { + x1 += x1 + xe-- + } + } + + // ans = a1*2**ae + // if y < 0 { ans = 1 / ans } + // but in the opposite order + if y < 0 { + a1 = 1 / a1 + ae = -ae + } + return Ldexp(a1, ae) +} diff --git a/gnovm/stdlibs/math/pow10.gno b/gnovm/stdlibs/math/pow10.gno new file mode 100644 index 00000000000..c31ad8dbc77 --- /dev/null +++ b/gnovm/stdlibs/math/pow10.gno @@ -0,0 +1,47 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// pow10tab stores the pre-computed values 10**i for i < 32. +var pow10tab = [...]float64{ + 1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, + 1e30, 1e31, +} + +// pow10postab32 stores the pre-computed value for 10**(i*32) at index i. +var pow10postab32 = [...]float64{ + 1e00, 1e32, 1e64, 1e96, 1e128, 1e160, 1e192, 1e224, 1e256, 1e288, +} + +// pow10negtab32 stores the pre-computed value for 10**(-i*32) at index i. +var pow10negtab32 = [...]float64{ + 1e-00, 1e-32, 1e-64, 1e-96, 1e-128, 1e-160, 1e-192, 1e-224, 1e-256, 1e-288, 1e-320, +} + +// Pow10 returns 10**n, the base-10 exponential of n. +// +// Special cases are: +// +// Pow10(n) = 0 for n < -323 +// Pow10(n) = +Inf for n > 308 +func Pow10(n int) float64 { + if 0 <= n && n <= 308 { + return pow10postab32[uint(n)/32] * pow10tab[uint(n)%32] + } + + if -323 <= n && n <= 0 { + return pow10negtab32[uint(-n)/32] / pow10tab[uint(-n)%32] + } + + // n < -323 || 308 < n + if n > 0 { + return Inf(1) + } + + // n < -323 + return 0 +} diff --git a/gnovm/stdlibs/math/remainder.gno b/gnovm/stdlibs/math/remainder.gno new file mode 100644 index 00000000000..436dc595fa1 --- /dev/null +++ b/gnovm/stdlibs/math/remainder.gno @@ -0,0 +1,92 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code and the comment below are from +// FreeBSD's /usr/src/lib/msun/src/e_remainder.c and came +// with this notice. The go code is a simplified version of +// the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// __ieee754_remainder(x,y) +// Return : +// returns x REM y = x - [x/y]*y as if in infinite +// precision arithmetic, where [x/y] is the (infinite bit) +// integer nearest x/y (in half way cases, choose the even one). +// Method : +// Based on Mod() returning x - [x/y]chopped * y exactly. + +// Remainder returns the IEEE 754 floating-point remainder of x/y. +// +// Special cases are: +// +// Remainder(±Inf, y) = NaN +// Remainder(NaN, y) = NaN +// Remainder(x, 0) = NaN +// Remainder(x, ±Inf) = x +// Remainder(x, NaN) = NaN +func Remainder(x, y float64) float64 { + return remainder(x, y) +} + +func remainder(x, y float64) float64 { + const ( + Tiny = 4.45014771701440276618e-308 // 0x0020000000000000 + HalfMax = MaxFloat64 / 2 + ) + // special cases + switch { + case IsNaN(x) || IsNaN(y) || IsInf(x, 0) || y == 0: + return NaN() + case IsInf(y, 0): + return x + } + sign := false + if x < 0 { + x = -x + sign = true + } + if y < 0 { + y = -y + } + if x == y { + if sign { + zero := 0.0 + return -zero + } + return 0 + } + if y <= HalfMax { + x = Mod(x, y+y) // now x < 2y + } + if y < Tiny { + if x+x > y { + x -= y + if x+x >= y { + x -= y + } + } + } else { + yHalf := 0.5 * y + if x > yHalf { + x -= y + if x >= yHalf { + x -= y + } + } + } + if sign { + x = -x + } + return x +} diff --git a/gnovm/stdlibs/math/signbit.gno b/gnovm/stdlibs/math/signbit.gno index 38c2178932e..f6e61d660e2 100644 --- a/gnovm/stdlibs/math/signbit.gno +++ b/gnovm/stdlibs/math/signbit.gno @@ -1,10 +1,10 @@ -package math +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. -import ( - imath "internal/math" -) +package math // Signbit reports whether x is negative or negative zero. func Signbit(x float64) bool { - return imath.Float64bits(x)&(1<<63) != 0 + return Float64bits(x)&(1<<63) != 0 } diff --git a/gnovm/stdlibs/math/sin.gno b/gnovm/stdlibs/math/sin.gno index 40a611edf70..a5c70d8074e 100644 --- a/gnovm/stdlibs/math/sin.gno +++ b/gnovm/stdlibs/math/sin.gno @@ -1,7 +1,6 @@ // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// from https://github.com/golang/go/blob/e4de2e7fd04c92d4035cd268d5043f2380aef437/src/pkg/math/sin.go package math @@ -110,42 +109,49 @@ var _cos = [...]float64{ 4.16666666666665929218e-2, // 0x3fa555555555554b } -// Cos returns the cosine of x. +// Cos returns the cosine of the radian argument x. // // Special cases are: // // Cos(±Inf) = NaN // Cos(NaN) = NaN func Cos(x float64) float64 { + return cos(x) +} + +func cos(x float64) float64 { const ( - PI4A = 7.85398125648498535156e-1 // 0x3fe921fb40000000, Pi/4 split into three parts - PI4B = 3.77489470793079817668e-8 // 0x3e64442d00000000, - PI4C = 2.69515142907905952645e-15 // 0x3ce8469898cc5170, - M4PI = 1.273239544735162542821171882678754627704620361328125 // 4/pi + PI4A = 7.85398125648498535156e-1 // 0x3fe921fb40000000, Pi/4 split into three parts + PI4B = 3.77489470793079817668e-8 // 0x3e64442d00000000, + PI4C = 2.69515142907905952645e-15 // 0x3ce8469898cc5170, ) - // TODO(rsc): Remove manual inlining of IsNaN, IsInf - // when compiler does it for us // special cases switch { - case x != x || x < -MaxFloat64 || x > MaxFloat64: // IsNaN(x) || IsInf(x, 0): + case IsNaN(x) || IsInf(x, 0): return NaN() } // make argument positive sign := false - if x < 0 { - x = -x - } + x = Abs(x) - j := int64(x * M4PI) // integer part of x/(Pi/4), as integer for tests on the phase angle - y := float64(j) // integer part of x/(Pi/4), as float + var j uint64 + var y, z float64 + if x >= reduceThreshold { + j, z = trigReduce(x) + } else { + j = uint64(x * (4 / Pi)) // integer part of x/(Pi/4), as integer for tests on the phase angle + y = float64(j) // integer part of x/(Pi/4), as float - // map zeros to origin - if j&1 == 1 { - j += 1 - y += 1 + // map zeros to origin + if j&1 == 1 { + j++ + y++ + } + j &= 7 // octant modulo 2Pi radians (360 degrees) + z = ((x - y*PI4A) - y*PI4B) - y*PI4C // Extended precision modular arithmetic } - j &= 7 // octant modulo 2Pi radians (360 degrees) + if j > 3 { j -= 4 sign = !sign @@ -154,7 +160,6 @@ func Cos(x float64) float64 { sign = !sign } - z := ((x - y*PI4A) - y*PI4B) - y*PI4C // Extended precision modular arithmetic zz := z * z if j == 1 || j == 2 { y = z + z*zz*((((((_sin[0]*zz)+_sin[1])*zz+_sin[2])*zz+_sin[3])*zz+_sin[4])*zz+_sin[5]) @@ -167,7 +172,7 @@ func Cos(x float64) float64 { return y } -// Sin returns the sine of x. +// Sin returns the sine of the radian argument x. // // Special cases are: // @@ -175,19 +180,20 @@ func Cos(x float64) float64 { // Sin(±Inf) = NaN // Sin(NaN) = NaN func Sin(x float64) float64 { + return sin(x) +} + +func sin(x float64) float64 { const ( - PI4A = 7.85398125648498535156e-1 // 0x3fe921fb40000000, Pi/4 split into three parts - PI4B = 3.77489470793079817668e-8 // 0x3e64442d00000000, - PI4C = 2.69515142907905952645e-15 // 0x3ce8469898cc5170, - M4PI = 1.273239544735162542821171882678754627704620361328125 // 4/pi + PI4A = 7.85398125648498535156e-1 // 0x3fe921fb40000000, Pi/4 split into three parts + PI4B = 3.77489470793079817668e-8 // 0x3e64442d00000000, + PI4C = 2.69515142907905952645e-15 // 0x3ce8469898cc5170, ) - // TODO(rsc): Remove manual inlining of IsNaN, IsInf - // when compiler does it for us // special cases switch { - case x == 0 || x != x: // x == 0 || IsNaN(): + case x == 0 || IsNaN(x): return x // return ±0 || NaN() - case x < -MaxFloat64 || x > MaxFloat64: // IsInf(x, 0): + case IsInf(x, 0): return NaN() } @@ -198,22 +204,27 @@ func Sin(x float64) float64 { sign = true } - j := int64(x * M4PI) // integer part of x/(Pi/4), as integer for tests on the phase angle - y := float64(j) // integer part of x/(Pi/4), as float + var j uint64 + var y, z float64 + if x >= reduceThreshold { + j, z = trigReduce(x) + } else { + j = uint64(x * (4 / Pi)) // integer part of x/(Pi/4), as integer for tests on the phase angle + y = float64(j) // integer part of x/(Pi/4), as float - // map zeros to origin - if j&1 == 1 { - j += 1 - y += 1 + // map zeros to origin + if j&1 == 1 { + j++ + y++ + } + j &= 7 // octant modulo 2Pi radians (360 degrees) + z = ((x - y*PI4A) - y*PI4B) - y*PI4C // Extended precision modular arithmetic } - j &= 7 // octant modulo 2Pi radians (360 degrees) // reflect in x axis if j > 3 { sign = !sign j -= 4 } - - z := ((x - y*PI4A) - y*PI4B) - y*PI4C // Extended precision modular arithmetic zz := z * z if j == 1 || j == 2 { y = 1.0 - 0.5*zz + zz*zz*((((((_cos[0]*zz)+_cos[1])*zz+_cos[2])*zz+_cos[3])*zz+_cos[4])*zz+_cos[5]) diff --git a/gnovm/stdlibs/math/sincos.gno b/gnovm/stdlibs/math/sincos.gno new file mode 100644 index 00000000000..e3fb96094fa --- /dev/null +++ b/gnovm/stdlibs/math/sincos.gno @@ -0,0 +1,73 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// Coefficients _sin[] and _cos[] are found in pkg/math/sin.go. + +// Sincos returns Sin(x), Cos(x). +// +// Special cases are: +// +// Sincos(±0) = ±0, 1 +// Sincos(±Inf) = NaN, NaN +// Sincos(NaN) = NaN, NaN +func Sincos(x float64) (sin, cos float64) { + const ( + PI4A = 7.85398125648498535156e-1 // 0x3fe921fb40000000, Pi/4 split into three parts + PI4B = 3.77489470793079817668e-8 // 0x3e64442d00000000, + PI4C = 2.69515142907905952645e-15 // 0x3ce8469898cc5170, + ) + // special cases + switch { + case x == 0: + return x, 1 // return ±0.0, 1.0 + case IsNaN(x) || IsInf(x, 0): + return NaN(), NaN() + } + + // make argument positive + sinSign, cosSign := false, false + if x < 0 { + x = -x + sinSign = true + } + + var j uint64 + var y, z float64 + if x >= reduceThreshold { + j, z = trigReduce(x) + } else { + j = uint64(x * (4 / Pi)) // integer part of x/(Pi/4), as integer for tests on the phase angle + y = float64(j) // integer part of x/(Pi/4), as float + + if j&1 == 1 { // map zeros to origin + j++ + y++ + } + j &= 7 // octant modulo 2Pi radians (360 degrees) + z = ((x - y*PI4A) - y*PI4B) - y*PI4C // Extended precision modular arithmetic + } + if j > 3 { // reflect in x axis + j -= 4 + sinSign, cosSign = !sinSign, !cosSign + } + if j > 1 { + cosSign = !cosSign + } + + zz := z * z + cos = 1.0 - 0.5*zz + zz*zz*((((((_cos[0]*zz)+_cos[1])*zz+_cos[2])*zz+_cos[3])*zz+_cos[4])*zz+_cos[5]) + sin = z + z*zz*((((((_sin[0]*zz)+_sin[1])*zz+_sin[2])*zz+_sin[3])*zz+_sin[4])*zz+_sin[5]) + if j == 1 || j == 2 { + sin, cos = cos, sin + } + if cosSign { + cos = -cos + } + if sinSign { + sin = -sin + } + return +} diff --git a/gnovm/stdlibs/math/sinh.gno b/gnovm/stdlibs/math/sinh.gno new file mode 100644 index 00000000000..b52149e2752 --- /dev/null +++ b/gnovm/stdlibs/math/sinh.gno @@ -0,0 +1,87 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +/* + Floating-point hyperbolic sine and cosine. + + The exponential func is called for arguments + greater in magnitude than 0.5. + + A series is used for arguments smaller in magnitude than 0.5. + + Cosh(x) is computed from the exponential func for + all arguments. +*/ + +// Sinh returns the hyperbolic sine of x. +// +// Special cases are: +// +// Sinh(±0) = ±0 +// Sinh(±Inf) = ±Inf +// Sinh(NaN) = NaN +func Sinh(x float64) float64 { + return sinh(x) +} + +func sinh(x float64) float64 { + // The coefficients are #2029 from Hart & Cheney. (20.36D) + const ( + P0 = -0.6307673640497716991184787251e+6 + P1 = -0.8991272022039509355398013511e+5 + P2 = -0.2894211355989563807284660366e+4 + P3 = -0.2630563213397497062819489e+2 + Q0 = -0.6307673640497716991212077277e+6 + Q1 = 0.1521517378790019070696485176e+5 + Q2 = -0.173678953558233699533450911e+3 + ) + + sign := false + if x < 0 { + x = -x + sign = true + } + + var temp float64 + switch { + case x > 21: + temp = Exp(x) * 0.5 + + case x > 0.5: + ex := Exp(x) + temp = (ex - 1/ex) * 0.5 + + default: + sq := x * x + temp = (((P3*sq+P2)*sq+P1)*sq + P0) * x + temp = temp / (((sq+Q2)*sq+Q1)*sq + Q0) + } + + if sign { + temp = -temp + } + return temp +} + +// Cosh returns the hyperbolic cosine of x. +// +// Special cases are: +// +// Cosh(±0) = 1 +// Cosh(±Inf) = +Inf +// Cosh(NaN) = NaN +func Cosh(x float64) float64 { + return cosh(x) +} + +func cosh(x float64) float64 { + x = Abs(x) + if x > 21 { + return Exp(x) * 0.5 + } + ex := Exp(x) + return (ex + 1/ex) * 0.5 +} diff --git a/gnovm/stdlibs/math/sqrt.gno b/gnovm/stdlibs/math/sqrt.gno new file mode 100644 index 00000000000..54929ebcaf7 --- /dev/null +++ b/gnovm/stdlibs/math/sqrt.gno @@ -0,0 +1,145 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code and the long comment below are +// from FreeBSD's /usr/src/lib/msun/src/e_sqrt.c and +// came with this notice. The go code is a simplified +// version of the original C. +// +// ==================================================== +// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +// +// Developed at SunPro, a Sun Microsystems, Inc. business. +// Permission to use, copy, modify, and distribute this +// software is freely granted, provided that this notice +// is preserved. +// ==================================================== +// +// __ieee754_sqrt(x) +// Return correctly rounded sqrt. +// ----------------------------------------- +// | Use the hardware sqrt if you have one | +// ----------------------------------------- +// Method: +// Bit by bit method using integer arithmetic. (Slow, but portable) +// 1. Normalization +// Scale x to y in [1,4) with even powers of 2: +// find an integer k such that 1 <= (y=x*2**(2k)) < 4, then +// sqrt(x) = 2**k * sqrt(y) +// 2. Bit by bit computation +// Let q = sqrt(y) truncated to i bit after binary point (q = 1), +// i 0 +// i+1 2 +// s = 2*q , and y = 2 * ( y - q ). (1) +// i i i i +// +// To compute q from q , one checks whether +// i+1 i +// +// -(i+1) 2 +// (q + 2 ) <= y. (2) +// i +// -(i+1) +// If (2) is false, then q = q ; otherwise q = q + 2 . +// i+1 i i+1 i +// +// With some algebraic manipulation, it is not difficult to see +// that (2) is equivalent to +// -(i+1) +// s + 2 <= y (3) +// i i +// +// The advantage of (3) is that s and y can be computed by +// i i +// the following recurrence formula: +// if (3) is false +// +// s = s , y = y ; (4) +// i+1 i i+1 i +// +// otherwise, +// -i -(i+1) +// s = s + 2 , y = y - s - 2 (5) +// i+1 i i+1 i i +// +// One may easily use induction to prove (4) and (5). +// Note. Since the left hand side of (3) contain only i+2 bits, +// it is not necessary to do a full (53-bit) comparison +// in (3). +// 3. Final rounding +// After generating the 53 bits result, we compute one more bit. +// Together with the remainder, we can decide whether the +// result is exact, bigger than 1/2ulp, or less than 1/2ulp +// (it will never equal to 1/2ulp). +// The rounding mode can be detected by checking whether +// huge + tiny is equal to huge, and whether huge - tiny is +// equal to huge for some floating point number "huge" and "tiny". +// +// +// Notes: Rounding mode detection omitted. The constants "mask", "shift", +// and "bias" are found in src/math/bits.go + +// Sqrt returns the square root of x. +// +// Special cases are: +// +// Sqrt(+Inf) = +Inf +// Sqrt(±0) = ±0 +// Sqrt(x < 0) = NaN +// Sqrt(NaN) = NaN +func Sqrt(x float64) float64 { + return sqrt(x) +} + +// Note: On systems where Sqrt is a single instruction, the compiler +// may turn a direct call into a direct use of that instruction instead. + +func sqrt(x float64) float64 { + // special cases + switch { + case x == 0 || IsNaN(x) || IsInf(x, 1): + return x + case x < 0: + return NaN() + } + ix := Float64bits(x) + // normalize x + exp := int((ix >> shift) & mask) + if exp == 0 { // subnormal x + for ix&(1<>= 1 // exp = exp/2, exponent of square root + // generate sqrt(x) bit by bit + ix <<= 1 + var q, s uint64 // q = sqrt(x) + r := uint64(1 << (shift + 1)) // r = moving bit from MSB to LSB + for r != 0 { + t := s + r + if t <= ix { + s = t + r + ix -= t + q += r + } + ix <<= 1 + r >>= 1 + } + // final rounding + if ix != 0 { // remainder, result not exact + q += q & 1 // round according to extra bit + } + ix = q>>1 + uint64(exp-1+bias)< 2**49 = 5.6e14. +// [Accuracy loss statement from sin.go comments.] +// +// Cephes Math Library Release 2.8: June, 2000 +// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier +// +// The readme file at http://netlib.sandia.gov/cephes/ says: +// Some software in this archive may be from the book _Methods and +// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +// International, 1989) or from the Cephes Mathematical Library, a +// commercial product. In either event, it is copyrighted by the author. +// What you see here may be used freely but it comes with no support or +// guarantee. +// +// The two known misprints in the book are repaired here in the +// source listings for the gamma function and the incomplete beta +// integral. +// +// Stephen L. Moshier +// moshier@na-net.ornl.gov + +// tan coefficients +var _tanP = [...]float64{ + -1.30936939181383777646e4, // 0xc0c992d8d24f3f38 + 1.15351664838587416140e6, // 0x413199eca5fc9ddd + -1.79565251976484877988e7, // 0xc1711fead3299176 +} + +var _tanQ = [...]float64{ + 1.00000000000000000000e0, + 1.36812963470692954678e4, // 0x40cab8a5eeb36572 + -1.32089234440210967447e6, // 0xc13427bc582abc96 + 2.50083801823357915839e7, // 0x4177d98fc2ead8ef + -5.38695755929454629881e7, // 0xc189afe03cbe5a31 +} + +// Tan returns the tangent of the radian argument x. +// +// Special cases are: +// +// Tan(±0) = ±0 +// Tan(±Inf) = NaN +// Tan(NaN) = NaN +func Tan(x float64) float64 { + return tan(x) +} + +func tan(x float64) float64 { + const ( + PI4A = 7.85398125648498535156e-1 // 0x3fe921fb40000000, Pi/4 split into three parts + PI4B = 3.77489470793079817668e-8 // 0x3e64442d00000000, + PI4C = 2.69515142907905952645e-15 // 0x3ce8469898cc5170, + ) + // special cases + switch { + case x == 0 || IsNaN(x): + return x // return ±0 || NaN() + case IsInf(x, 0): + return NaN() + } + + // make argument positive but save the sign + sign := false + if x < 0 { + x = -x + sign = true + } + var j uint64 + var y, z float64 + if x >= reduceThreshold { + j, z = trigReduce(x) + } else { + j = uint64(x * (4 / Pi)) // integer part of x/(Pi/4), as integer for tests on the phase angle + y = float64(j) // integer part of x/(Pi/4), as float + + /* map zeros and singularities to origin */ + if j&1 == 1 { + j++ + y++ + } + + z = ((x - y*PI4A) - y*PI4B) - y*PI4C + } + zz := z * z + + if zz > 1e-14 { + y = z + z*(zz*(((_tanP[0]*zz)+_tanP[1])*zz+_tanP[2])/((((zz+_tanQ[1])*zz+_tanQ[2])*zz+_tanQ[3])*zz+_tanQ[4])) + } else { + y = z + } + if j&2 == 2 { + y = -1 / y + } + if sign { + y = -y + } + return y +} diff --git a/gnovm/stdlibs/math/tanh.gno b/gnovm/stdlibs/math/tanh.gno new file mode 100644 index 00000000000..4bb9d5d43d1 --- /dev/null +++ b/gnovm/stdlibs/math/tanh.gno @@ -0,0 +1,103 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +// The original C code, the long comment, and the constants +// below were from http://netlib.sandia.gov/cephes/cmath/sin.c, +// available from http://www.netlib.org/cephes/cmath.tgz. +// The go code is a simplified version of the original C. +// tanh.c +// +// Hyperbolic tangent +// +// SYNOPSIS: +// +// double x, y, tanh(); +// +// y = tanh( x ); +// +// DESCRIPTION: +// +// Returns hyperbolic tangent of argument in the range MINLOG to MAXLOG. +// MAXLOG = 8.8029691931113054295988e+01 = log(2**127) +// MINLOG = -8.872283911167299960540e+01 = log(2**-128) +// +// A rational function is used for |x| < 0.625. The form +// x + x**3 P(x)/Q(x) of Cody & Waite is employed. +// Otherwise, +// tanh(x) = sinh(x)/cosh(x) = 1 - 2/(exp(2x) + 1). +// +// ACCURACY: +// +// Relative error: +// arithmetic domain # trials peak rms +// IEEE -2,2 30000 2.5e-16 5.8e-17 +// +// Cephes Math Library Release 2.8: June, 2000 +// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier +// +// The readme file at http://netlib.sandia.gov/cephes/ says: +// Some software in this archive may be from the book _Methods and +// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +// International, 1989) or from the Cephes Mathematical Library, a +// commercial product. In either event, it is copyrighted by the author. +// What you see here may be used freely but it comes with no support or +// guarantee. +// +// The two known misprints in the book are repaired here in the +// source listings for the gamma function and the incomplete beta +// integral. +// +// Stephen L. Moshier +// moshier@na-net.ornl.gov +// + +var tanhP = [...]float64{ + -9.64399179425052238628e-1, + -9.92877231001918586564e1, + -1.61468768441708447952e3, +} + +var tanhQ = [...]float64{ + 1.12811678491632931402e2, + 2.23548839060100448583e3, + 4.84406305325125486048e3, +} + +// Tanh returns the hyperbolic tangent of x. +// +// Special cases are: +// +// Tanh(±0) = ±0 +// Tanh(±Inf) = ±1 +// Tanh(NaN) = NaN +func Tanh(x float64) float64 { + return tanh(x) +} + +func tanh(x float64) float64 { + const MAXLOG = 8.8029691931113054295988e+01 // log(2**127) + z := Abs(x) + switch { + case z > 0.5*MAXLOG: + if x < 0 { + return -1 + } + return 1 + case z >= 0.625: + s := Exp(2 * z) + z = 1 - 2/(s+1) + if x < 0 { + z = -z + } + default: + if x == 0 { + return x + } + s := x * x + z = x + x*s*((tanhP[0]*s+tanhP[1])*s+tanhP[2])/(((s+tanhQ[0])*s+tanhQ[1])*s+tanhQ[2]) + } + return z +} diff --git a/gnovm/stdlibs/math/trig_reduce.gno b/gnovm/stdlibs/math/trig_reduce.gno new file mode 100644 index 00000000000..5ecdd8375e3 --- /dev/null +++ b/gnovm/stdlibs/math/trig_reduce.gno @@ -0,0 +1,102 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package math + +import ( + "math/bits" +) + +// reduceThreshold is the maximum value of x where the reduction using Pi/4 +// in 3 float64 parts still gives accurate results. This threshold +// is set by y*C being representable as a float64 without error +// where y is given by y = floor(x * (4 / Pi)) and C is the leading partial +// terms of 4/Pi. Since the leading terms (PI4A and PI4B in sin.go) have 30 +// and 32 trailing zero bits, y should have less than 30 significant bits. +// +// y < 1<<30 -> floor(x*4/Pi) < 1<<30 -> x < (1<<30 - 1) * Pi/4 +// +// So, conservatively we can take x < 1<<29. +// Above this threshold Payne-Hanek range reduction must be used. +const reduceThreshold = 1 << 29 + +// trigReduce implements Payne-Hanek range reduction by Pi/4 +// for x > 0. It returns the integer part mod 8 (j) and +// the fractional part (z) of x / (Pi/4). +// The implementation is based on: +// "ARGUMENT REDUCTION FOR HUGE ARGUMENTS: Good to the Last Bit" +// K. C. Ng et al, March 24, 1992 +// The simulated multi-precision calculation of x*B uses 64-bit integer arithmetic. +func trigReduce(x float64) (j uint64, z float64) { + const PI4 = Pi / 4 + if x < PI4 { + return 0, x + } + // Extract out the integer and exponent such that, + // x = ix * 2 ** exp. + ix := Float64bits(x) + exp := int(ix>>shift&mask) - bias - shift + ix &^= mask << shift + ix |= 1 << shift + // Use the exponent to extract the 3 appropriate uint64 digits from mPi4, + // B ~ (z0, z1, z2), such that the product leading digit has the exponent -61. + // Note, exp >= -53 since x >= PI4 and exp < 971 for maximum float64. + digit, bitshift := uint(exp+61)/64, uint(exp+61)%64 + z0 := (mPi4[digit] << bitshift) | (mPi4[digit+1] >> (64 - bitshift)) + z1 := (mPi4[digit+1] << bitshift) | (mPi4[digit+2] >> (64 - bitshift)) + z2 := (mPi4[digit+2] << bitshift) | (mPi4[digit+3] >> (64 - bitshift)) + // Multiply mantissa by the digits and extract the upper two digits (hi, lo). + z2hi, _ := bits.Mul64(z2, ix) + z1hi, z1lo := bits.Mul64(z1, ix) + z0lo := z0 * ix + lo, c := bits.Add64(z1lo, z2hi, 0) + hi, _ := bits.Add64(z0lo, z1hi, c) + // The top 3 bits are j. + j = hi >> 61 + // Extract the fraction and find its magnitude. + hi = hi<<3 | lo>>61 + lz := uint(bits.LeadingZeros64(hi)) + e := uint64(bias - (lz + 1)) + // Clear implicit mantissa bit and shift into place. + hi = (hi << (lz + 1)) | (lo >> (64 - (lz + 1))) + hi >>= 64 - shift + // Include the exponent and convert to a float. + hi |= e << shift + z = Float64frombits(hi) + // Map zeros to origin. + if j&1 == 1 { + j++ + j &= 7 + z-- + } + // Multiply the fractional part by pi/4. + return j, z * PI4 +} + +// mPi4 is the binary digits of 4/pi as a uint64 array, +// that is, 4/pi = Sum mPi4[i]*2^(-64*i) +// 19 64-bit digits and the leading one bit give 1217 bits +// of precision to handle the largest possible float64 exponent. +var mPi4 = [...]uint64{ + 0x0000000000000001, + 0x45f306dc9c882a53, + 0xf84eafa3ea69bb81, + 0xb6c52b3278872083, + 0xfca2c757bd778ac3, + 0x6e48dc74849ba5c0, + 0x0c925dd413a32439, + 0xfc3bd63962534e7d, + 0xd1046bea5d768909, + 0xd338e04d68befc82, + 0x7323ac7306a673e9, + 0x3908bf177bf25076, + 0x3ff12fffbc0b301f, + 0xde5e2316b414da3e, + 0xda6cfd9e4f96136e, + 0x9e8c7ecd3cbfd45a, + 0xea4f758fd7cbe2f6, + 0x7a0e73ef14a525d4, + 0xd7f6bf623f1aba10, + 0xac06608df8f6d757, +} diff --git a/gnovm/stdlibs/native.go b/gnovm/stdlibs/native.go new file mode 100644 index 00000000000..4125c56ffe1 --- /dev/null +++ b/gnovm/stdlibs/native.go @@ -0,0 +1,782 @@ +// This file is autogenerated from the genstd tool (@/misc/genstd); do not edit. +// To regenerate it, run `go generate` from this directory. + +package stdlibs + +import ( + "reflect" + + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + libs_crypto_sha256 "github.com/gnolang/gno/gnovm/stdlibs/crypto/sha256" + libs_math "github.com/gnolang/gno/gnovm/stdlibs/math" + libs_std "github.com/gnolang/gno/gnovm/stdlibs/std" + libs_strconv "github.com/gnolang/gno/gnovm/stdlibs/strconv" + libs_testing "github.com/gnolang/gno/gnovm/stdlibs/testing" + libs_time "github.com/gnolang/gno/gnovm/stdlibs/time" + tm2_crypto "github.com/gnolang/gno/tm2/pkg/crypto" +) + +type nativeFunc struct { + gnoPkg string + gnoFunc gno.Name + params []gno.FieldTypeExpr + results []gno.FieldTypeExpr + f func(m *gno.Machine) +} + +var nativeFuncs = [...]nativeFunc{ + { + "crypto/sha256", + "sum256", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("[]byte")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("[32]byte")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 []byte + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_crypto_sha256.X_sum256(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "math", + "Float32bits", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("float32")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("uint32")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 float32 + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_math.Float32bits(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "math", + "Float32frombits", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("uint32")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("float32")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 uint32 + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_math.Float32frombits(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "math", + "Float64bits", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("float64")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("uint64")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 float64 + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_math.Float64bits(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "math", + "Float64frombits", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("uint64")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("float64")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 uint64 + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_math.Float64frombits(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "AssertOriginCall", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{}, + func(m *gno.Machine) { + libs_std.AssertOriginCall( + m, + ) + }, + }, + { + "std", + "IsOriginCall", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("bool")}, + }, + func(m *gno.Machine) { + r0 := libs_std.IsOriginCall( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "CurrentRealmPath", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + r0 := libs_std.CurrentRealmPath( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "GetChainID", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + r0 := libs_std.GetChainID( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "GetHeight", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("int64")}, + }, + func(m *gno.Machine) { + r0 := libs_std.GetHeight( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "GetOrigSend", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Coins")}, + }, + func(m *gno.Machine) { + r0 := libs_std.GetOrigSend( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "GetOrigCaller", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Address")}, + }, + func(m *gno.Machine) { + r0 := libs_std.GetOrigCaller( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "CurrentRealm", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Realm")}, + }, + func(m *gno.Machine) { + r0 := libs_std.CurrentRealm( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "PrevRealm", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Realm")}, + }, + func(m *gno.Machine) { + r0 := libs_std.PrevRealm( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "GetOrigPkgAddr", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Address")}, + }, + func(m *gno.Machine) { + r0 := libs_std.GetOrigPkgAddr( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "GetCallerAt", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("int")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Address")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 int + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_std.GetCallerAt( + m, + p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "GetBanker", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("BankerType")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Banker")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 libs_std.BankerType + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_std.GetBanker( + m, + p0) + + m.PushValue(r0) + }, + }, + { + "std", + "DerivePkgAddr", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Address")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_std.DerivePkgAddr(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "EncodeBech32", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + {Name: gno.N("p1"), Type: gno.X("[20]byte")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Address")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + p1 [20]byte + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + r0 := libs_std.EncodeBech32(p0, p1) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "DecodeBech32", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("Address")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + {Name: gno.N("r1"), Type: gno.X("[20]byte")}, + {Name: gno.N("r2"), Type: gno.X("bool")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 tm2_crypto.Bech32Address + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0, r1, r2 := libs_std.DecodeBech32(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r1).Elem(), + )) + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r2).Elem(), + )) + }, + }, + { + "strconv", + "Itoa", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("int")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 int + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_strconv.Itoa(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "strconv", + "AppendUint", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("[]byte")}, + {Name: gno.N("p1"), Type: gno.X("uint64")}, + {Name: gno.N("p2"), Type: gno.X("int")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("[]byte")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 []byte + rp0 = reflect.ValueOf(&p0).Elem() + p1 uint64 + rp1 = reflect.ValueOf(&p1).Elem() + p2 int + rp2 = reflect.ValueOf(&p2).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 2, "")).TV, rp2) + + r0 := libs_strconv.AppendUint(p0, p1, p2) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "strconv", + "Atoi", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("int")}, + {Name: gno.N("r1"), Type: gno.X("error")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0, r1 := libs_strconv.Atoi(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r1).Elem(), + )) + }, + }, + { + "strconv", + "CanBackquote", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("bool")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_strconv.CanBackquote(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "strconv", + "FormatInt", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("int64")}, + {Name: gno.N("p1"), Type: gno.X("int")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 int64 + rp0 = reflect.ValueOf(&p0).Elem() + p1 int + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + r0 := libs_strconv.FormatInt(p0, p1) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "strconv", + "FormatUint", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("uint64")}, + {Name: gno.N("p1"), Type: gno.X("int")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 uint64 + rp0 = reflect.ValueOf(&p0).Elem() + p1 int + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + r0 := libs_strconv.FormatUint(p0, p1) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "strconv", + "Quote", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_strconv.Quote(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "strconv", + "QuoteToASCII", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("string")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 string + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := libs_strconv.QuoteToASCII(p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "testing", + "unixNano", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("int64")}, + }, + func(m *gno.Machine) { + r0 := libs_testing.X_unixNano() + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "time", + "now", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("int64")}, + {Name: gno.N("r1"), Type: gno.X("int32")}, + {Name: gno.N("r2"), Type: gno.X("int64")}, + }, + func(m *gno.Machine) { + r0, r1, r2 := libs_time.X_now( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r1).Elem(), + )) + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r2).Elem(), + )) + }, + }, +} diff --git a/gnovm/stdlibs/banker.go b/gnovm/stdlibs/std/banker.go similarity index 97% rename from gnovm/stdlibs/banker.go rename to gnovm/stdlibs/std/banker.go index 82bf1bad42a..7653f2a519f 100644 --- a/gnovm/stdlibs/banker.go +++ b/gnovm/stdlibs/std/banker.go @@ -1,4 +1,4 @@ -package stdlibs +package std import ( "fmt" @@ -21,10 +21,10 @@ type Banker interface { } // Used in std.GetBanker(options). -// Also available as Gno in stdlibs/std/banker.go +// Also available as Gno in stdlibs/std/banker.gno type BankerType uint8 -// Also available as Gno in stdlibs/std/banker.go +// Also available as Gno in stdlibs/std/banker.gno const ( // Can only read state. BankerTypeReadonly BankerType = iota diff --git a/gnovm/stdlibs/context.go b/gnovm/stdlibs/std/context.go similarity index 96% rename from gnovm/stdlibs/context.go rename to gnovm/stdlibs/std/context.go index 5f140c344d4..c50e2e5e1b9 100644 --- a/gnovm/stdlibs/context.go +++ b/gnovm/stdlibs/std/context.go @@ -1,4 +1,4 @@ -package stdlibs +package std import ( "github.com/gnolang/gno/tm2/pkg/crypto" diff --git a/gnovm/stdlibs/std/crypto.gno b/gnovm/stdlibs/std/crypto.gno index 3ebd802dc3f..8d005dccf5c 100644 --- a/gnovm/stdlibs/std/crypto.gno +++ b/gnovm/stdlibs/std/crypto.gno @@ -6,6 +6,11 @@ func (a Address) String() string { return string(a) } +// IsValid checks if the address is of specific length. Doesn't check prefix or checksum for the address +func (a Address) IsValid() bool { + return len(a) == RawAddressSize*2 // hex length +} + const RawAddressSize = 20 type RawAddress [RawAddressSize]byte diff --git a/gnovm/stdlibs/std/crypto_test.gno b/gnovm/stdlibs/std/crypto_test.gno new file mode 100644 index 00000000000..293f3e06945 --- /dev/null +++ b/gnovm/stdlibs/std/crypto_test.gno @@ -0,0 +1,30 @@ +package std + +import ( + "testing" +) + +func TestValid(t *testing.T) { + type test struct { + inputAddress Address + expected bool + } + + testCases := []test{ + {inputAddress: "g1f4v282mwyhu29afke4vq5r2xzcm6z3ftnugcnv", expected: true}, + {inputAddress: "g127jydsh6cms3lrtdenydxsckh23a8d6emqcvfa", expected: true}, + {inputAddress: "g1u7y667z64x2h7vc6fmpcprgey4ck233jaww9zq", expected: true}, + {inputAddress: "g14da4n9hcynyzz83q607uu8keuh9hwlv42ra6fa", expected: true}, + {inputAddress: "", expected: false}, + {inputAddress: "000000000000", expected: false}, + {inputAddress: "0000000000000000000000000000000000000000000000000000000000000000000000", expected: false}, + } + + for _, tc := range testCases { + result := tc.inputAddress.IsValid() + + if result != tc.expected { + t.Fatalf("Expected: %t, got: %t", tc.expected, result) + } + } +} diff --git a/gnovm/stdlibs/frame.go b/gnovm/stdlibs/std/frame.go similarity index 94% rename from gnovm/stdlibs/frame.go rename to gnovm/stdlibs/std/frame.go index e428bb1776d..2948813ad0f 100644 --- a/gnovm/stdlibs/frame.go +++ b/gnovm/stdlibs/std/frame.go @@ -1,4 +1,4 @@ -package stdlibs +package std import "github.com/gnolang/gno/tm2/pkg/crypto" diff --git a/gnovm/stdlibs/std/native.gno b/gnovm/stdlibs/std/native.gno new file mode 100644 index 00000000000..2f7da810bcb --- /dev/null +++ b/gnovm/stdlibs/std/native.gno @@ -0,0 +1,18 @@ +package std + +func AssertOriginCall() // injected +func IsOriginCall() bool // injected +func CurrentRealmPath() string // injected +func GetChainID() string // injected +func GetHeight() int64 // injected +func GetOrigSend() Coins // injected +func GetOrigCaller() Address // injected +func CurrentRealm() Realm // injected +func PrevRealm() Realm // injected +func GetOrigPkgAddr() Address // injected +func GetCallerAt(n int) Address // injected +func GetBanker(bt BankerType) Banker // injected +func DerivePkgAddr(pkgPath string) Address // injected + +func EncodeBech32(prefix string, bz [20]byte) Address // injected +func DecodeBech32(addr Address) (prefix string, bz [20]byte, ok bool) // injected diff --git a/gnovm/stdlibs/std/native.go b/gnovm/stdlibs/std/native.go new file mode 100644 index 00000000000..8cdddd916ad --- /dev/null +++ b/gnovm/stdlibs/std/native.go @@ -0,0 +1,185 @@ +package std + +import ( + "reflect" + + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/tm2/pkg/bech32" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/std" +) + +func AssertOriginCall(m *gno.Machine) { + if !IsOriginCall(m) { + m.Panic(typedString("invalid non-origin call")) + } +} + +func IsOriginCall(m *gno.Machine) bool { + return len(m.Frames) == 2 +} + +func CurrentRealmPath(m *gno.Machine) string { + if m.Realm != nil { + return m.Realm.Path + } + return "" +} + +func GetChainID(m *gno.Machine) string { + return m.Context.(ExecContext).ChainID +} + +func GetHeight(m *gno.Machine) int64 { + return m.Context.(ExecContext).Height +} + +func GetOrigSend(m *gno.Machine) std.Coins { + return m.Context.(ExecContext).OrigSend +} + +func GetOrigCaller(m *gno.Machine) crypto.Bech32Address { + return m.Context.(ExecContext).OrigCaller +} + +func CurrentRealm(m *gno.Machine) Realm { + var ( + ctx = m.Context.(ExecContext) + // Default lastCaller is OrigCaller, the signer of the tx + lastCaller = ctx.OrigCaller + lastPkgPath = "" + ) + + for i := m.NumFrames() - 1; i > 0; i-- { + fr := m.Frames[i] + if fr.LastPackage != nil && fr.LastPackage.IsRealm() { + lastCaller = fr.LastPackage.GetPkgAddr().Bech32() + lastPkgPath = fr.LastPackage.PkgPath + break + } + } + + return Realm{ + addr: lastCaller, + pkgPath: lastPkgPath, + } +} + +func PrevRealm(m *gno.Machine) Realm { + var ( + ctx = m.Context.(ExecContext) + // Default lastCaller is OrigCaller, the signer of the tx + lastCaller = ctx.OrigCaller + lastPkgPath = "" + ) + + for i := m.NumFrames() - 1; i > 0; i-- { + fr := m.Frames[i] + if fr.LastPackage == nil || !fr.LastPackage.IsRealm() { + // Ignore non-realm frame + continue + } + pkgPath := fr.LastPackage.PkgPath + // The first realm we encounter will be the one calling + // this function; to get the calling realm determine the first frame + // where fr.LastPackage changes. + if lastPkgPath == "" { + lastPkgPath = pkgPath + } else if lastPkgPath == pkgPath { + continue + } else { + lastCaller = fr.LastPackage.GetPkgAddr().Bech32() + lastPkgPath = pkgPath + break + } + } + + // Empty the pkgPath if we return a user + if ctx.OrigCaller == lastCaller { + lastPkgPath = "" + } + + return Realm{ + addr: lastCaller, + pkgPath: lastPkgPath, + } +} + +func GetOrigPkgAddr(m *gno.Machine) crypto.Bech32Address { + return m.Context.(ExecContext).OrigPkgAddr +} + +func GetCallerAt(m *gno.Machine, n int) crypto.Bech32Address { + if n <= 0 { + m.Panic(typedString("GetCallerAt requires positive arg")) + return "" + } + if n > m.NumFrames() { + // NOTE: the last frame's LastPackage + // is set to the original non-frame + // package, so need this check. + m.Panic(typedString("frame not found")) + return "" + } + if n == m.NumFrames() { + // This makes it consistent with GetOrigCaller. + ctx := m.Context.(ExecContext) + return ctx.OrigCaller + } + return m.LastCallFrame(n).LastPackage.GetPkgAddr().Bech32() +} + +func GetBanker(m *gno.Machine, bankerType BankerType) gno.TypedValue { + ctx := m.Context.(ExecContext) + banker := ctx.Banker + switch bankerType { + case BankerTypeReadonly: + banker = NewReadonlyBanker(banker) + case BankerTypeOrigSend: + banker = NewOrigSendBanker(banker, ctx.OrigPkgAddr, ctx.OrigSend, ctx.OrigSendSpent) + case BankerTypeRealmSend: + banker = NewRealmSendBanker(banker, ctx.OrigPkgAddr) + case BankerTypeRealmIssue: + banker = banker + default: + panic("should not happen") // defensive + } + m.Alloc.AllocateStruct() // defensive; native space not allocated. + m.Alloc.AllocateStructFields(10) // defensive 10; native space not allocated. + + // make gno bankAdapter{rv} + btv := gno.Go2GnoNativeValue(m.Alloc, reflect.ValueOf(banker)) + bsv := m.Alloc.NewStructWithFields(btv) + bankAdapterType := m.Store.GetType(gno.DeclaredTypeID("std", "bankAdapter")) + res0 := gno.TypedValue{T: bankAdapterType, V: bsv} + + return res0 +} + +func EncodeBech32(prefix string, bytes [20]byte) crypto.Bech32Address { + b32, err := bech32.ConvertAndEncode(prefix, bytes[:]) + if err != nil { + panic(err) // should not happen + } + return crypto.Bech32Address(b32) +} + +func DerivePkgAddr(pkgPath string) crypto.Bech32Address { + return gno.DerivePkgAddr(pkgPath).Bech32() +} + +func DecodeBech32(addr crypto.Bech32Address) (prefix string, bytes [20]byte, ok bool) { + prefix, bz, err := bech32.Decode(string(addr)) + if err != nil || len(bz) != 20 { + return "", [20]byte{}, false + } + // TODO: can be simplified when we switch to go1.20 in go mod to be a simple [20]byte(bz) + copy(bytes[:], bz) + return prefix, bytes, true +} + +func typedString(s gno.StringValue) gno.TypedValue { + tv := gno.TypedValue{T: gno.StringType} + tv.SetString(s) + return tv +} diff --git a/gnovm/stdlibs/stdlibs.go b/gnovm/stdlibs/stdlibs.go index fb230a0cf86..22054613c03 100644 --- a/gnovm/stdlibs/stdlibs.go +++ b/gnovm/stdlibs/stdlibs.go @@ -1,593 +1,30 @@ package stdlibs +//go:generate go run github.com/gnolang/gno/misc/genstd + import ( - "crypto/sha256" - "math" "reflect" - "strconv" - "time" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/tm2/pkg/bech32" + libsstd "github.com/gnolang/gno/gnovm/stdlibs/std" "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/std" ) +type ExecContext = libsstd.ExecContext + func InjectNativeMappings(store gno.Store) { store.AddGo2GnoMapping(reflect.TypeOf(crypto.Bech32Address("")), "std", "Address") store.AddGo2GnoMapping(reflect.TypeOf(std.Coins{}), "std", "Coins") store.AddGo2GnoMapping(reflect.TypeOf(std.Coin{}), "std", "Coin") - store.AddGo2GnoMapping(reflect.TypeOf(Realm{}), "std", "Realm") + store.AddGo2GnoMapping(reflect.TypeOf(libsstd.Realm{}), "std", "Realm") } -func InjectPackage(store gno.Store, pn *gno.PackageNode) { - switch pn.PkgPath { - case "internal/crypto/sha256": - pn.DefineNative("Sum256", - gno.Flds( // params - "data", "[]byte", - ), - gno.Flds( // results - "bz", "[32]byte", - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - bz := []byte(nil) - - if arg0.V != nil { - slice := arg0.V.(*gno.SliceValue) - array := slice.GetBase(m.Store) - bz = array.GetReadonlyBytes()[:slice.Length] - } - - hash := sha256.Sum256(bz) - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(hash), - ) - m.PushValue(res0) - }, - ) - case "internal/math": - pn.DefineNative("Float32bits", - gno.Flds( // params - "f", "float32", - ), - gno.Flds( // results - "b", "uint32", - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - res0 := typedUint32(math.Float32bits(arg0.GetFloat32())) - m.PushValue(res0) - }, - ) - pn.DefineNative("Float32frombits", - gno.Flds( // params - "b", "uint32", - ), - gno.Flds( // results - "f", "float32", - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - res0 := typedFloat32(math.Float32frombits(arg0.GetUint32())) - m.PushValue(res0) - }, - ) - pn.DefineNative("Float64bits", - gno.Flds( // params - "f", "float64", - ), - gno.Flds( // results - "b", "uint64", - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - res0 := typedUint64(math.Float64bits(arg0.GetFloat64())) - m.PushValue(res0) - }, - ) - pn.DefineNative("Float64frombits", - gno.Flds( // params - "b", "uint64", - ), - gno.Flds( // results - "f", "float64", - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - res0 := typedFloat64(math.Float64frombits(arg0.GetUint64())) - m.PushValue(res0) - }, - ) - case "internal/os": - pn.DefineNative("Now", - gno.Flds( // params - ), - gno.Flds( // results - "sec", "int64", - "nsec", "int32", - "mono", "int64", - ), - func(m *gno.Machine) { - if m.Context == nil { - res0 := typedInt64(0) - res1 := typedInt32(0) - res2 := typedInt64(0) - m.PushValue(res0) - m.PushValue(res1) - m.PushValue(res2) - } else { - ctx := m.Context.(ExecContext) - res0 := typedInt64(ctx.Timestamp) - res1 := typedInt32(int32(ctx.TimestampNano)) - res2 := typedInt64(ctx.Timestamp*int64(time.Second) + ctx.TimestampNano) - m.PushValue(res0) - m.PushValue(res1) - m.PushValue(res2) - } - }, - ) - // case "internal/os_test": - // XXX defined in tests/imports.go - case "strconv": - pn.DefineGoNativeValue("Itoa", strconv.Itoa) - pn.DefineGoNativeValue("Atoi", strconv.Atoi) - pn.DefineGoNativeValue("FormatInt", strconv.FormatInt) - pn.DefineGoNativeValue("FormatUint", strconv.FormatUint) - pn.DefineGoNativeValue("Quote", strconv.Quote) - pn.DefineGoNativeValue("QuoteToASCII", strconv.QuoteToASCII) - pn.DefineGoNativeValue("CanBackquote", strconv.CanBackquote) - pn.DefineGoNativeValue("IntSize", strconv.IntSize) - pn.DefineGoNativeValue("AppendUint", strconv.AppendUint) - case "std": - // NOTE: some of these are overridden in tests/imports.go - // Also see stdlibs/InjectPackage. - pn.DefineNative("AssertOriginCall", - gno.Flds( // params - ), - gno.Flds( // results - ), - func(m *gno.Machine) { - isOrigin := len(m.Frames) == 2 - if !isOrigin { - m.Panic(typedString("invalid non-origin call")) - return - } - }, - ) - pn.DefineNative("IsOriginCall", - gno.Flds( // params - ), - gno.Flds( // results - "isOrigin", "bool", - ), - func(m *gno.Machine) { - isOrigin := len(m.Frames) == 2 - res0 := gno.TypedValue{T: gno.BoolType} - res0.SetBool(isOrigin) - m.PushValue(res0) - }, - ) - pn.DefineNative("CurrentRealmPath", - gno.Flds( // params - ), - gno.Flds( // results - "", "string", - ), - func(m *gno.Machine) { - realmPath := "" - if m.Realm != nil { - realmPath = m.Realm.Path - } - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(realmPath), - ) - m.PushValue(res0) - }, - ) - pn.DefineNative("GetChainID", - gno.Flds( // params - ), - gno.Flds( // results - "", "string", - ), - func(m *gno.Machine) { - ctx := m.Context.(ExecContext) - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(ctx.ChainID), - ) - m.PushValue(res0) - }, - ) - pn.DefineNative("GetHeight", - gno.Flds( // params - ), - gno.Flds( // results - "", "int64", - ), - func(m *gno.Machine) { - ctx := m.Context.(ExecContext) - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(ctx.Height), - ) - m.PushValue(res0) - }, - ) - pn.DefineNative("GetOrigSend", - gno.Flds( // params - ), - gno.Flds( // results - "", "Coins", - ), - func(m *gno.Machine) { - ctx := m.Context.(ExecContext) - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(ctx.OrigSend), - ) - coinT := store.GetType(gno.DeclaredTypeID("std", "Coin")) - coinsT := store.GetType(gno.DeclaredTypeID("std", "Coins")) - res0.T = coinsT - av := res0.V.(*gno.SliceValue).Base.(*gno.ArrayValue) - for i := range av.List { - av.List[i].T = coinT - } - m.PushValue(res0) - }, - ) - pn.DefineNative("GetOrigCaller", - gno.Flds( // params - ), - gno.Flds( // results - "", "Address", - ), - func(m *gno.Machine) { - ctx := m.Context.(ExecContext) - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(ctx.OrigCaller), - ) - addrT := store.GetType(gno.DeclaredTypeID("std", "Address")) - res0.T = addrT - m.PushValue(res0) - }, - ) - pn.DefineNative("CurrentRealm", - gno.Flds( // params - ), - gno.Flds( // results - "", "Realm", - ), - func(m *gno.Machine) { - var ( - ctx = m.Context.(ExecContext) - // Default lastCaller is OrigCaller, the signer of the tx - lastCaller = ctx.OrigCaller - lastPkgPath = "" - ) - - for i := m.NumFrames() - 1; i > 0; i-- { - fr := m.Frames[i] - if fr.LastPackage != nil && fr.LastPackage.IsRealm() { - lastCaller = fr.LastPackage.GetPkgAddr().Bech32() - lastPkgPath = fr.LastPackage.PkgPath - break - } - } - - // Return the result - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(Realm{ - addr: lastCaller, - pkgPath: lastPkgPath, - }), - ) - - realmT := store.GetType(gno.DeclaredTypeID("std", "Realm")) - res0.T = realmT - m.PushValue(res0) - }, - ) - pn.DefineNative("PrevRealm", - gno.Flds( // params - ), - gno.Flds( // results - "", "Realm", - ), - func(m *gno.Machine) { - var ( - ctx = m.Context.(ExecContext) - // Default lastCaller is OrigCaller, the signer of the tx - lastCaller = ctx.OrigCaller - lastPkgPath = "" - ) - - for i := m.NumFrames() - 1; i > 0; i-- { - fr := m.Frames[i] - if fr.LastPackage == nil || !fr.LastPackage.IsRealm() { - // Ignore non-realm frame - continue - } - pkgPath := fr.LastPackage.PkgPath - // The first realm we encounter will be the one calling - // this function; to get the calling realm determine the first frame - // where fr.LastPackage changes. - if lastPkgPath == "" { - lastPkgPath = pkgPath - } else if lastPkgPath == pkgPath { - continue - } else { - lastCaller = fr.LastPackage.GetPkgAddr().Bech32() - lastPkgPath = pkgPath - break - } - } - - // Empty the pkgPath if we return a user - if ctx.OrigCaller == lastCaller { - lastPkgPath = "" - } - - // Return the result - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(Realm{ - addr: lastCaller, - pkgPath: lastPkgPath, - }), - ) - - realmT := store.GetType(gno.DeclaredTypeID("std", "Realm")) - res0.T = realmT - m.PushValue(res0) - }, - ) - pn.DefineNative("GetOrigPkgAddr", - gno.Flds( // params - ), - gno.Flds( // results - "", "Address", - ), - func(m *gno.Machine) { - ctx := m.Context.(ExecContext) - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(ctx.OrigPkgAddr), - ) - addrT := store.GetType(gno.DeclaredTypeID("std", "Address")) - res0.T = addrT - m.PushValue(res0) - }, - ) - pn.DefineNative("GetCallerAt", - gno.Flds( // params - "n", "int", - ), - gno.Flds( // results - "", "Address", - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - n := arg0.GetInt() - if n <= 0 { - m.Panic(typedString("GetCallerAt requires positive arg")) - return - } - if n > m.NumFrames() { - // NOTE: the last frame's LastPackage - // is set to the original non-frame - // package, so need this check. - m.Panic(typedString("frame not found")) - return - } - var pkgAddr string - if n == m.NumFrames() { - // This makes it consistent with GetOrigCaller. - ctx := m.Context.(ExecContext) - pkgAddr = string(ctx.OrigCaller) - } else { - pkgAddr = string(m.LastCallFrame(n).LastPackage.GetPkgAddr().Bech32()) - } - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(pkgAddr), - ) - addrT := store.GetType(gno.DeclaredTypeID("std", "Address")) - res0.T = addrT - m.PushValue(res0) - }, - ) - pn.DefineNative("GetBanker", - gno.Flds( // params - "bankerType", "BankerType", - ), - gno.Flds( // results - "", "Banker", - ), - func(m *gno.Machine) { - ctx := m.Context.(ExecContext) - arg0 := m.LastBlock().GetParams1().TV - bankerType := BankerType(arg0.GetUint8()) - banker := ctx.Banker - switch bankerType { - case BankerTypeReadonly: - banker = NewReadonlyBanker(banker) - case BankerTypeOrigSend: - banker = NewOrigSendBanker(banker, ctx.OrigPkgAddr, ctx.OrigSend, ctx.OrigSendSpent) - case BankerTypeRealmSend: - banker = NewRealmSendBanker(banker, ctx.OrigPkgAddr) - case BankerTypeRealmIssue: - banker = banker - default: - panic("should not happen") // defensive - } - rv := reflect.ValueOf(banker) - m.Alloc.AllocateStruct() // defensive; native space not allocated. - m.Alloc.AllocateStructFields(10) // defensive 10; native space not allocated. - - // make gno bankAdapter{rv} - btv := gno.Go2GnoNativeValue(m.Alloc, rv) - bsv := m.Alloc.NewStructWithFields(btv) - bankAdapterType := store.GetType(gno.DeclaredTypeID("std", "bankAdapter")) - res0 := gno.TypedValue{T: bankAdapterType, V: bsv} - m.PushValue(res0) - }, - ) - pn.DefineNative("EncodeBech32", - gno.Flds( // params - "prefix", "string", - "bytes", "[20]byte", - ), - gno.Flds( // results - "addr", "Address", - ), - func(m *gno.Machine) { - arg0, arg1 := m.LastBlock().GetParams2() - prefix := arg0.TV.GetString() - bz := arg1.TV.V.(*gno.ArrayValue).GetReadonlyBytes() - if len(bz) != crypto.AddressSize { - panic("should not happen") - } - b32, err := bech32.ConvertAndEncode(prefix, bz) - if err != nil { - panic(err) // should not happen - } - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(b32), - ) - addrT := store.GetType(gno.DeclaredTypeID("std", "Address")) - res0.T = addrT - m.PushValue(res0) - }, - ) - pn.DefineNative("DecodeBech32", - gno.Flds( // params - "addr", "Address", - ), - gno.Flds( // results - "prefix", "string", - "bytes", "[20]byte", - "ok", "bool", - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1() - addr := arg0.TV.GetString() - prefix, bz, err := bech32.Decode(addr) - if err != nil || len(bz) != 20 { - m.PushValue(typedString(m.Alloc.NewString(""))) - m.PushValue(typedByteArray(20, m.Alloc.NewDataArray(20))) - m.PushValue(typedBool(false)) - } else { - m.PushValue(typedString(m.Alloc.NewString(prefix))) - m.PushValue(typedByteArray(20, m.Alloc.NewArrayFromData(bz))) - m.PushValue(typedBool(true)) - } - }, - ) - pn.DefineNative("DerivePkgAddr", - gno.Flds( // params - "pkgPath", "string", - ), - gno.Flds( // results - "addr", "Address", - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - pkgPath := arg0.GetString() - pkgAddr := gno.DerivePkgAddr(pkgPath).Bech32() - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(pkgAddr), - ) - addrT := store.GetType(gno.DeclaredTypeID("std", "Address")) - res0.T = addrT - m.PushValue(res0) - }, - ) +func NativeStore(pkgPath string, name gno.Name) func(*gno.Machine) { + for _, nf := range nativeFuncs { + if nf.gnoPkg == pkgPath && name == nf.gnoFunc { + return nf.f + } } -} - -func typedInt32(i32 int32) gno.TypedValue { - tv := gno.TypedValue{T: gno.Int32Type} - tv.SetInt32(i32) - return tv -} - -func typedInt64(i64 int64) gno.TypedValue { - tv := gno.TypedValue{T: gno.Int64Type} - tv.SetInt64(i64) - return tv -} - -func typedUint32(u32 uint32) gno.TypedValue { - tv := gno.TypedValue{T: gno.Uint32Type} - tv.SetUint32(u32) - return tv -} - -func typedUint64(u64 uint64) gno.TypedValue { - tv := gno.TypedValue{T: gno.Uint64Type} - tv.SetUint64(u64) - return tv -} - -func typedFloat32(f32 float32) gno.TypedValue { - tv := gno.TypedValue{T: gno.Float32Type} - tv.SetFloat32(f32) - return tv -} - -func typedFloat64(f64 float64) gno.TypedValue { - tv := gno.TypedValue{T: gno.Float64Type} - tv.SetFloat64(f64) - return tv -} - -func typedString(s gno.StringValue) gno.TypedValue { - tv := gno.TypedValue{T: gno.StringType} - tv.SetString(s) - return tv -} - -func typedBool(b bool) gno.TypedValue { - tv := gno.TypedValue{T: gno.BoolType} - tv.SetBool(b) - return tv -} - -func typedByteArray(ln int, bz *gno.ArrayValue) gno.TypedValue { - if bz != nil && bz.GetLength() != ln { - panic("array length mismatch") - } - tv := gno.TypedValue{T: &gno.ArrayType{Len: ln, Elt: gno.Uint8Type}, V: bz} - return tv -} - -func typedByteSlice(bz *gno.SliceValue) gno.TypedValue { - tv := gno.TypedValue{T: &gno.SliceType{Elt: gno.Uint8Type}, V: bz} - return tv -} - -func typedNil(t gno.Type) gno.TypedValue { - tv := gno.TypedValue{T: t, V: nil} - return tv + return nil } diff --git a/gnovm/stdlibs/stdshim/crypto.gno b/gnovm/stdlibs/stdshim/crypto.gno index 3ebd802dc3f..8d005dccf5c 100644 --- a/gnovm/stdlibs/stdshim/crypto.gno +++ b/gnovm/stdlibs/stdshim/crypto.gno @@ -6,6 +6,11 @@ func (a Address) String() string { return string(a) } +// IsValid checks if the address is of specific length. Doesn't check prefix or checksum for the address +func (a Address) IsValid() bool { + return len(a) == RawAddressSize*2 // hex length +} + const RawAddressSize = 20 type RawAddress [RawAddressSize]byte diff --git a/gnovm/stdlibs/strconv/strconv.gno b/gnovm/stdlibs/strconv/strconv.gno index dce62b890e3..bc7b5d8d1b6 100644 --- a/gnovm/stdlibs/strconv/strconv.gno +++ b/gnovm/stdlibs/strconv/strconv.gno @@ -1,4 +1,10 @@ package strconv -// NOTE: currently these are implemented as native functions. -// See InjectNatives(). +func Itoa(n int) string // injected +func AppendUint(dst []byte, i uint64, base int) []byte // injected +func Atoi(s string) (int, error) // injected +func CanBackquote(s string) bool // injected +func FormatInt(i int64, base int) string // injected +func FormatUint(i uint64, base int) string // injected +func Quote(s string) string // injected +func QuoteToASCII(s string) string // injected diff --git a/gnovm/stdlibs/strconv/strconv.go b/gnovm/stdlibs/strconv/strconv.go new file mode 100644 index 00000000000..782a63e84b6 --- /dev/null +++ b/gnovm/stdlibs/strconv/strconv.go @@ -0,0 +1,12 @@ +package strconv + +import "strconv" + +func Itoa(n int) string { return strconv.Itoa(n) } +func AppendUint(dst []byte, i uint64, base int) []byte { return strconv.AppendUint(dst, i, base) } +func Atoi(s string) (int, error) { return strconv.Atoi(s) } +func CanBackquote(s string) bool { return strconv.CanBackquote(s) } +func FormatInt(i int64, base int) string { return strconv.FormatInt(i, base) } +func FormatUint(i uint64, base int) string { return strconv.FormatUint(i, base) } +func Quote(s string) string { return strconv.Quote(s) } +func QuoteToASCII(r string) string { return strconv.QuoteToASCII(r) } diff --git a/gnovm/stdlibs/testing/testing.gno b/gnovm/stdlibs/testing/testing.gno index 0f087d639f9..36e8e7a6955 100644 --- a/gnovm/stdlibs/testing/testing.gno +++ b/gnovm/stdlibs/testing/testing.gno @@ -4,13 +4,40 @@ package testing import ( "encoding/json" "fmt" + "os" "regexp" + "strconv" "strings" ) //---------------------------------------- // Top level functions +// skipErr is the type of the panic created by SkipNow +// and FailNow. Having it as a simple string means that it can be fmt.Printf'd +// easily (and doesn't get "corrupted" through gno2go). +type skipErr string + +func (s skipErr) Error() string { + return string(s) +} + +// Recover functions like recover(), but it ensures that the recovered error is +// not an internal error of the testing package. +// +// Due to a lack of goroutines and thus runtime.Goexit, gno's testing system resorts +// to panics to abort testing with FailNow (and Fatal* functions) or SkipNow +// (and Skip* functions). +// +// NOTE: Recover() is likely to be removed. +func Recover() interface{} { + r := recover() + if _, ok := r.(skipErr); ok { + panic(r) + } + return r +} + func Short() bool { return true // TODO configure somehow. } @@ -34,14 +61,13 @@ func AllocsPerRun2(runs int, f func()) (total int) { type T struct { name string failed bool - finished bool skipped bool - filtered bool subs []*T parent *T output []byte // Output generated by test verbose bool runFilter filterMatch + dur string } func NewT(name string) *T { @@ -65,15 +91,12 @@ func (t *T) Errorf(format string, args ...interface{}) { func (t *T) Fail() { t.failed = true - if t.parent != nil { - fmt.Printf("--- FAIL %s\n", t.name) - } } func (t *T) FailNow() { - // TODO: maybe use panic to control flow. because can't use goroutine in gno now. t.Fail() - t.finished = true + panic(skipErr("testing: you have recovered a panic attempting to interrupt a test, as a consequence of FailNow. " + + "Use testing.Recover to recover panics within tests")) } func (t *T) Failed() bool { @@ -88,6 +111,19 @@ func (t *T) Failed() bool { return false } +// only called when verbose == false +func (t *T) printFailure() { + fmt.Fprintf(os.Stderr, "--- FAIL: %s (%s)\n", t.name, t.dur) + if t.failed { + fmt.Fprint(os.Stderr, string(t.output)) + } + for _, sub := range t.subs { + if sub.Failed() { + sub.printFailure() + } + } +} + func (t *T) Fatal(args ...interface{}) { t.Log(args...) t.FailNow() @@ -119,19 +155,15 @@ func (t *T) Run(name string, f testingFunc) bool { fullName := t.name + "/" + rewrite(name) subT := &T{ - // parent: t, + parent: t, name: fullName, verbose: t.verbose, runFilter: t.runFilter, - parent: t, } - if t.subs == nil { - t.subs = make([]*T, 0) - } t.subs = append(t.subs, subT) - tRunner(subT, f, true) + tRunner(subT, f, t.verbose) return true } @@ -145,7 +177,9 @@ func (t *T) Skip(args ...interface{}) { } func (t *T) SkipNow() { - panic("not yet implemented") + t.skipped = true + panic(skipErr("testing: you have recovered a panic attempting to interrupt a test, as a consequence of SkipNow. " + + "Use testing.Recover to recover panics within tests")) } func (t *T) Skipped() bool { @@ -165,42 +199,27 @@ func (t *T) Helper() { } func (t *T) log(s string) { - t.output = append(t.output, s...) + if t.verbose { + // verbose, print immediately + fmt.Fprint(os.Stderr, s) + } else { + // defer printing only if test is failed + t.output = append(t.output, s...) + } } type Report struct { - Name string - Verbose bool - Failed bool - Skipped bool - Filtered bool - Output string - HasSubs bool + Failed bool + Skipped bool } func (t *T) report() Report { return Report{ - Name: t.name, - Verbose: t.verbose, - Failed: t.Failed(), - Skipped: t.Skipped(), - Filtered: t.filtered, - Output: strings.TrimSpace(t.Output()), - HasSubs: len(t.subs) > 0, + Failed: t.Failed(), + Skipped: t.skipped, } } -func (t *T) Output() string { - output := strings.TrimSpace(string(t.output)) - for _, sub := range t.subs { - subOutput := sub.Output() - if subOutput != "" { - output += "\n\n" + subOutput - } - } - return strings.TrimSpace(output) -} - //---------------------------------------- // B // TODO: actually implement @@ -271,35 +290,68 @@ func RunTest(runFlag string, verbose bool, test InternalTest) (ret string) { t.runFilter = splitRegexp(runFlag) } - tRunner(t, test.F, false) + tRunner(t, test.F, verbose) + if !t.verbose && t.Failed() { + // use printFailure to print output log of this + // and/or any subtests that may have failed. + t.printFailure() + } -doReport: report := t.report() out, _ := json.Marshal(report) return string(out) } -func tRunner(t *T, fn testingFunc, logSteps bool) { +func formatDur(dur int64) string { + // XXX switch to FormatFloat after it's been added + // 1 sec = 1e9 nsec + // this gets us the "centiseconds" which is what we show in tests. + dstr := strconv.Itoa(int(dur / 1e7)) + if len(dstr) < 3 { + const pad = "000" + dstr = pad[:3-len(dstr)] + dstr + } + return dstr[:len(dstr)-2] + "." + dstr[len(dstr)-2:] + "s" +} + +// used to calculate execution times; only present in testing stdlibs +func unixNano() int64 + +func tRunner(t *T, fn testingFunc, verbose bool) { if !t.shouldRun(t.name) { - t.filtered = true - if logSteps { - // fmt.Printf("--- FILT %s\n", t.name) - } return } + start := unixNano() + defer func() { err := recover() - if err != nil { + switch err.(type) { + case nil: + case skipErr: + default: t.Fail() - fmt.Printf(fmt.Sprintf("%v", err)) + fmt.Fprintf(os.Stderr, "panic: %v\n", err) + } + + dur := unixNano() - start + t.dur = formatDur(dur) + + if t.verbose { + switch { + case t.Failed(): + fmt.Fprintf(os.Stderr, "--- FAIL: %s (%s)\n", t.name, t.dur) + case t.skipped: + fmt.Fprintf(os.Stderr, "--- SKIP: %s (%s)\n", t.name, t.dur) + case t.verbose: + fmt.Fprintf(os.Stderr, "--- PASS: %s (%s)\n", t.name, t.dur) + } } }() - if logSteps { - fmt.Printf("=== RUN %s\n", t.name) + if verbose { + fmt.Fprintf(os.Stderr, "=== RUN %s\n", t.name) } - fn(t) - t.finished = true + fn(t) } diff --git a/gnovm/stdlibs/testing/testing.go b/gnovm/stdlibs/testing/testing.go new file mode 100644 index 00000000000..2c2e1d69904 --- /dev/null +++ b/gnovm/stdlibs/testing/testing.go @@ -0,0 +1,6 @@ +package testing + +func X_unixNano() int64 { + // only implemented in testing stdlibs + return 0 +} diff --git a/gnovm/stdlibs/time/time.gno b/gnovm/stdlibs/time/time.gno index 7b7e45ba9b6..ceed70452f6 100644 --- a/gnovm/stdlibs/time/time.gno +++ b/gnovm/stdlibs/time/time.gno @@ -80,8 +80,6 @@ package time import ( "errors" - - ios "internal/os" // XXX to access time. // XXX _ "unsafe" // for go:linkname ) @@ -1071,17 +1069,13 @@ func daysSinceEpoch(year int) uint64 { return d } -/* XXX replaced with ios.Now() -// Provided by package runtime. -func now() (sec int64, nsec int32, mono int64) -*/ +func now() (sec int64, nsec int32, mono int64) // injected -// XXX SHIM // runtimeNano returns the current value of the runtime clock in nanoseconds. // //go:linkname runtimeNano runtime.nanotime func runtimeNano() int64 { - _, _, mono := ios.Now() // XXX now() + _, _, mono := now() return mono } @@ -1095,7 +1089,7 @@ var startNano int64 = runtimeNano() - 1 // Now returns the current local time. func Now() Time { - sec, nsec, mono := ios.Now() // XXX now() + sec, nsec, mono := now() mono -= startNano sec += unixToInternal - minWall if uint64(sec)>>33 != 0 { diff --git a/gnovm/stdlibs/time/time.go b/gnovm/stdlibs/time/time.go new file mode 100644 index 00000000000..8c1c768715e --- /dev/null +++ b/gnovm/stdlibs/time/time.go @@ -0,0 +1,17 @@ +package time + +import ( + "time" + + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/stdlibs/std" +) + +func X_now(m *gno.Machine) (sec int64, nsec int32, mono int64) { + if m == nil || m.Context == nil { + return 0, 0, 0 + } + + ctx := m.Context.(std.ExecContext) + return ctx.Timestamp, int32(ctx.TimestampNano), ctx.Timestamp*int64(time.Second) + ctx.TimestampNano +} diff --git a/gnovm/tests/backup/cli1.gno b/gnovm/tests/backup/cli1.gno index 8eb9e8c40d7..843eb461ac2 100644 --- a/gnovm/tests/backup/cli1.gno +++ b/gnovm/tests/backup/cli1.gno @@ -2,7 +2,7 @@ package main import ( "fmt" - "io/ioutil" + "io" "log" "net" "net/http" @@ -13,7 +13,7 @@ func client(uri string) { if err != nil { log.Fatal(err) } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } diff --git a/gnovm/tests/backup/cli2.gno b/gnovm/tests/backup/cli2.gno index a9e8e53be3a..0be01d2f1a9 100644 --- a/gnovm/tests/backup/cli2.gno +++ b/gnovm/tests/backup/cli2.gno @@ -2,7 +2,7 @@ package main import ( "fmt" - "io/ioutil" + "io" "log" "net" "net/http" @@ -21,7 +21,7 @@ func client(uri string) { if err != nil { log.Fatal(err) } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } diff --git a/gnovm/tests/backup/cli3.gno b/gnovm/tests/backup/cli3.gno index 1c696d4bc4c..50b11b9e4c3 100644 --- a/gnovm/tests/backup/cli3.gno +++ b/gnovm/tests/backup/cli3.gno @@ -2,7 +2,7 @@ package main import ( "fmt" - "io/ioutil" + "io" "log" "net/http" "net/http/httptest" @@ -13,7 +13,7 @@ func client(uri string) { if err != nil { log.Fatal(err) } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } diff --git a/gnovm/tests/backup/cli4.gno b/gnovm/tests/backup/cli4.gno index 147b63c3e6e..aab6405917c 100644 --- a/gnovm/tests/backup/cli4.gno +++ b/gnovm/tests/backup/cli4.gno @@ -2,7 +2,7 @@ package main import ( "fmt" - "io/ioutil" + "io" "log" "net/http" "net/http/httptest" @@ -40,7 +40,7 @@ func client(uri string) { if err != nil { log.Fatal(err) } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } diff --git a/gnovm/tests/backup/cli5.gno b/gnovm/tests/backup/cli5.gno index a2e1787c996..6b536841a6d 100644 --- a/gnovm/tests/backup/cli5.gno +++ b/gnovm/tests/backup/cli5.gno @@ -2,7 +2,7 @@ package main import ( "fmt" - "io/ioutil" + "io" "log" "net/http" "net/http/httptest" @@ -40,7 +40,7 @@ func client(uri string) { if err != nil { log.Fatal(err) } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } diff --git a/gnovm/tests/backup/cli6.gno b/gnovm/tests/backup/cli6.gno index 89ae9f8b98d..e97da82736e 100644 --- a/gnovm/tests/backup/cli6.gno +++ b/gnovm/tests/backup/cli6.gno @@ -2,7 +2,7 @@ package main import ( "fmt" - "io/ioutil" + "io" "log" "net/http" "net/http/httptest" @@ -41,7 +41,7 @@ func client(uri string) { if err != nil { log.Fatal(err) } - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { log.Fatal(err) } diff --git a/gnovm/tests/backup/file_access.gno b/gnovm/tests/backup/file_access.gno index 6d750c44a36..e81cc6a0bee 100644 --- a/gnovm/tests/backup/file_access.gno +++ b/gnovm/tests/backup/file_access.gno @@ -2,12 +2,11 @@ package main import ( "fmt" - "io/ioutil" - "os" + "io" ) func main() { - file, err := ioutil.TempFile("", "yeagibench") + file, err := io.TempFile("", "yeagibench") if err != nil { panic(err) } diff --git a/gnovm/tests/backup/ioutil.gno b/gnovm/tests/backup/ioutil.gno deleted file mode 100644 index eae22b6ea5a..00000000000 --- a/gnovm/tests/backup/ioutil.gno +++ /dev/null @@ -1,16 +0,0 @@ -package main - -import ( - "fmt" - "os" -) - -func main() { - _, err := os.ReadFile("__NotExisting__") - if err != nil { - fmt.Println(err.Error()) - } -} - -// Output: -// open __NotExisting__: no such file or directory diff --git a/gnovm/tests/backup/issue-558.gno b/gnovm/tests/backup/issue-558.gno index d36bb4e18a7..e99566f7634 100644 --- a/gnovm/tests/backup/issue-558.gno +++ b/gnovm/tests/backup/issue-558.gno @@ -4,7 +4,7 @@ import ( "errors" "fmt" "io" - "io/ioutil" + "io" "log" "strings" ) @@ -37,7 +37,7 @@ type pipe struct { func newReadAutoCloser(r io.Reader) readAutoCloser { if _, ok := r.(io.Closer); !ok { - return readAutoCloser{ioutil.NopCloser(r)} + return readAutoCloser{io.NopCloser(r)} } return readAutoCloser{r.(io.ReadCloser)} } @@ -45,7 +45,7 @@ func newReadAutoCloser(r io.Reader) readAutoCloser { func main() { p := &pipe{} p.Reader = newReadAutoCloser(strings.NewReader("test")) - b, err := ioutil.ReadAll(p.Reader) + b, err := io.ReadAll(p.Reader) if err != nil { log.Fatal(err) } diff --git a/gnovm/tests/challenges/not_a_type.gno b/gnovm/tests/challenges/not_a_type.gno new file mode 100644 index 00000000000..ed8adb9983a --- /dev/null +++ b/gnovm/tests/challenges/not_a_type.gno @@ -0,0 +1,10 @@ +package main + +var T struct{} + +func (t T) do() {} + +func main() {} + +// Error: +// T (variable of type struct{}) is not a type diff --git a/gnovm/tests/file.go b/gnovm/tests/file.go index 451bf0677dc..70bed4eda50 100644 --- a/gnovm/tests/file.go +++ b/gnovm/tests/file.go @@ -183,6 +183,7 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { }, }, } + // run decls and init functions. m.RunMemPackage(memPkg, true) // reconstruct machine and clear store cache. // whether package is realm or not, since non-realm @@ -240,15 +241,20 @@ func RunFileTest(rootDir string, path string, opts ...RunFileTestOption) error { if pnc == nil { panic(fmt.Sprintf("fail on %s: got nil error, want: %q", path, errWanted)) } + errstr := "" - if tv, ok := pnc.(*gno.TypedValue); ok { - errstr = tv.Sprint(m) - } else { + switch v := pnc.(type) { + case *gno.TypedValue: + errstr = v.Sprint(m) + case *gno.PreprocessError: + errstr = v.Unwrap().Error() + default: errstr = strings.TrimSpace(fmt.Sprintf("%v", pnc)) } if errstr != errWanted { panic(fmt.Sprintf("fail on %s: got %q, want: %q", path, errstr, errWanted)) } + // NOTE: ignores any gno.GetDebugErrors(). gno.ClearDebugErrors() return nil // nothing more to do. diff --git a/gnovm/tests/files/a47.gno b/gnovm/tests/files/a47.gno new file mode 100644 index 00000000000..2775547a3e8 --- /dev/null +++ b/gnovm/tests/files/a47.gno @@ -0,0 +1,32 @@ +package main + +type S struct { + i int +} + +func main() { + sArr := make([]S, 0, 4) + sArr = append(sArr, S{1}, S{2}, S{3}) + println(sArr[0].i, sArr[1].i, sArr[2].i) + + newArr := append(sArr[:0], sArr[1:]...) + + // The append modified the underlying array because it was within capacity. + println(len(sArr) == 3) + println(sArr[0].i, sArr[1].i, sArr[2].i) + + // It generated a new slice that references the same array. + println(len(newArr) == 2) + println(newArr[0].i, newArr[1].i) + + // The struct should have been copied, not referenced. + println(&sArr[2] != &newArr[1]) +} + +// Output: +// 1 2 3 +// true +// 2 3 3 +// true +// 2 3 +// true diff --git a/gnovm/tests/files/append5.gno b/gnovm/tests/files/append5.gno new file mode 100644 index 00000000000..0eba5a46463 --- /dev/null +++ b/gnovm/tests/files/append5.gno @@ -0,0 +1,10 @@ +package main + +func main() { + var x string + y := append([]byte{'X'}, x...) + println(string(y)) +} + +// Output: +// X \ No newline at end of file diff --git a/gnovm/tests/files/append6.gno b/gnovm/tests/files/append6.gno new file mode 100644 index 00000000000..523c57bf044 --- /dev/null +++ b/gnovm/tests/files/append6.gno @@ -0,0 +1,12 @@ +package main + +func main() { + const x = 118999 + y := 11 + p := []int{} + p = append(p, x, y) + println(p[0] + p[1]) +} + +// Output: +// 119010 diff --git a/gnovm/tests/files/append7.gno b/gnovm/tests/files/append7.gno new file mode 100644 index 00000000000..85783724205 --- /dev/null +++ b/gnovm/tests/files/append7.gno @@ -0,0 +1,10 @@ +package main + +func main() { + var errors []error + errors = append(errors, nil, nil) + println(len(errors)) +} + +// Output: +// 2 diff --git a/gnovm/tests/files/base_conv0.gno b/gnovm/tests/files/base_conv0.gno new file mode 100644 index 00000000000..381d10cdc98 --- /dev/null +++ b/gnovm/tests/files/base_conv0.gno @@ -0,0 +1,27 @@ +package main + +func main() { + // binary + println(0b1010) + println(0B1010) + + // decimal + println(10) + + // octal + println(0o12) + println(012) + + // hex + println(0xA) + println(0xa) +} + +// Output: +// 10 +// 10 +// 10 +// 10 +// 10 +// 10 +// 10 diff --git a/gnovm/tests/files/base_conv1.gno b/gnovm/tests/files/base_conv1.gno new file mode 100644 index 00000000000..6ccf8d0fe70 --- /dev/null +++ b/gnovm/tests/files/base_conv1.gno @@ -0,0 +1,27 @@ +package main + +func main() { + // binary + println(-0b1010) + println(-0B1010) + + // decimal + println(-10) + + // octal + println(-0o12) + println(-012) + + // hex + println(-0xA) + println(-0xa) +} + +// Output: +// -10 +// -10 +// -10 +// -10 +// -10 +// -10 +// -10 diff --git a/gnovm/tests/files/bool6.gno b/gnovm/tests/files/bool6.gno new file mode 100644 index 00000000000..ad4d832036c --- /dev/null +++ b/gnovm/tests/files/bool6.gno @@ -0,0 +1,12 @@ +package main + +func main() { + println(X()) +} + +func X() string { + return "hello" || "world" +} + +// Error: +// main/files/bool6.gno:8: operands of boolean operators must evaluate to boolean typed values diff --git a/gnovm/tests/files/bool7.gno b/gnovm/tests/files/bool7.gno new file mode 100644 index 00000000000..ba8be09dc7c --- /dev/null +++ b/gnovm/tests/files/bool7.gno @@ -0,0 +1,16 @@ +package main + +// Ensure, when comparing evaluated boolean operand types, that the kinds produced +// are the same when one operand is typed and the other is untyped. +func main() { + println(boolAndTrue(true)) + println(boolAndTrue(false)) +} + +func boolAndTrue(b bool) bool { + return b && true +} + +// Output: +// true +// false diff --git a/gnovm/tests/files/byte_slice_issue1570.gno b/gnovm/tests/files/byte_slice_issue1570.gno new file mode 100644 index 00000000000..c1956f3d15d --- /dev/null +++ b/gnovm/tests/files/byte_slice_issue1570.gno @@ -0,0 +1,15 @@ +package main + +func main() { + for i := 0; i < 2; i++ { + l := []byte("lead") + if i == 0 { + l[0] = 'f' + } + println(string(l)) + } +} + +// Output: +// fead +// lead diff --git a/gnovm/tests/files/comp3.gno b/gnovm/tests/files/comp3.gno new file mode 100644 index 00000000000..d412e8f0e48 --- /dev/null +++ b/gnovm/tests/files/comp3.gno @@ -0,0 +1,21 @@ +package main + +func main() { + // test against uninitialized value: https://github.com/gnolang/gno/pull/1132 + var x string + y := "Hello" + results := [...]bool{ + x < y, + x <= y, + x >= y, + x > y, + + x == y, + x == "", + y == "", + } + println(results) +} + +// Output: +// array[(true bool),(true bool),(false bool),(false bool),(false bool),(true bool),(false bool)] diff --git a/gnovm/tests/files/convert4.gno b/gnovm/tests/files/convert4.gno new file mode 100644 index 00000000000..6ad748499ed --- /dev/null +++ b/gnovm/tests/files/convert4.gno @@ -0,0 +1,7 @@ +package main + +func main() { + println(int(nil)) +} + +// error: cannot convert (undefined) to int diff --git a/gnovm/tests/files/convert5.gno b/gnovm/tests/files/convert5.gno new file mode 100644 index 00000000000..acd33889ff0 --- /dev/null +++ b/gnovm/tests/files/convert5.gno @@ -0,0 +1,9 @@ +package main + +func main() { + var ints []int + ints = append(ints, nil, nil) + println(ints) +} + +// error: cannot convert (undefined) to int diff --git a/gnovm/tests/files/extern/redeclaration1/README.md b/gnovm/tests/files/extern/redeclaration1/README.md new file mode 100644 index 00000000000..6c8514c3ca1 --- /dev/null +++ b/gnovm/tests/files/extern/redeclaration1/README.md @@ -0,0 +1,4 @@ +This package is invalid because 'a' is defined twice. +NOTE: the Go parser itself returns an error for redefinitions in the same file, +but testing for redeclarations across files requires our own custom logic. +(arguably we should check ourself either way). diff --git a/gnovm/tests/files/extern/redeclaration1/redeclaration.gno b/gnovm/tests/files/extern/redeclaration1/redeclaration.gno new file mode 100644 index 00000000000..b9b17a50e80 --- /dev/null +++ b/gnovm/tests/files/extern/redeclaration1/redeclaration.gno @@ -0,0 +1,3 @@ +package redeclaration + +var a = 1 diff --git a/gnovm/tests/files/extern/redeclaration1/redeclaration2.gno b/gnovm/tests/files/extern/redeclaration1/redeclaration2.gno new file mode 100644 index 00000000000..11f2f095cb4 --- /dev/null +++ b/gnovm/tests/files/extern/redeclaration1/redeclaration2.gno @@ -0,0 +1,4 @@ +package redeclaration + +// redeclared +var a = 2 diff --git a/gnovm/tests/files/extern/redeclaration2/README.md b/gnovm/tests/files/extern/redeclaration2/README.md new file mode 100644 index 00000000000..6c8514c3ca1 --- /dev/null +++ b/gnovm/tests/files/extern/redeclaration2/README.md @@ -0,0 +1,4 @@ +This package is invalid because 'a' is defined twice. +NOTE: the Go parser itself returns an error for redefinitions in the same file, +but testing for redeclarations across files requires our own custom logic. +(arguably we should check ourself either way). diff --git a/gnovm/tests/files/extern/redeclaration2/redeclaration.gno b/gnovm/tests/files/extern/redeclaration2/redeclaration.gno new file mode 100644 index 00000000000..ef6cac198db --- /dev/null +++ b/gnovm/tests/files/extern/redeclaration2/redeclaration.gno @@ -0,0 +1,3 @@ +package redeclaration + +func a() int { return 1 } diff --git a/gnovm/tests/files/extern/redeclaration2/redeclaration2.gno b/gnovm/tests/files/extern/redeclaration2/redeclaration2.gno new file mode 100644 index 00000000000..3f58a963502 --- /dev/null +++ b/gnovm/tests/files/extern/redeclaration2/redeclaration2.gno @@ -0,0 +1,4 @@ +package redeclaration + +// redeclared (with same signature) +func a() int { return 2 } diff --git a/gnovm/tests/files/extern/redeclaration3/README.md b/gnovm/tests/files/extern/redeclaration3/README.md new file mode 100644 index 00000000000..6c8514c3ca1 --- /dev/null +++ b/gnovm/tests/files/extern/redeclaration3/README.md @@ -0,0 +1,4 @@ +This package is invalid because 'a' is defined twice. +NOTE: the Go parser itself returns an error for redefinitions in the same file, +but testing for redeclarations across files requires our own custom logic. +(arguably we should check ourself either way). diff --git a/gnovm/tests/files/extern/redeclaration3/redeclaration.gno b/gnovm/tests/files/extern/redeclaration3/redeclaration.gno new file mode 100644 index 00000000000..380a1a4288f --- /dev/null +++ b/gnovm/tests/files/extern/redeclaration3/redeclaration.gno @@ -0,0 +1,5 @@ +package redeclaration + +type a struct{} + +func (_ a) method() int { return 1 } diff --git a/gnovm/tests/files/extern/redeclaration3/redeclaration2.gno b/gnovm/tests/files/extern/redeclaration3/redeclaration2.gno new file mode 100644 index 00000000000..c2256bdf7cb --- /dev/null +++ b/gnovm/tests/files/extern/redeclaration3/redeclaration2.gno @@ -0,0 +1,4 @@ +package redeclaration + +// redeclared (with same signature) +func (_ a) method() int { return 2 } diff --git a/gnovm/tests/files/float5_native.gno b/gnovm/tests/files/float5_native.gno index 1050d277605..b2d9228c0bc 100644 --- a/gnovm/tests/files/float5_native.gno +++ b/gnovm/tests/files/float5_native.gno @@ -1,13 +1,13 @@ package main import ( - imath "internal/math" + "math" ) func main() { // test float64 f := float64(0.3) - x := imath.Float64bits(f) + x := math.Float64bits(f) e := uint(40) println(f, x, e, (1 << (64 - e))) diff --git a/gnovm/tests/files/float5_stdlibs.gno b/gnovm/tests/files/float5_stdlibs.gno index 8449ddff820..b3d8cd84713 100644 --- a/gnovm/tests/files/float5_stdlibs.gno +++ b/gnovm/tests/files/float5_stdlibs.gno @@ -2,13 +2,11 @@ package main import ( "math" - - imath "internal/math" ) func main() { - println(math.MaxFloat32, imath.Float32bits(math.MaxFloat32)) - println(math.MaxFloat64, imath.Float64bits(math.MaxFloat64)) + println(math.MaxFloat32, math.Float32bits(math.MaxFloat32)) + println(math.MaxFloat64, math.Float64bits(math.MaxFloat64)) } // Output: diff --git a/gnovm/tests/files/float6.gno b/gnovm/tests/files/float6.gno new file mode 100644 index 00000000000..680b9461975 --- /dev/null +++ b/gnovm/tests/files/float6.gno @@ -0,0 +1,13 @@ +package main + +const ( + SmallestNonzeroFloat64 = 0x1p-1022 * 0x1p-52 // 4.9406564584124654417656879286822137236505980e-324 + DividedByTwo = SmallestNonzeroFloat64 / 2 +) + +func main() { + println(DividedByTwo) +} + +// Output: +// 0 diff --git a/gnovm/tests/files/float7.gno b/gnovm/tests/files/float7.gno new file mode 100644 index 00000000000..f519a963523 --- /dev/null +++ b/gnovm/tests/files/float7.gno @@ -0,0 +1,15 @@ +package main + +const ( + SmallestNonzeroFloat32 = 0x1p-126 * 0x1p-23 // 1.401298464324817070923729583289916131280e-45 + DividedByTwo = SmallestNonzeroFloat32 / 2 +) + +func main() { + var i float32 + i = DividedByTwo + println(i) +} + +// Output: +// 0 diff --git a/gnovm/tests/files/inc.gno b/gnovm/tests/files/inc0.gno similarity index 100% rename from gnovm/tests/files/inc.gno rename to gnovm/tests/files/inc0.gno diff --git a/gnovm/tests/files/inc1.gno b/gnovm/tests/files/inc1.gno new file mode 100644 index 00000000000..f997b3b9a00 --- /dev/null +++ b/gnovm/tests/files/inc1.gno @@ -0,0 +1,15 @@ +package main + +func main() { + var i float64 = 899 + i++ + println(i) + + j := float32(901) + j-- + println(j) +} + +// Output: +// 900 +// 900 diff --git a/gnovm/tests/files/ioutil0.gno b/gnovm/tests/files/io2.gno similarity index 88% rename from gnovm/tests/files/ioutil0.gno rename to gnovm/tests/files/io2.gno index 800d237be22..24655f5040c 100644 --- a/gnovm/tests/files/ioutil0.gno +++ b/gnovm/tests/files/io2.gno @@ -2,7 +2,7 @@ package main import ( "fmt" - "io/ioutil" + "io" "log" "strings" ) @@ -10,7 +10,7 @@ import ( func main() { r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.") - b, err := ioutil.ReadAll(r) + b, err := io.ReadAll(r) if err != nil { log.Fatal(err) } diff --git a/gnovm/tests/files/issue-1085.gno b/gnovm/tests/files/issue-1085.gno new file mode 100644 index 00000000000..680eb8a413e --- /dev/null +++ b/gnovm/tests/files/issue-1085.gno @@ -0,0 +1,20 @@ +package main + +func main() { + a := uint64(9223372036854775808) + var b uint64 = uint64(9223372036854775808) + + const c = uint64(9223372036854775808) + const d uint64 = uint64(9223372036854775808) + + println(a) + println(b) + println(c) + println(d) +} + +// Output: +// 9223372036854775808 +// 9223372036854775808 +// 9223372036854775808 +// 9223372036854775808 diff --git a/gnovm/tests/files/issue-1096.gno b/gnovm/tests/files/issue-1096.gno new file mode 100644 index 00000000000..b0593913401 --- /dev/null +++ b/gnovm/tests/files/issue-1096.gno @@ -0,0 +1,87 @@ +package main + +import "fmt" + +type X struct { + Array [8]int + Test bool +} + +type Y [8]int + +func main() { + x := X{} + x.Array[1] = 888 + println(x.Array[1]) + println(x.Array[2]) + println(x.Test) + + x.manip() + println(x.Array[1]) + println(x.Array[2]) + println(x.Test) + + println("-----") + + y := Y{} + y[1] = 888 + println(y[1]) + println(y[2]) + + y.manip() + println(y[1]) + println(y[2]) + println("-----") + + x = X{} + println(x.Array[1]) + println(x.Array[2]) + println(x.Test) + + x.Array[1] = 888 + println(x.Array[1]) + println(x.Array[2]) + println(x.Test) + + manip(x) + println(x.Array[1]) + println(x.Array[2]) + println(x.Test) +} + +func (x X) manip() { + x.Array[2] = 999 + x.Test = true +} + +func manip(x X) { + x.Array[2] = 999 + x.Test = true +} + +func (y Y) manip() { + y[2] = 111 +} + +// Output: +// 888 +// 0 +// false +// 888 +// 0 +// false +// ----- +// 888 +// 0 +// 888 +// 0 +// ----- +// 0 +// 0 +// false +// 888 +// 0 +// false +// 888 +// 0 +// false diff --git a/gnovm/tests/files/issue-558b.gno b/gnovm/tests/files/issue-558b.gno index 686c73b5c88..55eba88c985 100644 --- a/gnovm/tests/files/issue-558b.gno +++ b/gnovm/tests/files/issue-558b.gno @@ -3,7 +3,7 @@ package main import ( "fmt" "io" - "io/ioutil" + "io" "log" "strings" ) @@ -36,7 +36,7 @@ type pipe struct { func newReadAutoCloser(r io.Reader) readAutoCloser { if _, ok := r.(io.Closer); !ok { - return readAutoCloser{ioutil.NopCloser(r)} + return readAutoCloser{io.NopCloser(r)} } return readAutoCloser{r.(io.ReadCloser)} } diff --git a/gnovm/tests/files/math4.gno b/gnovm/tests/files/math4.gno new file mode 100644 index 00000000000..83bfb44f7b0 --- /dev/null +++ b/gnovm/tests/files/math4.gno @@ -0,0 +1,19 @@ +package main + +func main() { + println(-3.0 * 1.0 / 2.0) // -1.5 + println(-3.0 * 1.0 / 2) // -1.5 + println(-3.0 * 1 / 2.0) // -1.5 + + println(-3 * 1.0 / 2.0) // 1.5 + println(-3 * 1.0 / 2) // 1.5 + println(-3 * 1 / 2.0) // 1.5 +} + +// Output: +// -1.5 +// -1.5 +// -1.5 +// -1.5 +// -1.5 +// -1.5 diff --git a/gnovm/tests/files/print1.gno b/gnovm/tests/files/print1.gno new file mode 100644 index 00000000000..606759a5c05 --- /dev/null +++ b/gnovm/tests/files/print1.gno @@ -0,0 +1,9 @@ +package main + +func main() { + var a []string + println(a) +} + +// Output: +// nil []string diff --git a/gnovm/tests/files/redeclaration10.gno b/gnovm/tests/files/redeclaration10.gno new file mode 100644 index 00000000000..01584b1755c --- /dev/null +++ b/gnovm/tests/files/redeclaration10.gno @@ -0,0 +1,12 @@ +package main + +import _ "github.com/gnolang/gno/_test/redeclaration3" + +func main() { + println("should not happen") +} + +// XXX show what was redeclared. + +// Error: +// running package "github.com/gnolang/gno/_test/redeclaration3": duplicate declarations not allowed diff --git a/gnovm/tests/files/redeclaration6.gno b/gnovm/tests/files/redeclaration6.gno new file mode 100644 index 00000000000..25e36fa61aa --- /dev/null +++ b/gnovm/tests/files/redeclaration6.gno @@ -0,0 +1,12 @@ +package main + +import _ "github.com/gnolang/gno/_test/redeclaration1" + +func main() { + println("should not happen") +} + +// XXX show what was redeclared. + +// Error: +// running package "github.com/gnolang/gno/_test/redeclaration1": duplicate declarations not allowed diff --git a/gnovm/tests/files/redeclaration7.gno b/gnovm/tests/files/redeclaration7.gno new file mode 100644 index 00000000000..d987a58d7eb --- /dev/null +++ b/gnovm/tests/files/redeclaration7.gno @@ -0,0 +1,13 @@ +package main + +func f1() int { return 1 } + +func f1() int { return 2 } + +func main() { + println("hello", f1()) +} + +// Error: +// files/redeclaration7.gno:5:6: f1 redeclared in this block +// previous declaration at files/redeclaration7.gno:3:6 diff --git a/gnovm/tests/files/redeclaration8.gno b/gnovm/tests/files/redeclaration8.gno new file mode 100644 index 00000000000..d0e5b958030 --- /dev/null +++ b/gnovm/tests/files/redeclaration8.gno @@ -0,0 +1,12 @@ +package main + +import _ "github.com/gnolang/gno/_test/redeclaration2" + +func main() { + println("should not happen") +} + +// XXX show what was redeclared. + +// Error: +// running package "github.com/gnolang/gno/_test/redeclaration2": duplicate declarations not allowed diff --git a/gnovm/tests/files/redeclaration9.gno b/gnovm/tests/files/redeclaration9.gno new file mode 100644 index 00000000000..89a63683c13 --- /dev/null +++ b/gnovm/tests/files/redeclaration9.gno @@ -0,0 +1,14 @@ +package main + +type a struct{} + +func (_ a) method() int { return 1 } + +func (_ a) method() int { return 2 } + +func main() { + println("hello") +} + +// Error: +// main/files/redeclaration9.gno:7: redeclaration of method a.method diff --git a/gnovm/tests/files/zrealm0.gno b/gnovm/tests/files/zrealm0.gno index 1b8f37540b3..7578781e503 100644 --- a/gnovm/tests/files/zrealm0.gno +++ b/gnovm/tests/files/zrealm0.gno @@ -56,6 +56,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm1.gno b/gnovm/tests/files/zrealm1.gno index 1dea983a49d..d90c5e8621a 100644 --- a/gnovm/tests/files/zrealm1.gno +++ b/gnovm/tests/files/zrealm1.gno @@ -170,6 +170,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm2.gno b/gnovm/tests/files/zrealm2.gno index bf321c42d31..67ba2f5a768 100644 --- a/gnovm/tests/files/zrealm2.gno +++ b/gnovm/tests/files/zrealm2.gno @@ -173,6 +173,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.3", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -207,6 +209,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm3.gno b/gnovm/tests/files/zrealm3.gno index 4ff8dd1a531..da8a581375c 100644 --- a/gnovm/tests/files/zrealm3.gno +++ b/gnovm/tests/files/zrealm3.gno @@ -172,6 +172,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.2", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -206,6 +208,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm4.gno b/gnovm/tests/files/zrealm4.gno index 2e2fa4e8d09..dc3c48c774b 100644 --- a/gnovm/tests/files/zrealm4.gno +++ b/gnovm/tests/files/zrealm4.gno @@ -114,6 +114,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -148,6 +150,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm5.gno b/gnovm/tests/files/zrealm5.gno index 8ad3e7400b3..e65b089c18d 100644 --- a/gnovm/tests/files/zrealm5.gno +++ b/gnovm/tests/files/zrealm5.gno @@ -185,6 +185,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -219,6 +221,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm6.gno b/gnovm/tests/files/zrealm6.gno index fbe320ad962..20615fa7d39 100644 --- a/gnovm/tests/files/zrealm6.gno +++ b/gnovm/tests/files/zrealm6.gno @@ -257,6 +257,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -291,6 +293,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm7.gno b/gnovm/tests/files/zrealm7.gno index 689d55d3916..9decb0dae10 100644 --- a/gnovm/tests/files/zrealm7.gno +++ b/gnovm/tests/files/zrealm7.gno @@ -329,6 +329,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -363,6 +365,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm_avl0.gno b/gnovm/tests/files/zrealm_avl0.gno index 814e19d6d49..e91788ac8eb 100644 --- a/gnovm/tests/files/zrealm_avl0.gno +++ b/gnovm/tests/files/zrealm_avl0.gno @@ -267,6 +267,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -301,6 +303,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm_avl1.gno b/gnovm/tests/files/zrealm_avl1.gno index 410e9e93601..cdd56a5ad89 100644 --- a/gnovm/tests/files/zrealm_avl1.gno +++ b/gnovm/tests/files/zrealm_avl1.gno @@ -291,6 +291,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", @@ -325,6 +327,8 @@ func main() { // "FileName": "main.gno", // "IsMethod": false, // "Name": "main", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/test", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/files/zrealm_natbind0.gno b/gnovm/tests/files/zrealm_natbind0.gno new file mode 100644 index 00000000000..60e0d448202 --- /dev/null +++ b/gnovm/tests/files/zrealm_natbind0.gno @@ -0,0 +1,197 @@ +// PKGPATH: gno.land/r/test +package test + +import ( + "std" +) + +var node interface{} + +func init() { + node = std.GetOrigCaller +} + +func main() { + f := node.(func() std.Address) + println(f()) + node = std.DerivePkgAddr + g := node.(func(path string) std.Address) + println(g("x")) +} + +// Output: +// g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm +// g19kt9e22k34ny5jf5plrjdltmws0jc0qqd2cwky + +// Realm: +// switchrealm["gno.land/r/test"] +// u[a8ada09dee16d791fd406d629fe29bb0ed084a30:2]={ +// "Blank": {}, +// "ObjectInfo": { +// "ID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:2", +// "IsEscaped": true, +// "ModTime": "3", +// "RefCount": "2" +// }, +// "Parent": null, +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "", +// "Line": "0", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Values": [ +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "FileName": "main.gno", +// "IsMethod": false, +// "Name": "init.0", +// "NativeName": "", +// "NativePkg": "", +// "PkgPath": "gno.land/r/test", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "main.gno", +// "Line": "10", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a8ada09dee16d791fd406d629fe29bb0ed084a30:3" +// }, +// "FileName": "main.gno", +// "IsMethod": false, +// "Name": "main", +// "NativeName": "", +// "NativePkg": "", +// "PkgPath": "gno.land/r/test", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "main.gno", +// "Line": "14", +// "Nonce": "0", +// "PkgPath": "gno.land/r/test" +// } +// }, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [], +// "Results": [] +// } +// } +// }, +// { +// "T": { +// "@type": "/gno.FuncType", +// "Params": [ +// { +// "Embedded": false, +// "Name": "pkgPath", +// "Tag": "", +// "Type": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// } +// } +// ], +// "Results": [ +// { +// "Embedded": false, +// "Name": "", +// "Tag": "", +// "Type": { +// "@type": "/gno.RefType", +// "ID": "std.Address" +// } +// } +// ] +// }, +// "V": { +// "@type": "/gno.FuncValue", +// "Closure": { +// "@type": "/gno.RefValue", +// "Escaped": true, +// "ObjectID": "a7f5397443359ea76c50be82c77f1f893a060925:5" +// }, +// "FileName": "native.gno", +// "IsMethod": false, +// "Name": "DerivePkgAddr", +// "NativeName": "DerivePkgAddr", +// "NativePkg": "std", +// "PkgPath": "std", +// "Source": { +// "@type": "/gno.RefNode", +// "BlockNode": null, +// "Location": { +// "File": "native.gno", +// "Line": "15", +// "Nonce": "0", +// "PkgPath": "std" +// } +// }, +// "Type": { +// "@type": "/gno.FuncType", +// "Params": [ +// { +// "Embedded": false, +// "Name": "pkgPath", +// "Tag": "", +// "Type": { +// "@type": "/gno.PrimitiveType", +// "value": "16" +// } +// } +// ], +// "Results": [ +// { +// "Embedded": false, +// "Name": "", +// "Tag": "", +// "Type": { +// "@type": "/gno.RefType", +// "ID": "std.Address" +// } +// } +// ] +// } +// } +// } +// ] +// } diff --git a/gnovm/tests/files/zrealm_tests0.gno b/gnovm/tests/files/zrealm_tests0.gno index cfb1f08c6f4..73d07f726eb 100644 --- a/gnovm/tests/files/zrealm_tests0.gno +++ b/gnovm/tests/files/zrealm_tests0.gno @@ -239,62 +239,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": true, // "Name": "Modify", -// "PkgPath": "gno.land/r/demo/tests", -// "Source": { -// "@type": "/gno.RefNode", -// "BlockNode": null, -// "Location": { -// "File": "tests.gno", -// "Line": "42", -// "Nonce": "0", -// "PkgPath": "gno.land/r/demo/tests" -// } -// }, -// "Type": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "t", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.TestRealmObject" -// } -// } -// } -// ], -// "Results": [] -// } -// } -// }, -// { -// "T": { -// "@type": "/gno.FuncType", -// "Params": [ -// { -// "Embedded": false, -// "Name": "t", -// "Tag": "", -// "Type": { -// "@type": "/gno.PointerType", -// "Elt": { -// "@type": "/gno.RefType", -// "ID": "gno.land/r/demo/tests.TestRealmObject" -// } -// } -// } -// ], -// "Results": [] -// }, -// "V": { -// "@type": "/gno.FuncValue", -// "Closure": null, -// "FileName": "tests.gno", -// "IsMethod": true, -// "Name": "Modify", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -399,6 +345,8 @@ func main() { // "FileName": "interfaces.gno", // "IsMethod": false, // "Name": "AddStringer", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -463,6 +411,8 @@ func main() { // "FileName": "interfaces.gno", // "IsMethod": false, // "Name": "Render", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -517,6 +467,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "IncCounter", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -561,6 +513,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "Counter", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -615,6 +569,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "CurrentRealmPath", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -659,6 +615,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "AssertOriginCall", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -703,6 +661,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "IsOriginCall", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -760,6 +720,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "ModifyTestRealmObject", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -807,6 +769,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "InitTestNodes", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -841,6 +805,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "ModTestNodes", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -875,6 +841,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "PrintTestNodes", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -919,6 +887,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "GetPrevRealm", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -973,6 +943,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "GetRSubtestsPrevRealm", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", @@ -1028,6 +1000,8 @@ func main() { // "FileName": "tests.gno", // "IsMethod": false, // "Name": "Exec", +// "NativeName": "", +// "NativePkg": "", // "PkgPath": "gno.land/r/demo/tests", // "Source": { // "@type": "/gno.RefNode", diff --git a/gnovm/tests/imports.go b/gnovm/tests/imports.go index fc2820ce00e..0db5651fbcc 100644 --- a/gnovm/tests/imports.go +++ b/gnovm/tests/imports.go @@ -20,13 +20,13 @@ import ( "image" "image/color" "io" - "io/ioutil" "log" "math" "math/big" "math/rand" "net" "net/url" + "os" "path/filepath" "reflect" "sort" @@ -34,17 +34,15 @@ import ( "strings" "sync" "sync/atomic" - "testing" "text/template" "time" "unicode/utf8" gno "github.com/gnolang/gno/gnovm/pkg/gnolang" "github.com/gnolang/gno/gnovm/stdlibs" - "github.com/gnolang/gno/tm2/pkg/crypto" + teststdlibs "github.com/gnolang/gno/gnovm/tests/stdlibs" dbm "github.com/gnolang/gno/tm2/pkg/db" osm "github.com/gnolang/gno/tm2/pkg/os" - "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/store/dbadapter" "github.com/gnolang/gno/tm2/pkg/store/iavl" stypes "github.com/gnolang/gno/tm2/pkg/store/types" @@ -52,15 +50,16 @@ import ( type importMode uint64 +// Import modes to control the import behaviour of TestStore. const ( + // use stdlibs/* only (except a few exceptions). for stdlibs/* and examples/* testing. ImportModeStdlibsOnly importMode = iota + // use stdlibs/* if present, otherwise use native. used in files/tests, excluded for *_native.go ImportModeStdlibsPreferred + // do not use stdlibs/* if native registered. used in files/tests, excluded for *_stdlibs.go ImportModeNativePreferred ) -// ImportModeStdlibsOnly: use stdlibs/* only (except a few exceptions). for stdlibs/* and examples/* testing. -// ImportModeStdlibsPreferred: use stdlibs/* if present, otherwise use native. for files/tests2/*. -// ImportModeNativePreferred: do not use stdlibs/* if native registered. for files/tests/*. // NOTE: this isn't safe, should only be used for testing. func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Writer, mode importMode) (store gno.Store) { getPackage := func(pkgPath string) (pn *gno.PackageNode, pv *gno.PackageValue) { @@ -94,23 +93,9 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri // if stdlibs package is preferred , try to load it first. if mode == ImportModeStdlibsOnly || mode == ImportModeStdlibsPreferred { - stdlibPath := filepath.Join(rootDir, "gnovm", "stdlibs", pkgPath) - if osm.DirExists(stdlibPath) { - memPkg := gno.ReadMemPackage(stdlibPath, pkgPath) - if !memPkg.IsEmpty() { - m2 := gno.NewMachineWithOptions(gno.MachineOptions{ - // NOTE: see also pkgs/sdk/vm/builtins.go - // XXX: why does this fail when just pkgPath? - PkgPath: "gno.land/r/stdlibs/" + pkgPath, - Output: stdout, - Store: store, - }) - save := pkgPath != "testing" // never save the "testing" package - return m2.RunMemPackage(memPkg, save) - } - - // There is no package there, but maybe we have a - // native counterpart below. + pn, pv = loadStdlib(rootDir, pkgPath, store, stdout) + if pn != nil { + return } } @@ -121,12 +106,10 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri pkgPath == "crypto/rand" || pkgPath == "crypto/md5" || pkgPath == "crypto/sha1" || - pkgPath == "encoding/base64" || pkgPath == "encoding/binary" || pkgPath == "encoding/json" || pkgPath == "encoding/xml" || pkgPath == "internal/os_test" || - pkgPath == "math" || pkgPath == "math/big" || pkgPath == "math/rand" || mode == ImportModeStdlibsPreferred || @@ -216,17 +199,6 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri pkg.DefineGoNativeType(reflect.TypeOf(net.TCPAddr{})) pkg.DefineGoNativeValue("IPv4", net.IPv4) return pkg, pkg.NewPackage() - case "net/http": - // XXX UNSAFE - // There's no reason why we can't replace these with safer alternatives. - panic("just say gno") - /* - pkg := gno.NewPackageNode("http", pkgPath, nil) - pkg.DefineGoNativeType(reflect.TypeOf(http.Request{})) - pkg.DefineGoNativeValue("DefaultClient", http.DefaultClient) - pkg.DefineGoNativeType(reflect.TypeOf(http.Client{})) - return pkg, pkg.NewPackage() - */ case "net/url": pkg := gno.NewPackageNode("url", pkgPath, nil) pkg.DefineGoNativeType(reflect.TypeOf(url.Values{})) @@ -278,6 +250,8 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri pkg.DefineGoNativeValue("Abs", math.Abs) pkg.DefineGoNativeValue("Cos", math.Cos) pkg.DefineGoNativeValue("Pi", math.Pi) + pkg.DefineGoNativeValue("Float64bits", math.Float64bits) + pkg.DefineGoNativeValue("Pi", math.Pi) pkg.DefineGoNativeValue("MaxFloat32", math.MaxFloat32) pkg.DefineGoNativeValue("MaxFloat64", math.MaxFloat64) return pkg, pkg.NewPackage() @@ -356,16 +330,13 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri case "io": pkg := gno.NewPackageNode("io", pkgPath, nil) pkg.DefineGoNativeValue("EOF", io.EOF) + pkg.DefineGoNativeValue("NopCloser", io.NopCloser) pkg.DefineGoNativeValue("ReadFull", io.ReadFull) + pkg.DefineGoNativeValue("ReadAll", io.ReadAll) pkg.DefineGoNativeType(reflect.TypeOf((*io.ReadCloser)(nil)).Elem()) pkg.DefineGoNativeType(reflect.TypeOf((*io.Closer)(nil)).Elem()) pkg.DefineGoNativeType(reflect.TypeOf((*io.Reader)(nil)).Elem()) return pkg, pkg.NewPackage() - case "io/ioutil": - pkg := gno.NewPackageNode("ioutil", pkgPath, nil) - pkg.DefineGoNativeValue("NopCloser", ioutil.NopCloser) - pkg.DefineGoNativeValue("ReadAll", ioutil.ReadAll) - return pkg, pkg.NewPackage() case "log": pkg := gno.NewPackageNode("log", pkgPath, nil) pkg.DefineGoNativeValue("Fatal", log.Fatal) @@ -389,25 +360,6 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri pkg := gno.NewPackageNode("fnv", pkgPath, nil) pkg.DefineGoNativeValue("New32a", fnv.New32a) return pkg, pkg.NewPackage() - /* XXX support somehow for speed. for now, generic implemented in stdlibs. - case "internal/bytealg": - pkg := gno.NewPackageNode("bytealg", pkgPath, nil) - pkg.DefineGoNativeValue("Compare", bytealg.Compare) - pkg.DefineGoNativeValue("CountString", bytealg.CountString) - pkg.DefineGoNativeValue("Cutover", bytealg.Cutover) - pkg.DefineGoNativeValue("Equal", bytealg.Equal) - pkg.DefineGoNativeValue("HashStr", bytealg.HashStr) - pkg.DefineGoNativeValue("HashStrBytes", bytealg.HashStrBytes) - pkg.DefineGoNativeValue("HashStrRev", bytealg.HashStrRev) - pkg.DefineGoNativeValue("HashStrRevBytes", bytealg.HashStrRevBytes) - pkg.DefineGoNativeValue("Index", bytealg.Index) - pkg.DefineGoNativeValue("IndexByte", bytealg.IndexByte) - pkg.DefineGoNativeValue("IndexByteString", bytealg.IndexByteString) - pkg.DefineGoNativeValue("IndexRabinKarp", bytealg.IndexRabinKarp) - pkg.DefineGoNativeValue("IndexRabinKarpBytes", bytealg.IndexRabinKarpBytes) - pkg.DefineGoNativeValue("IndexString", bytealg.IndexString) - return pkg, pkg.NewPackage() - */ default: // continue on... } @@ -415,19 +367,8 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri // if native package is preferred, try to load stdlibs/* as backup. if mode == ImportModeNativePreferred { - stdlibPath := filepath.Join(rootDir, "gnovm", "stdlibs", pkgPath) - if osm.DirExists(stdlibPath) { - memPkg := gno.ReadMemPackage(stdlibPath, pkgPath) - if memPkg.IsEmpty() { - panic(fmt.Sprintf("found an empty package %q", pkgPath)) - } - - m2 := gno.NewMachineWithOptions(gno.MachineOptions{ - PkgPath: "test", - Output: stdout, - Store: store, - }) - pn, pv = m2.RunMemPackage(memPkg, true) + pn, pv = loadStdlib(rootDir, pkgPath, store, stdout) + if pn != nil { return } } @@ -456,6 +397,7 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri iavlStore := iavl.StoreConstructor(db, stypes.StoreOptions{}) store = gno.NewStore(nil, baseStore, iavlStore) store.SetPackageGetter(getPackage) + store.SetNativeStore(teststdlibs.NativeStore) store.SetPackageInjector(testPackageInjector) store.SetStrictGo2GnoMapping(false) // native mappings @@ -463,30 +405,49 @@ func TestStore(rootDir, filesPath string, stdin io.Reader, stdout, stderr io.Wri return } -//---------------------------------------- -// testInjectNatives -// analogous to stdlibs.InjectNatives, but with -// native methods suitable for the testing environment. - -func testPackageInjector(store gno.Store, pn *gno.PackageNode) { - // Also inject stdlibs native functions. - stdlibs.InjectPackage(store, pn) - isOriginCall := func(m *gno.Machine) bool { - tname := m.Frames[0].Func.Name - switch tname { - case "main": // test is a _filetest - return len(m.Frames) == 3 - case "runtest": // test is a _test - return len(m.Frames) == 7 +func loadStdlib(rootDir, pkgPath string, store gno.Store, stdout io.Writer) (*gno.PackageNode, *gno.PackageValue) { + dirs := [...]string{ + // normal stdlib path. + filepath.Join(rootDir, "gnovm", "stdlibs", pkgPath), + // override path. definitions here override the previous if duplicate. + filepath.Join(rootDir, "gnovm", "tests", "stdlibs", pkgPath), + } + files := make([]string, 0, 32) // pre-alloc 32 as a likely high number of files + for _, path := range dirs { + dl, err := os.ReadDir(path) + if err != nil { + if os.IsNotExist(err) { + continue + } + panic(fmt.Errorf("could not access dir %q: %w", path, err)) } - // support init() in _filetest - // XXX do we need to distinguish from 'runtest'/_test? - // XXX pretty hacky even if not. - if strings.HasPrefix(string(tname), "init.") { - return len(m.Frames) == 3 + + for _, f := range dl { + // NOTE: RunMemPackage has other rules; those should be mostly useful + // for on-chain packages (ie. include README and gno.mod). + if !f.IsDir() && strings.HasSuffix(f.Name(), ".gno") { + files = append(files, filepath.Join(path, f.Name())) + } } - panic("unable to determine if test is a _test or a _filetest") } + if len(files) == 0 { + return nil, nil + } + + memPkg := gno.ReadMemPackageFromList(files, pkgPath) + m2 := gno.NewMachineWithOptions(gno.MachineOptions{ + // NOTE: see also pkgs/sdk/vm/builtins.go + // Needs PkgPath != its name because TestStore.getPackage is the package + // getter for the store, which calls loadStdlib, so it would be recursively called. + PkgPath: "stdlibload", + Output: stdout, + Store: store, + }) + save := pkgPath != "testing" // never save the "testing" package + return m2.RunMemPackageWithOverrides(memPkg, save) +} + +func testPackageInjector(store gno.Store, pn *gno.PackageNode) { // Test specific injections: switch pn.PkgPath { case "strconv": @@ -494,203 +455,6 @@ func testPackageInjector(store gno.Store, pn *gno.PackageNode) { // from stdlibs.InjectNatives. pn.DefineGoNativeType(reflect.TypeOf(strconv.NumError{})) pn.DefineGoNativeValue("ParseInt", strconv.ParseInt) - case "std": - // NOTE: some of these are overrides. - // Also see stdlibs/InjectPackage. - pn.DefineNativeOverride("AssertOriginCall", - /* - gno.Flds( // params - ), - gno.Flds( // results - ), - */ - func(m *gno.Machine) { - if !isOriginCall(m) { - m.Panic(typedString("invalid non-origin call")) - return - } - }, - ) - pn.DefineNativeOverride("IsOriginCall", - /* - gno.Flds( // params - ), - gno.Flds( // results - "isOrigin", "bool", - ), - */ - func(m *gno.Machine) { - res0 := gno.TypedValue{T: gno.BoolType} - res0.SetBool(isOriginCall(m)) - m.PushValue(res0) - }, - ) - pn.DefineNativeOverride("GetCallerAt", - /* - gno.Flds( // params - "n", "int", - ), - gno.Flds( // results - "", "Address", - ), - */ - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - n := arg0.GetInt() - if n <= 0 { - m.Panic(typedString("GetCallerAt requires positive arg")) - return - } - if n > m.NumFrames()-1 { - // NOTE: the last frame's LastPackage - // is set to the original non-frame - // package, so need this check. - m.Panic(typedString("frame not found")) - return - } - var pkgAddr string - if n == m.NumFrames()-1 { - // This makes it consistent with GetOrigCaller and TestSetOrigCaller. - ctx := m.Context.(stdlibs.ExecContext) - pkgAddr = string(ctx.OrigCaller) - } else { - pkgAddr = string(m.LastCallFrame(n).LastPackage.GetPkgAddr().Bech32()) - } - res0 := gno.Go2GnoValue( - m.Alloc, - m.Store, - reflect.ValueOf(pkgAddr), - ) - addrT := store.GetType(gno.DeclaredTypeID("std", "Address")) - res0.T = addrT - m.PushValue(res0) - }, - ) - pn.DefineNative("TestSetOrigCaller", - gno.Flds( // params - "", "Address", - ), - gno.Flds( // results - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - addr := arg0.GetString() - // overwrite context - ctx := m.Context.(stdlibs.ExecContext) - ctx.OrigCaller = crypto.Bech32Address(addr) - m.Context = ctx - }, - ) - pn.DefineNative("TestSetOrigPkgAddr", - gno.Flds( // params - "", "Address", - ), - gno.Flds( // results - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - addr := crypto.Bech32Address(arg0.GetString()) - // overwrite context - ctx := m.Context.(stdlibs.ExecContext) - ctx.OrigPkgAddr = addr - m.Context = ctx - }, - ) - pn.DefineNative("TestSetOrigSend", - gno.Flds( // params - "sent", "Coins", - "spent", "Coins", - ), - gno.Flds( // results - ), - func(m *gno.Machine) { - arg0, arg1 := m.LastBlock().GetParams2() - var sent std.Coins - rvSent := reflect.ValueOf(&sent).Elem() - gno.Gno2GoValue(arg0.TV, rvSent) - sent = rvSent.Interface().(std.Coins) // needed? - var spent std.Coins - rvSpent := reflect.ValueOf(&spent).Elem() - gno.Gno2GoValue(arg1.TV, rvSpent) - spent = rvSpent.Interface().(std.Coins) // needed? - // overwrite context. - ctx := m.Context.(stdlibs.ExecContext) - ctx.OrigSend = sent - ctx.OrigSendSpent = &spent - m.Context = ctx - }, - ) - pn.DefineNative("TestIssueCoins", - gno.Flds( // params - "addr", "Address", - "coins", "Coins", - ), - gno.Flds( // results - ), - func(m *gno.Machine) { - arg0, arg1 := m.LastBlock().GetParams2() - addr := crypto.Bech32Address(arg0.TV.GetString()) - var coins std.Coins - rvCoins := reflect.ValueOf(&coins).Elem() - gno.Gno2GoValue(arg1.TV, rvCoins) - coins = rvCoins.Interface().(std.Coins) // needed? - // overwrite context. - ctx := m.Context.(stdlibs.ExecContext) - banker := ctx.Banker - for _, coin := range coins { - banker.IssueCoin(addr, coin.Denom, coin.Amount) - } - }, - ) - pn.DefineNative("TestCurrentRealm", - gno.Flds( // params - ), - gno.Flds( // results - "realm", "string", - ), - func(m *gno.Machine) { - rlmpath := m.Realm.Path - m.PushValue(typedString(rlmpath)) - }, - ) - pn.DefineNative("TestSkipHeights", - gno.Flds( // params - "count", "int64", - ), - gno.Flds( // results - ), - func(m *gno.Machine) { - arg0 := m.LastBlock().GetParams1().TV - count := arg0.GetInt64() - - ctx := m.Context.(stdlibs.ExecContext) - ctx.Height += count - m.Context = ctx - }, - ) - // TODO: move elsewhere. - pn.DefineNative("ClearStoreCache", - gno.Flds( // params - ), - gno.Flds( // results - ), - func(m *gno.Machine) { - if gno.IsDebug() && testing.Verbose() { - store.Print() - fmt.Println("========================================") - fmt.Println("CLEAR CACHE (RUNTIME)") - fmt.Println("========================================") - } - m.Store.ClearCache() - m.PreprocessAllFilesAndSaveBlockNodes() - if gno.IsDebug() && testing.Verbose() { - store.Print() - fmt.Println("========================================") - fmt.Println("CLEAR CACHE DONE") - fmt.Println("========================================") - } - }, - ) } } @@ -707,13 +471,6 @@ func (*dummyReader) Read(b []byte) (n int, err error) { //---------------------------------------- -// NOTE: does not allocate; used for panics. -func typedString(s string) gno.TypedValue { - tv := gno.TypedValue{T: gno.StringType} - tv.V = gno.StringValue(s) - return tv -} - type TestReport struct { Name string Verbose bool diff --git a/gnovm/tests/integ/invalid-gno-file/gno.mod b/gnovm/tests/integ/invalid-gno-file/gno.mod new file mode 100644 index 00000000000..060e28b9dc4 --- /dev/null +++ b/gnovm/tests/integ/invalid-gno-file/gno.mod @@ -0,0 +1 @@ +module test diff --git a/gnovm/tests/integ/invalid-gno-file/invalid.gno b/gnovm/tests/integ/invalid-gno-file/invalid.gno new file mode 100644 index 00000000000..1e4ff406ada --- /dev/null +++ b/gnovm/tests/integ/invalid-gno-file/invalid.gno @@ -0,0 +1 @@ +packag invalid diff --git a/gnovm/tests/integ/package-not-declared/gno.mod b/gnovm/tests/integ/package-not-declared/gno.mod new file mode 100644 index 00000000000..8b4e0375297 --- /dev/null +++ b/gnovm/tests/integ/package-not-declared/gno.mod @@ -0,0 +1 @@ +module gno.land/tests/nodeclared \ No newline at end of file diff --git a/gnovm/tests/integ/package-not-declared/main.gno b/gnovm/tests/integ/package-not-declared/main.gno new file mode 100644 index 00000000000..bdbb2e7cfcb --- /dev/null +++ b/gnovm/tests/integ/package-not-declared/main.gno @@ -0,0 +1,5 @@ +package main + +func Main() { + fmt.Println("hello world") +} diff --git a/gnovm/tests/integ/undefined-variable-test/gno.mod b/gnovm/tests/integ/undefined-variable-test/gno.mod new file mode 100644 index 00000000000..0a75f00e83f --- /dev/null +++ b/gnovm/tests/integ/undefined-variable-test/gno.mod @@ -0,0 +1 @@ +module gno.land/tests/undefined-test \ No newline at end of file diff --git a/gnovm/tests/integ/undefined-variable-test/undefined_variables_test.gno b/gnovm/tests/integ/undefined-variable-test/undefined_variables_test.gno new file mode 100644 index 00000000000..0afdec36ef3 --- /dev/null +++ b/gnovm/tests/integ/undefined-variable-test/undefined_variables_test.gno @@ -0,0 +1,7 @@ +package main + +import "testing" + +func TestUndefinedVariables(t *testing.T) { + println("hello world: " + toto) +} diff --git a/gnovm/tests/integ/valid2/gno.mod b/gnovm/tests/integ/valid2/gno.mod new file mode 100644 index 00000000000..98a5a0dacc1 --- /dev/null +++ b/gnovm/tests/integ/valid2/gno.mod @@ -0,0 +1,3 @@ +module gno.land/p/integ/valid + +require gno.land/p/demo/avl v0.0.0-latest diff --git a/gnovm/tests/integ/valid2/valid.gno b/gnovm/tests/integ/valid2/valid.gno new file mode 100644 index 00000000000..4de283f5d87 --- /dev/null +++ b/gnovm/tests/integ/valid2/valid.gno @@ -0,0 +1,11 @@ +package valid + +import ( + "gno.land/p/demo/avl" +) + +const Foo = "foo" + +func DoNothing(t *avl.Tree) { + // noop +} diff --git a/gnovm/tests/integ/valid2/valid_test.gno b/gnovm/tests/integ/valid2/valid_test.gno new file mode 100644 index 00000000000..2394da5c5ae --- /dev/null +++ b/gnovm/tests/integ/valid2/valid_test.gno @@ -0,0 +1,11 @@ +package valid + +import ( + "testing" + + "gno.land/p/integ/valid" +) + +func TestAlwaysValid(t *testing.T) { + _ = valid.Foo +} diff --git a/gno.land/cmd/gnotxsync/main_test.go b/gnovm/tests/integ/valid2/z_0_filetest.gno similarity index 100% rename from gno.land/cmd/gnotxsync/main_test.go rename to gnovm/tests/integ/valid2/z_0_filetest.gno diff --git a/gnovm/tests/selector_test.go b/gnovm/tests/selector_test.go index 4d6e9c587b0..1f0b400555b 100644 --- a/gnovm/tests/selector_test.go +++ b/gnovm/tests/selector_test.go @@ -52,6 +52,8 @@ func _printValue(x interface{}) { } func TestSelectors(t *testing.T) { + t.Parallel() + x0 := struct{ F0 int }{1} _printValue(x0.F0) // *ST.F0 // F:0 diff --git a/gnovm/tests/stdlibs/README.md b/gnovm/tests/stdlibs/README.md new file mode 100644 index 00000000000..16d5d171342 --- /dev/null +++ b/gnovm/tests/stdlibs/README.md @@ -0,0 +1,6 @@ +# tests/stdlibs + +This directory contains test-specific standard libraries. These are only +available when testing gno code in `_test.gno` and `_filetest.gno` files. +Re-declarations of functions already existing override the definitions of the +normal stdlibs directory. diff --git a/gnovm/tests/stdlibs/native.go b/gnovm/tests/stdlibs/native.go new file mode 100644 index 00000000000..373be0ad23b --- /dev/null +++ b/gnovm/tests/stdlibs/native.go @@ -0,0 +1,244 @@ +// This file is autogenerated from the genstd tool (@/misc/genstd); do not edit. +// To regenerate it, run `go generate` from this directory. + +package stdlibs + +import ( + "reflect" + + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + testlibs_std "github.com/gnolang/gno/gnovm/tests/stdlibs/std" + testlibs_testing "github.com/gnolang/gno/gnovm/tests/stdlibs/testing" + tm2_crypto "github.com/gnolang/gno/tm2/pkg/crypto" + tm2_std "github.com/gnolang/gno/tm2/pkg/std" +) + +type nativeFunc struct { + gnoPkg string + gnoFunc gno.Name + params []gno.FieldTypeExpr + results []gno.FieldTypeExpr + f func(m *gno.Machine) +} + +var nativeFuncs = [...]nativeFunc{ + { + "std", + "AssertOriginCall", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{}, + func(m *gno.Machine) { + testlibs_std.AssertOriginCall( + m, + ) + }, + }, + { + "std", + "IsOriginCall", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("bool")}, + }, + func(m *gno.Machine) { + r0 := testlibs_std.IsOriginCall( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "TestCurrentRealm", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("string")}, + }, + func(m *gno.Machine) { + r0 := testlibs_std.TestCurrentRealm( + m, + ) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "TestSkipHeights", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("int64")}, + }, + []gno.FieldTypeExpr{}, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 int64 + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + testlibs_std.TestSkipHeights( + m, + p0) + }, + }, + { + "std", + "ClearStoreCache", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{}, + func(m *gno.Machine) { + testlibs_std.ClearStoreCache( + m, + ) + }, + }, + { + "std", + "GetCallerAt", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("int")}, + }, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("Address")}, + }, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 int + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + r0 := testlibs_std.GetCallerAt( + m, + p0) + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, + { + "std", + "TestSetOrigCaller", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("Address")}, + }, + []gno.FieldTypeExpr{}, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 tm2_crypto.Bech32Address + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + testlibs_std.TestSetOrigCaller( + m, + p0) + }, + }, + { + "std", + "TestSetOrigPkgAddr", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("Address")}, + }, + []gno.FieldTypeExpr{}, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 tm2_crypto.Bech32Address + rp0 = reflect.ValueOf(&p0).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + + testlibs_std.TestSetOrigPkgAddr( + m, + p0) + }, + }, + { + "std", + "TestSetOrigSend", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("Coins")}, + {Name: gno.N("p1"), Type: gno.X("Coins")}, + }, + []gno.FieldTypeExpr{}, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 tm2_std.Coins + rp0 = reflect.ValueOf(&p0).Elem() + p1 tm2_std.Coins + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + testlibs_std.TestSetOrigSend( + m, + p0, p1) + }, + }, + { + "std", + "TestIssueCoins", + []gno.FieldTypeExpr{ + {Name: gno.N("p0"), Type: gno.X("Address")}, + {Name: gno.N("p1"), Type: gno.X("Coins")}, + }, + []gno.FieldTypeExpr{}, + func(m *gno.Machine) { + b := m.LastBlock() + var ( + p0 tm2_crypto.Bech32Address + rp0 = reflect.ValueOf(&p0).Elem() + p1 tm2_std.Coins + rp1 = reflect.ValueOf(&p1).Elem() + ) + + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 0, "")).TV, rp0) + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, 1, "")).TV, rp1) + + testlibs_std.TestIssueCoins( + m, + p0, p1) + }, + }, + { + "testing", + "unixNano", + []gno.FieldTypeExpr{}, + []gno.FieldTypeExpr{ + {Name: gno.N("r0"), Type: gno.X("int64")}, + }, + func(m *gno.Machine) { + r0 := testlibs_testing.X_unixNano() + + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r0).Elem(), + )) + }, + }, +} diff --git a/gnovm/tests/stdlibs/std/std.gno b/gnovm/tests/stdlibs/std/std.gno new file mode 100644 index 00000000000..380549be694 --- /dev/null +++ b/gnovm/tests/stdlibs/std/std.gno @@ -0,0 +1,12 @@ +package std + +func AssertOriginCall() // injected +func IsOriginCall() bool // injected +func TestCurrentRealm() string // injected +func TestSkipHeights(count int64) // injected +func ClearStoreCache() // injected +func GetCallerAt(n int) Address // injected +func TestSetOrigCaller(addr Address) // injected +func TestSetOrigPkgAddr(addr Address) // injected +func TestSetOrigSend(sent, spent Coins) // injected +func TestIssueCoins(addr Address, coins Coins) // injected diff --git a/gnovm/tests/stdlibs/std/std.go b/gnovm/tests/stdlibs/std/std.go new file mode 100644 index 00000000000..27ad079b4a6 --- /dev/null +++ b/gnovm/tests/stdlibs/std/std.go @@ -0,0 +1,116 @@ +package std + +import ( + "fmt" + "strings" + "testing" + + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/stdlibs" + "github.com/gnolang/gno/gnovm/stdlibs/std" + "github.com/gnolang/gno/tm2/pkg/crypto" + tm2std "github.com/gnolang/gno/tm2/pkg/std" +) + +func AssertOriginCall(m *gno.Machine) { + if !IsOriginCall(m) { + m.Panic(typedString("invalid non-origin call")) + } +} + +func typedString(s gno.StringValue) gno.TypedValue { + tv := gno.TypedValue{T: gno.StringType} + tv.SetString(s) + return tv +} + +func IsOriginCall(m *gno.Machine) bool { + tname := m.Frames[0].Func.Name + switch tname { + case "main": // test is a _filetest + return len(m.Frames) == 3 + case "runtest": // test is a _test + return len(m.Frames) == 7 + } + // support init() in _filetest + // XXX do we need to distinguish from 'runtest'/_test? + // XXX pretty hacky even if not. + if strings.HasPrefix(string(tname), "init.") { + return len(m.Frames) == 3 + } + panic("unable to determine if test is a _test or a _filetest") +} + +func TestCurrentRealm(m *gno.Machine) string { + return m.Realm.Path +} + +func TestSkipHeights(m *gno.Machine, count int64) { + ctx := m.Context.(std.ExecContext) + ctx.Height += count + m.Context = ctx +} + +func ClearStoreCache(m *gno.Machine) { + if gno.IsDebug() && testing.Verbose() { + m.Store.Print() + fmt.Println("========================================") + fmt.Println("CLEAR CACHE (RUNTIME)") + fmt.Println("========================================") + } + m.Store.ClearCache() + m.PreprocessAllFilesAndSaveBlockNodes() + if gno.IsDebug() && testing.Verbose() { + m.Store.Print() + fmt.Println("========================================") + fmt.Println("CLEAR CACHE DONE") + fmt.Println("========================================") + } +} + +func GetCallerAt(m *gno.Machine, n int) crypto.Bech32Address { + if n <= 0 { + m.Panic(typedString("GetCallerAt requires positive arg")) + return "" + } + if n > m.NumFrames()-1 { + // NOTE: the last frame's LastPackage + // is set to the original non-frame + // package, so need this check. + m.Panic(typedString("frame not found")) + return "" + } + if n == m.NumFrames()-1 { + // This makes it consistent with GetOrigCaller and TestSetOrigCaller. + ctx := m.Context.(stdlibs.ExecContext) + return ctx.OrigCaller + } + return m.LastCallFrame(n).LastPackage.GetPkgAddr().Bech32() +} + +func TestSetOrigCaller(m *gno.Machine, addr crypto.Bech32Address) { + ctx := m.Context.(std.ExecContext) + ctx.OrigCaller = addr + m.Context = ctx +} + +func TestSetOrigPkgAddr(m *gno.Machine, addr crypto.Bech32Address) { + ctx := m.Context.(stdlibs.ExecContext) + ctx.OrigPkgAddr = addr + m.Context = ctx +} + +func TestSetOrigSend(m *gno.Machine, sent, spent tm2std.Coins) { + ctx := m.Context.(stdlibs.ExecContext) + ctx.OrigSend = sent + ctx.OrigSendSpent = &spent + m.Context = ctx +} + +func TestIssueCoins(m *gno.Machine, addr crypto.Bech32Address, coins tm2std.Coins) { + ctx := m.Context.(stdlibs.ExecContext) + banker := ctx.Banker + for _, coin := range coins { + banker.IssueCoin(addr, coin.Denom, coin.Amount) + } +} diff --git a/gnovm/tests/stdlibs/stdlibs.go b/gnovm/tests/stdlibs/stdlibs.go new file mode 100644 index 00000000000..b0a1050af41 --- /dev/null +++ b/gnovm/tests/stdlibs/stdlibs.go @@ -0,0 +1,18 @@ +// Package stdlibs provides supplemental stdlibs for the testing environment. +package stdlibs + +import ( + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" + "github.com/gnolang/gno/gnovm/stdlibs" +) + +//go:generate go run github.com/gnolang/gno/misc/genstd + +func NativeStore(pkgPath string, name gno.Name) func(*gno.Machine) { + for _, nf := range nativeFuncs { + if nf.gnoPkg == pkgPath && name == nf.gnoFunc { + return nf.f + } + } + return stdlibs.NativeStore(pkgPath, name) +} diff --git a/gnovm/tests/stdlibs/testing/native_testing.gno b/gnovm/tests/stdlibs/testing/native_testing.gno new file mode 100644 index 00000000000..3076a679e77 --- /dev/null +++ b/gnovm/tests/stdlibs/testing/native_testing.gno @@ -0,0 +1,3 @@ +package testing + +func unixNano() int64 diff --git a/gnovm/tests/stdlibs/testing/native_testing.go b/gnovm/tests/stdlibs/testing/native_testing.go new file mode 100644 index 00000000000..db681ad8a62 --- /dev/null +++ b/gnovm/tests/stdlibs/testing/native_testing.go @@ -0,0 +1,7 @@ +package testing + +import "time" + +func X_unixNano() int64 { + return time.Now().UnixNano() +} diff --git a/go.mod b/go.mod index d7544ddcdf5..ac56660dad1 100644 --- a/go.mod +++ b/go.mod @@ -3,47 +3,61 @@ module github.com/gnolang/gno go 1.20 require ( - github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c - github.com/btcsuite/btcd/btcutil v1.0.0 - github.com/btcsuite/btcutil v1.0.2 - github.com/cockroachdb/apd v1.1.0 + github.com/btcsuite/btcd/btcutil v1.1.3 + github.com/cockroachdb/apd/v3 v3.2.1 + github.com/cosmos/ledger-cosmos-go v0.13.3 github.com/davecgh/go-spew v1.1.1 - github.com/dgraph-io/badger/v3 v3.2103.4 + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 + github.com/dgraph-io/badger/v3 v3.2103.5 github.com/fortytw2/leaktest v1.3.0 - github.com/gdamore/tcell/v2 v2.1.0 + github.com/gdamore/tcell/v2 v2.6.0 github.com/gnolang/goleveldb v0.0.9 github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 github.com/golang/protobuf v1.5.3 github.com/google/gofuzz v1.2.0 - github.com/gorilla/mux v1.8.0 - github.com/gorilla/websocket v1.5.0 + github.com/gorilla/mux v1.8.1 + github.com/gorilla/websocket v1.5.1 github.com/gotuna/gotuna v0.6.0 github.com/jaekwon/testify v1.6.1 github.com/jmhodges/levigo v1.0.0 github.com/libp2p/go-buffer-pool v0.1.0 - github.com/linxGnu/grocksdb v1.8.4 + github.com/linxGnu/grocksdb v1.8.11 github.com/mattn/go-runewidth v0.0.15 github.com/pelletier/go-toml v1.9.5 github.com/peterbourgon/ff/v3 v3.4.0 github.com/pmezard/go-difflib v1.0.0 github.com/rogpeppe/go-internal v1.11.0 - github.com/rs/cors v1.10.0 + github.com/rs/cors v1.10.1 github.com/stretchr/testify v1.8.4 github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c - go.etcd.io/bbolt v1.3.7 - go.uber.org/multierr v1.9.0 - golang.org/x/crypto v0.13.0 - golang.org/x/mod v0.12.0 - golang.org/x/net v0.15.0 - golang.org/x/term v0.12.0 - golang.org/x/tools v0.6.0 + go.etcd.io/bbolt v1.3.8 + go.uber.org/multierr v1.10.0 + go.uber.org/zap v1.24.0 + go.uber.org/zap/exp v0.1.0 + golang.org/x/crypto v0.18.0 + golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 + golang.org/x/mod v0.14.0 + golang.org/x/net v0.20.0 + golang.org/x/term v0.16.0 + golang.org/x/tools v0.17.0 google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v3 v3.0.1 ) +require dario.cat/mergo v1.0.0 + +require ( + github.com/zondax/hid v0.9.2 // indirect + github.com/zondax/ledger-go v0.14.3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c // indirect + google.golang.org/grpc v1.58.3 // indirect +) + require ( + github.com/btcsuite/btcd/btcec/v2 v2.3.2 + github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect github.com/cespare/xxhash v1.1.0 // indirect - github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dustin/go-humanize v1.0.0 // indirect github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c // indirect @@ -51,7 +65,7 @@ require ( github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 // indirect github.com/gdamore/encoding v1.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect + github.com/golang/glog v1.1.0 // indirect github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/flatbuffers v1.12.1 // indirect @@ -59,14 +73,13 @@ require ( github.com/gorilla/sessions v1.2.1 // indirect github.com/klauspost/compress v1.12.3 // indirect github.com/kr/text v0.2.0 // indirect - github.com/lib/pq v1.10.7 // indirect - github.com/lucasb-eyer/go-colorful v1.0.3 // indirect - github.com/nxadm/tail v1.4.8 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/nxadm/tail v1.4.11 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/rivo/uniseg v0.4.3 // indirect go.opencensus.io v0.22.5 // indirect - go.uber.org/atomic v1.7.0 // indirect - golang.org/x/sys v0.12.0 // indirect - golang.org/x/text v0.13.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/go.sum b/go.sum index b038376393c..cdde46147e8 100644 --- a/go.sum +++ b/go.sum @@ -1,41 +1,63 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c h1:lnAMg3ra/Gw4AkRMxrxYs8nrprWsHowg8H9zaYsJOo4= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= -github.com/btcsuite/btcd/btcutil v1.0.0 h1:dB36qRTOucIh6NUe40UCieOS+axPhP6VNyRtYkTUKKk= +github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= +github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/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/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/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/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= +github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= +github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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/dgraph-io/badger/v3 v3.2103.4 h1:WE1B07YNTTJTtG9xjBcSW2wn0RJLyiV99h959RKZqM4= -github.com/dgraph-io/badger/v3 v3.2103.4/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= +github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= @@ -51,25 +73,33 @@ github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+ne github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= 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/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell/v2 v2.1.0 h1:UnSmozHgBkQi2PGsFr+rpdXuAPRRucMegpQp3Z3kDro= -github.com/gdamore/tcell/v2 v2.1.0/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA= +github.com/gdamore/tcell/v2 v2.6.0 h1:OKbluoP9VYmJwZwq/iLb4BxwKcwGthaa1YNBJIyCySg= +github.com/gdamore/tcell/v2 v2.6.0/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y= github.com/gnolang/goleveldb v0.0.9 h1:Q7rGko9oXMKtQA+Apeeed5a3sjba/mcDhzJGoTVLCKE= github.com/gnolang/goleveldb v0.0.9/go.mod h1:Dz6p9bmpy/FBESTgduiThZt5mToVDipcHGzj/zUOo8E= github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk= github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= 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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -79,20 +109,23 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/csrf v1.7.0/go.mod h1:+a/4tCmqhG6/w4oafeAZ9pEa3/NZOWYVbD9fV0FwIQA= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gotuna/gotuna v0.6.0 h1:N1lQKXEi/lwRp8u3sccTYLhzOffA4QasExz/1M5Riws= github.com/gotuna/gotuna v0.6.0/go.mod h1:F/ecRt29ChB6Ycy1AFIBpBiMNK0j7Heq+gFbLWquhjc= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= @@ -101,6 +134,7 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/jaekwon/testify v1.6.1 h1:4AtAJcR9GzXN5W4DdY7ie74iCPiJV1JJUJL90t2ZUyw= github.com/jaekwon/testify v1.6.1/go.mod h1:Oun0RXIHI7osufabQ60i4Lqkj0GXLbqI1I7kgzBNm1U= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= @@ -117,26 +151,31 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= -github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= -github.com/linxGnu/grocksdb v1.8.4 h1:ZMsBpPpJNtRLHiKKp0mI7gW+NT4s7UgfD5xHxx1jVRo= -github.com/linxGnu/grocksdb v1.8.4/go.mod h1:xZCIb5Muw+nhbDK4Y5UJuOrin5MceOuiXkVUR7vp4WY= -github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= -github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/linxGnu/grocksdb v1.8.11 h1:BGol9e5gB1BrsTvOxloC88pe70TCqgrfLNwkyWW0kD8= +github.com/linxGnu/grocksdb v1.8.11/go.mod h1:xZCIb5Muw+nhbDK4Y5UJuOrin5MceOuiXkVUR7vp4WY= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= +github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 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.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/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= @@ -146,12 +185,13 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= +github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/rs/cors v1.10.0 h1:62NOS1h+r8p1mW6FM0FSB0exioXLhd/sh15KpjWBZ+8= -github.com/rs/cors v1.10.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= @@ -164,41 +204,56 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= 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/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= -go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= +github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= +github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI= +go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= -go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap/exp v0.1.0 h1:Ol9zQNvAEAgFHSBiR5LlwS9Xq8u5QF+7HBwNHUB8rcI= +go.uber.org/zap/exp v0.1.0/go.mod h1:z/0T3As39ttolxZGOsvk1OEvQfwwfTZpmV9YTp+VAkc= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= 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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +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= @@ -207,9 +262,13 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 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= @@ -217,24 +276,41 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -242,8 +318,9 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -253,8 +330,18 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c h1:jHkCUWkseRf+W+edG5hMzr/Uh1xkDREY4caybAq4dpY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= @@ -268,6 +355,8 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/misc/Makefile b/misc/Makefile new file mode 100644 index 00000000000..44dd9b2c98a --- /dev/null +++ b/misc/Makefile @@ -0,0 +1,36 @@ +.PHONY: help +help: + @echo "Available make commands:" + @cat Makefile | grep '^[a-z][^:]*:' | cut -d: -f1 | sort | sed 's/^/ /' + +rundep=go run -modfile ./devdeps/go.mod + +######################################## +# Dev tools +.PHONY: lint +lint: + $(rundep) github.com/golangci/golangci-lint/cmd/golangci-lint run --config ../.github/golangci.yml ./... + +.PHONY: fmt +GOFMT_FLAGS ?= -w +fmt: + $(rundep) mvdan.cc/gofumpt $(GOFMT_FLAGS) . + +######################################## +# Test suite +.PHONY: test +test: _test.genstd + +GOTEST_FLAGS ?= -v -p 1 -timeout=30m + +.PHONY: _test.genstd +_test.genstd: + go test ./genstd/... $(GOTEST_FLAGS) + +.PHONY: tidy +VERIFY_MOD_SUMS ?= false +tidy: + # Give execute permissions + chmod +x ./mod_tidy.sh + # Tidy go mods + VERIFY_MOD_SUMS=$(VERIFY_MOD_SUMS) ./mod_tidy.sh diff --git a/misc/deployments/staging.gno.land/Makefile b/misc/deployments/staging.gno.land/Makefile index 63966df6ec1..516be6dfc0e 100644 --- a/misc/deployments/staging.gno.land/Makefile +++ b/misc/deployments/staging.gno.land/Makefile @@ -11,7 +11,7 @@ logs: down: docker compose down docker volume rm -f staginggnoland_gnonode - docker compose run gnoland rm -rf /opt/gno/src/testdir/data /opt/gno/src/testdir/config + docker compose run gnoland rm -rf /opt/gno/src/gno.land/testdir/data /opt/gno/src/gno.land/testdir/config pull: git pull diff --git a/misc/deployments/staging.gno.land/docker-compose.yml b/misc/deployments/staging.gno.land/docker-compose.yml index 268002902ab..bf825b54536 100644 --- a/misc/deployments/staging.gno.land/docker-compose.yml +++ b/misc/deployments/staging.gno.land/docker-compose.yml @@ -5,6 +5,9 @@ services: container_name: gnoland build: ../../.. environment: + - VIRTUAL_HOST=rpc.staging.gno.land + - VIRTUAL_PORT=36657 + - LETSENCRYPT_HOST=rpc.staging.gno.land - LOG_LEVEL=4 working_dir: /opt/gno/src/gno.land command: @@ -36,8 +39,7 @@ services: - --faucet-url=https://faucet-staging.gno.land/ - --help-chainid=staging - --help-remote=staging.gno.land:36657 - - --pages-dir=/overlay/pages - - --views-dir=./gno.land/cmd/gnoweb/views + - --with-analytics volumes: - "./overlay:/overlay:ro" links: diff --git a/misc/deployments/staging.gno.land/overlay/config.toml b/misc/deployments/staging.gno.land/overlay/config.toml index a02bb45bbb7..1f5952f9638 100644 --- a/misc/deployments/staging.gno.land/overlay/config.toml +++ b/misc/deployments/staging.gno.land/overlay/config.toml @@ -123,7 +123,7 @@ max_body_bytes = 1000000 max_header_bytes = 1048576 # The path to a file containing certificate that is used to create the HTTPS server. -# Migth be either absolute path or path related to tendermint's config directory. +# Might be either absolute path or path related to tendermint's config directory. # If the certificate is signed by a certificate authority, # the certFile should be the concatenation of the server's certificate, any intermediates, # and the CA's certificate. @@ -131,7 +131,7 @@ max_header_bytes = 1048576 tls_cert_file = "" # The path to a file containing matching private key that is used to create the HTTPS server. -# Migth be either absolute path or path related to tendermint's config directory. +# Might be either absolute path or path related to tendermint's config directory. # NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. tls_key_file = "" diff --git a/misc/deployments/test2.gno.land/overlay/config.toml b/misc/deployments/test2.gno.land/overlay/config.toml index edf04ba4431..65e34a2c55c 100644 --- a/misc/deployments/test2.gno.land/overlay/config.toml +++ b/misc/deployments/test2.gno.land/overlay/config.toml @@ -123,7 +123,7 @@ max_body_bytes = 1000000 max_header_bytes = 1048576 # The path to a file containing certificate that is used to create the HTTPS server. -# Migth be either absolute path or path related to tendermint's config directory. +# Might be either absolute path or path related to tendermint's config directory. # If the certificate is signed by a certificate authority, # the certFile should be the concatenation of the server's certificate, any intermediates, # and the CA's certificate. @@ -131,7 +131,7 @@ max_header_bytes = 1048576 tls_cert_file = "" # The path to a file containing matching private key that is used to create the HTTPS server. -# Migth be either absolute path or path related to tendermint's config directory. +# Might be either absolute path or path related to tendermint's config directory. # NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. tls_key_file = "" diff --git a/misc/deployments/test3.gno.land/overlay/config.toml b/misc/deployments/test3.gno.land/overlay/config.toml index edf04ba4431..65e34a2c55c 100644 --- a/misc/deployments/test3.gno.land/overlay/config.toml +++ b/misc/deployments/test3.gno.land/overlay/config.toml @@ -123,7 +123,7 @@ max_body_bytes = 1000000 max_header_bytes = 1048576 # The path to a file containing certificate that is used to create the HTTPS server. -# Migth be either absolute path or path related to tendermint's config directory. +# Might be either absolute path or path related to tendermint's config directory. # If the certificate is signed by a certificate authority, # the certFile should be the concatenation of the server's certificate, any intermediates, # and the CA's certificate. @@ -131,7 +131,7 @@ max_header_bytes = 1048576 tls_cert_file = "" # The path to a file containing matching private key that is used to create the HTTPS server. -# Migth be either absolute path or path related to tendermint's config directory. +# Might be either absolute path or path related to tendermint's config directory. # NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. tls_key_file = "" diff --git a/misc/devdeps/Makefile b/misc/devdeps/Makefile index 99aea512ed0..54df62cc031 100644 --- a/misc/devdeps/Makefile +++ b/misc/devdeps/Makefile @@ -1,4 +1,3 @@ install: go install mvdan.cc/gofumpt go install google.golang.org/protobuf/cmd/protoc-gen-go - go install golang.org/x/tools/cmd/godoc diff --git a/misc/devdeps/deps.go b/misc/devdeps/deps.go index 3454e5b13f6..7ac068c71ac 100644 --- a/misc/devdeps/deps.go +++ b/misc/devdeps/deps.go @@ -11,6 +11,9 @@ import ( // required to generate String method _ "golang.org/x/tools/cmd/stringer" + // required for import formatting + _ "golang.org/x/tools/cmd/goimports" + // required for formatting, linting, pls. _ "golang.org/x/tools/gopls" _ "mvdan.cc/gofumpt" diff --git a/misc/docker-integration/integration_test.go b/misc/docker-integration/integration_test.go index 6ae0ea1d36e..1142709cc16 100644 --- a/misc/docker-integration/integration_test.go +++ b/misc/docker-integration/integration_test.go @@ -29,6 +29,8 @@ const ( ) func TestDockerIntegration(t *testing.T) { + t.Parallel() + tmpdir, err := os.MkdirTemp(os.TempDir(), "*-gnoland-integration") require.NoError(t, err) diff --git a/misc/docusaurus/.gitignore b/misc/docusaurus/.gitignore new file mode 100644 index 00000000000..9e2bac49798 --- /dev/null +++ b/misc/docusaurus/.gitignore @@ -0,0 +1,21 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.docusaurus +.cache-loader + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.yarn \ No newline at end of file diff --git a/misc/docusaurus/.yarnrc.yml b/misc/docusaurus/.yarnrc.yml new file mode 100644 index 00000000000..3186f3f0795 --- /dev/null +++ b/misc/docusaurus/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/misc/docusaurus/babel.config.js b/misc/docusaurus/babel.config.js new file mode 100644 index 00000000000..e00595dae7d --- /dev/null +++ b/misc/docusaurus/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: [require.resolve('@docusaurus/core/lib/babel/preset')], +}; diff --git a/misc/docusaurus/docusaurus.config.js b/misc/docusaurus/docusaurus.config.js new file mode 100644 index 00000000000..6daec1be821 --- /dev/null +++ b/misc/docusaurus/docusaurus.config.js @@ -0,0 +1,184 @@ +// @ts-check +// Note: type annotations allow type checking and IDEs autocompletion + +const lightCodeTheme = require("prism-react-renderer/themes/github"); +const darkCodeTheme = require("prism-react-renderer/themes/dracula"); + +/** @type {import('@docusaurus/types').Config} */ +const config = { + title: "Gno.land Documentation", + favicon: "img/favicon.ico", + url: "https://docs.gno.land", + baseUrl: "/", + + organizationName: "gnolang", + projectName: "gno", + + onBrokenLinks: "throw", + onBrokenMarkdownLinks: "warn", + + i18n: { + defaultLocale: "en", + locales: ["en"], + }, + + scripts: [ + { + // 100% privacy-first analytics + src: "https://sa.gno.services/latest.js", + async: true, + defer: true, + }, + ], + + presets: [ + [ + "classic", + /** @type {import('@docusaurus/preset-classic').Options} */ + ({ + docs: { + path: "../../docs", + routeBasePath: "/", + sidebarPath: require.resolve("./sidebars.js"), + showLastUpdateTime: true, + editUrl: ({ docPath }) => `https://github.com/gnolang/gno/edit/master/docs/${docPath}`, + }, + blog: false, + theme: { + customCss: require.resolve("./src/css/custom.css"), + }, + }), + ], + ], + + themeConfig: + /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ + ({ + image: "img/og.jpg", + navbar: { + hideOnScroll: true, + logo: { + alt: "Gno.land Logo", + src: "img/gnoland.svg", + srcDark: "img/gnoland_light.svg", + }, + items: [ + { + position: "right", + label: "Back to Gno.Land", + to: "https://gno.land", + className: "gno-header__copy", + }, + { + type: "docSidebar", + sidebarId: "tutorialSidebar", + position: "left", + label: "Docs", + }, + { + position: "left", + label: "Playground", + to: "https://play.gno.land", + }, + { + position: "left", + label: "Blog", + to: "https://test3.gno.land/r/gnoland/blog", + }, + { + href: "https://github.com/gnolang/gno", + html: ` + + + `, + position: "right", + }, + ], + }, + footer: { + style: "dark", + + links: [ + { + items: [ + { + html: ` + + + + + + `, + }, + ], + }, + { + title: "Gno Libraries", + items: [ + { + label: "gno-js-client", + href: "https://github.com/gnolang/gno-js-client", + }, + { + label: "tm2-js-client", + href: "https://github.com/gnolang/tm2-js-client", + }, + ], + }, + { + title: "Ecosystem", + items: [ + { + label: "Gno by Example", + href: "https://gno-by-example.com/", + }, + { + label: "Awesome GNO", + href: "https://github.com/gnolang/awesome-gno", + }, + ], + }, + ], + }, + prism: { + theme: lightCodeTheme, + darkTheme: darkCodeTheme, + }, + }), +}; + +module.exports = config; diff --git a/misc/docusaurus/package.json b/misc/docusaurus/package.json new file mode 100644 index 00000000000..7c67713d14c --- /dev/null +++ b/misc/docusaurus/package.json @@ -0,0 +1,47 @@ +{ + "name": "gno-docs", + "version": "0.0.0", + "private": true, + "scripts": { + "docusaurus": "docusaurus", + "start": "docusaurus start", + "build": "docusaurus build", + "swizzle": "docusaurus swizzle", + "deploy": "docusaurus deploy", + "clear": "docusaurus clear", + "serve": "docusaurus serve", + "write-translations": "docusaurus write-translations", + "write-heading-ids": "docusaurus write-heading-ids", + "typecheck": "tsc", + "embed": "for f in $(find ../../docs -name '*.md'); do embedmd -w $f; done\n" + }, + "dependencies": { + "@docusaurus/core": "2.4.1", + "@docusaurus/preset-classic": "2.4.1", + "@mdx-js/react": "^1.6.22", + "clsx": "^1.2.1", + "prism-react-renderer": "^1.3.5", + "react": "^17.0.2", + "react-dom": "^17.0.2" + }, + "devDependencies": { + "@docusaurus/module-type-aliases": "2.4.1", + "@tsconfig/docusaurus": "^1.0.5", + "typescript": "^4.7.4" + }, + "browserslist": { + "production": [ + ">0.5%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "engines": { + "node": ">=16.14" + } +} diff --git a/misc/docusaurus/sidebars.js b/misc/docusaurus/sidebars.js new file mode 100644 index 00000000000..b6317cbbae0 --- /dev/null +++ b/misc/docusaurus/sidebars.js @@ -0,0 +1,142 @@ +// @ts-check + +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const sidebars = { + tutorialSidebar: [ + 'overview', + { + type: 'category', + label: 'Getting Started', + items: [ + 'getting-started/local-setup', + 'getting-started/working-with-key-pairs', + { + type: 'category', + label: 'Setting up Funds', + items: [ + 'getting-started/setting-up-funds/premining-balances', + 'getting-started/setting-up-funds/running-a-faucet', + ] + }, + 'getting-started/setting-up-a-local-chain', + 'getting-started/browsing-gno-source-code', + ], + }, + { + type: 'category', + label: 'How-to Guides', + items: [ + 'how-to-guides/simple-contract', + 'how-to-guides/simple-library', + 'how-to-guides/testing-gno', + 'how-to-guides/deploy', + 'how-to-guides/write-simple-dapp', + 'how-to-guides/creating-grc20', + 'how-to-guides/creating-grc721', + 'how-to-guides/connect-wallet-dapp', + ], + }, + { + type: 'category', + label: 'Concepts', + items: [ + 'concepts/realms', + 'concepts/packages', + { + type: 'category', + label: 'Standard Libraries', + items: [ + 'concepts/standard-library/overview', + 'concepts/standard-library/banker', + 'concepts/standard-library/coin', + 'concepts/standard-library/gnopher-hole-stdlib', + ] + }, + 'concepts/gnovm', + 'concepts/gno-language', + 'concepts/effective-gno', + 'concepts/proof-of-contribution', + 'concepts/tendermint2', + 'concepts/gno-modules', + 'concepts/gno-test', + 'concepts/from-go-to-gno', + ], + }, + { + type: 'category', + label: 'Gno Tooling', + items: [ + 'gno-tooling/cli/gno-tooling-gno', + 'gno-tooling/cli/gno-tooling-gnokey', + 'gno-tooling/cli/gno-tooling-gnodev', + 'gno-tooling/cli/gno-tooling-gnoland', + 'gno-tooling/cli/gno-tooling-gnofaucet', + ] + }, + { + type: 'category', + label: 'Reference', + items: [ + 'reference/rpc-endpoints', + { + type: 'category', + label: 'Standard Libraries', + items: [ + 'reference/standard-library/overview', + { + type: 'category', + label: 'std', + items: [ + 'reference/standard-library/std/address', + 'reference/standard-library/std/banker', + 'reference/standard-library/std/coin', + 'reference/standard-library/std/coins', + 'reference/standard-library/std/chain', + 'reference/standard-library/std/testing', + ] + } + ] + }, + 'reference/go-gno-compatibility', + { + type: 'category', + label: 'tm2-js-client', + items: [ + 'reference/tm2-js-client/tm2-js-getting-started', + 'reference/tm2-js-client/tm2-js-wallet', + { + type: 'category', + label: 'Provider', + items: [ + 'reference/tm2-js-client/Provider/tm2-js-provider', + 'reference/tm2-js-client/Provider/tm2-js-json-rpc-provider', + 'reference/tm2-js-client/Provider/tm2-js-ws-provider', + 'reference/tm2-js-client/Provider/tm2-js-utility', + ] + }, + { + type: 'category', + label: 'Signer', + items: [ + 'reference/tm2-js-client/Signer/tm2-js-signer', + 'reference/tm2-js-client/Signer/tm2-js-key', + 'reference/tm2-js-client/Signer/tm2-js-ledger', + ] + }, + ] + }, + { + type: 'category', + label: 'gno-js-client', + items: [ + 'reference/gno-js-client/gno-js-getting-started', + 'reference/gno-js-client/gno-js-provider', + 'reference/gno-js-client/gno-js-wallet', + ] + }, + ], + }, + ], +}; + +module.exports = sidebars; diff --git a/misc/docusaurus/src/css/custom.css b/misc/docusaurus/src/css/custom.css new file mode 100644 index 00000000000..a0512068943 --- /dev/null +++ b/misc/docusaurus/src/css/custom.css @@ -0,0 +1,343 @@ +@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;300;400;600&display=swap"); +/** + * Any CSS included here will be global. The classic template + * bundles Infima by default. Infima is a CSS framework designed to + * work well for content-centric websites. + */ + +/* You can override the default Infima variables here. */ +:root { + --gno-text-font: "Inter"; + --ifm-font-family-base: var(--gno-text-font), -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif; + + --ifm-color-primary: #000000; + --ifm-color-primary-light: #6f6f6f; + --ifm-color-primary-lighter: #efefef; + --ifm-color-primary-lightest: #ffffff; + --ifm-font-color-base: #6f6f6f; + --ifm-code-font-size: 95%; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); + --ifm-global-radius: 1rem; + + --ring-offset-shadow: 0 0 #0000; + --ring-shadow: 0 0 #0000; + --shadow: 14px 26px 100px -6px rgba(0, 0, 0, 0.09); + + --ifm-color-content: var(--ifm-color-primary-light); + --ifm-font-color-base: var(--ifm-color-primary-light); + --ifm-heading-color: var(--ifm-color-primary); + --ifm-background-color: var(--ifm-color-primary-lightest); + --ifm-navbar-background-color: var(--ifm-color-primary-lightest); + --ifm-menu-color: var(--ifm-color-primary-light); + --ifm-code-background-color: var(--ifm-color-primary-lighter); + --ifm-toc-background-color: var(--ifm-color-primary-lightest); + --ifm-links-background-color: var(--ifm-color-primary-lightest); + --ifm-links-hover-background-color: var(--ifm-color-primary-light); + --ifm-color-secondary: var(--ifm-color-primary-light); + --ifm-color-white: var(--ifm-color-primary); + + --ifm-menu-link-sublist-icon: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zLjk2OTY3IDE1LjUzMDNDMy42NzY3OCAxNS4yMzc0IDMuNjc2NzggMTQuNzYyNiAzLjk2OTY3IDE0LjQ2OTdMMTEuNDY5NyA2Ljk2OTY3QzExLjc2MjYgNi42NzY3OCAxMi4yMzc0IDYuNjc2NzggMTIuNTMwMyA2Ljk2OTY3TDIwLjAzMDMgMTQuNDY5N0MyMC4zMjMyIDE0Ljc2MjYgMjAuMzIzMiAxNS4yMzc0IDIwLjAzMDMgMTUuNTMwM0MxOS43Mzc0IDE1LjgyMzIgMTkuMjYyNiAxNS44MjMyIDE4Ljk2OTcgMTUuNTMwM0wxMiA4LjU2MDY2TDUuMDMwMzMgMTUuNTMwM0M0LjczNzQ0IDE1LjgyMzIgNC4yNjI1NiAxNS44MjMyIDMuOTY5NjcgMTUuNTMwM1oiIGZpbGw9IiMxQzFDMUMiLz4KPC9zdmc+Cg=="); + --content-max-w: 50rem; +} + +/* For readability concerns, you should choose a lighter palette in dark mode. */ +[data-theme="dark"]:root { + --ifm-color-primary: white; + --ifm-color-content-secondary: #a8a8a8; + --ifm-color-primary-dark: #282828; + --ifm-color-primary-darker: #1c1c1c; + --ifm-color-primary-light: #a8a8a8; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); + + --ifm-color-content: var(--ifm-color-primary-light); + --ifm-font-color-base: var(--ifm-color-primary-light); + --ifm-heading-color: var(--ifm-color-primary); + --ifm-background-color: var(--ifm-color-primary-dark); + --ifm-navbar-background-color: var(--ifm-color-primary-darker); + --ifm-menu-color: var(--ifm-color-primary-light); + --ifm-code-background-color: var(--ifm-color-primary-darker); + --ifm-toc-background-color: var(--ifm-color-primary-darker); + --ifm-links-background-color: var(--ifm-color-primary-darker); + --ifm-links-hover-background-color: var(--ifm-color-primary-light); +} + +/* Generic */ +body { + margin-left: auto; + margin-right: auto; + max-width: 100rem; + padding-inline: 1rem; +} + +pre code { + background-color: var(--ifm-code-background-color); + border-radius: 1rem; +} + +/* Header */ +.navbar { + top: 1rem; + border-radius: 1rem; + box-shadow: var(--ring-offset-shadow, 0 0 #0000), var(--ring-shadow, 0 0 #0000), var(--shadow); +} + +body nav[class*="navbarHidden"] { + transform: translate3d(0, calc(-100% - 2px - 1rem), 0) !important; +} + +.container article a { + color: var(--ifm-color-content); + text-decoration: underline; + transition: color 125ms; +} +.container article a:hover { + color: var(--ifm-color-primary); +} + +.navbar__items--right .navbar__link { + margin-block-start: 0.3rem; +} + +/* Main */ +.main-wrapper { + margin-block-start: 2rem; + transition: filter 0.2s ease; + will-change: filter; +} + +@media (min-width: 997px) { + .main-wrapper { + margin-block-start: 0; + } + .main-wrapper .container { + margin-block-start: 4.5rem; + } + + .main-wrapper .container > div > div:first-child { + padding-inline: 4rem 2rem; + } +} + +.theme-doc-markdown, +.theme-doc-footer { + max-width: var(--content-max-w, 700px); +} + +.navbar-sidebar--show ~ .main-wrapper { + filter: blur(2px); +} + +/* Links */ +[class*="iconExternalLink_node_modules"] { + display: none; +} + +.pagination-nav { + grid-template-columns: repeat(1, 100%); + grid-template-rows: repeat(2, 1fr); + max-width: var(--content-max-w, 700px); +} +.pagination-nav__link--next { + grid-column: 1/2; +} +@media (min-width: 600px) { + .pagination-nav { + grid-template-columns: repeat(2, 1fr); + } + .pagination-nav__link--next { + grid-column: 2/3; + } +} + +.pagination-nav__link { + background-color: var(--ifm-links-background-color); + border-radius: 1rem; + border-color: transparent; + transition: 0.3s ease opacity; + box-shadow: var(--ring-offset-shadow, 0 0 #0000), var(--ring-shadow, 0 0 #0000), var(--shadow); +} + +.pagination-nav__link:hover { + opacity: 0.7; + border-color: transparent; +} + +.pagination-nav__label { + display: flex; + font-size: 1.1rem; +} + +.pagination-nav__link--next .pagination-nav__label { + justify-content: flex-end; +} +.pagination-nav__link { + position: relative; +} +.pagination-nav__link--prev { + padding-inline-start: 3rem; +} +.pagination-nav__link--next { + padding-inline-end: 3rem; +} +.pagination-nav__link--prev .pagination-nav__label::before { + content: "←"; + position: absolute; + display: block; + left: 1rem; +} +.pagination-nav__link--next .pagination-nav__label::after { + content: "→"; + position: absolute; + display: block; + right: 1rem; +} + +.pagination-nav__sublabel { + opacity: 0.7; +} + +/* Sidebar */ +@media (min-width: 997px) { + .theme-doc-sidebar-container { + border-right: none !important; + } +} + +.menu::-webkit-scrollbar { + display: none; +} +.menu { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} + +.theme-doc-sidebar-container > div > div > a { + margin-block-start: 1rem; + margin-block-end: 4rem; +} + +.menu__link--active:not(.menu__link--sublist) { + background-color: transparent; +} + +.menu__link:hover, +.menu__caret:hover, +.menu__list-item-collapsible:hover { + background-color: transparent; + color: var(--ifm-color-primary); +} + +.menu__link--sublist-caret:after { + background: var(--ifm-menu-link-sublist-icon) 50% / 1.28rem 1.28rem; +} + +/* Breadcrumb */ +.breadcrumbs__item--active .breadcrumbs__link { + background-color: var(--ifm-navbar-background-color); +} + +/* ToC */ +.theme-doc-toc-desktop { + padding-block-start: 3.5rem; + overflow-y: inherit !important; +} +.table-of-contents { + position: relative; + background-color: var(--ifm-toc-background-color); + border-radius: 1rem; + border: none !important; + box-shadow: var(--ring-offset-shadow, 0 0 #0000), var(--ring-shadow, 0 0 #0000), var(--shadow); + max-height: calc(100vh - 10rem); + overflow: scroll; + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} +.table-of-contents::-webkit-scrollbar { + display: none; +} + +.theme-doc-toc-desktop::after { + content: ""; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 2.5ch; + border-radius: 1rem; + background: linear-gradient(0deg, var(--ifm-toc-background-color) 0%, transparent); + pointer-events: none; +} + +.table-of-contents__link:hover, +.table-of-contents__link:hover code, +.table-of-contents__link--active, +.table-of-contents__link--active code { + color: var(--ifm-color-primary); + text-decoration: none; +} + +/* Footer */ +.footer { + background-color: transparent; + border-top: 1px solid var(--ifm-menu-color); + margin-block-start: 2rem; + padding-inline: 0.5rem; +} + +a.footer__link-item:after { + content: "↗"; + font-size: 0.9em; + display: inline-block; + vertical-align: top; + padding-inline-start: 0.2em; + padding-block-start: 0.2; +} +a.footer__link-item > svg { + display: none; +} + +.footer .container { + max-width: 100%; +} + +.footer__title { + margin-block-start: 1rem; +} + +@media (min-width: 997px) { + .footer .footer__col:first-child { + max-width: var(--doc-sidebar-width); + } + .footer .footer__col:nth-child(2) { + padding-inline-start: 3.75rem; + } +} + +.gno-footer__socials { + margin-block-start: 1rem; + display: flex; +} + +.gno-footer__socials > a { + margin-inline-end: 0.5rem; +} +.gno-footer__socials > a path { + fill: var(--ifm-links-hover-background-color); + transition: 0.3s ease all; +} +.gno-footer__socials > a:hover path { + fill: var(--ifm-color-primary); +} + +.gno-footer__copy { + font-size: 0.8em; +} + +.gno-header__copy { + font-size: 1em; +} +@media (min-width: 997px) { + .gno-header__copy { + font-size: 0.8em; + } +} diff --git a/misc/docusaurus/static/.nojekyll b/misc/docusaurus/static/.nojekyll new file mode 100644 index 00000000000..e69de29bb2d diff --git a/misc/docusaurus/static/img/favicon.ico b/misc/docusaurus/static/img/favicon.ico new file mode 100644 index 00000000000..78b09c04e8d Binary files /dev/null and b/misc/docusaurus/static/img/favicon.ico differ diff --git a/misc/docusaurus/static/img/gnoland.svg b/misc/docusaurus/static/img/gnoland.svg new file mode 100644 index 00000000000..41a92131774 --- /dev/null +++ b/misc/docusaurus/static/img/gnoland.svg @@ -0,0 +1,3 @@ + + + diff --git a/misc/docusaurus/static/img/gnoland_light.svg b/misc/docusaurus/static/img/gnoland_light.svg new file mode 100644 index 00000000000..cabf871336d --- /dev/null +++ b/misc/docusaurus/static/img/gnoland_light.svg @@ -0,0 +1,3 @@ + + + diff --git a/misc/docusaurus/static/img/og.jpg b/misc/docusaurus/static/img/og.jpg new file mode 100644 index 00000000000..0ece3b02bed Binary files /dev/null and b/misc/docusaurus/static/img/og.jpg differ diff --git a/misc/docusaurus/tsconfig.json b/misc/docusaurus/tsconfig.json new file mode 100644 index 00000000000..6f4756980d4 --- /dev/null +++ b/misc/docusaurus/tsconfig.json @@ -0,0 +1,7 @@ +{ + // This file is not used in compilation. It is here just for a nice editor experience. + "extends": "@tsconfig/docusaurus/tsconfig.json", + "compilerOptions": { + "baseUrl": "." + } +} diff --git a/misc/docusaurus/yarn.lock b/misc/docusaurus/yarn.lock new file mode 100644 index 00000000000..ae433d870ed --- /dev/null +++ b/misc/docusaurus/yarn.lock @@ -0,0 +1,7739 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@algolia/autocomplete-core@1.9.3": + version "1.9.3" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz#1d56482a768c33aae0868c8533049e02e8961be7" + integrity sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw== + dependencies: + "@algolia/autocomplete-plugin-algolia-insights" "1.9.3" + "@algolia/autocomplete-shared" "1.9.3" + +"@algolia/autocomplete-plugin-algolia-insights@1.9.3": + version "1.9.3" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz#9b7f8641052c8ead6d66c1623d444cbe19dde587" + integrity sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg== + dependencies: + "@algolia/autocomplete-shared" "1.9.3" + +"@algolia/autocomplete-preset-algolia@1.9.3": + version "1.9.3" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz#64cca4a4304cfcad2cf730e83067e0c1b2f485da" + integrity sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA== + dependencies: + "@algolia/autocomplete-shared" "1.9.3" + +"@algolia/autocomplete-shared@1.9.3": + version "1.9.3" + resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz#2e22e830d36f0a9cf2c0ccd3c7f6d59435b77dfa" + integrity sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ== + +"@algolia/cache-browser-local-storage@4.22.1": + version "4.22.1" + resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.22.1.tgz#14b6dc9abc9e3a304a5fffb063d15f30af1032d1" + integrity sha512-Sw6IAmOCvvP6QNgY9j+Hv09mvkvEIDKjYW8ow0UDDAxSXy664RBNQk3i/0nt7gvceOJ6jGmOTimaZoY1THmU7g== + dependencies: + "@algolia/cache-common" "4.22.1" + +"@algolia/cache-common@4.22.1": + version "4.22.1" + resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.22.1.tgz#c625dff4bc2a74e79f9aed67b4e053b0ef1b3ec1" + integrity sha512-TJMBKqZNKYB9TptRRjSUtevJeQVXRmg6rk9qgFKWvOy8jhCPdyNZV1nB3SKGufzvTVbomAukFR8guu/8NRKBTA== + +"@algolia/cache-in-memory@4.22.1": + version "4.22.1" + resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.22.1.tgz#858a3d887f521362e87d04f3943e2810226a0d71" + integrity sha512-ve+6Ac2LhwpufuWavM/aHjLoNz/Z/sYSgNIXsinGofWOysPilQZPUetqLj8vbvi+DHZZaYSEP9H5SRVXnpsNNw== + dependencies: + "@algolia/cache-common" "4.22.1" + +"@algolia/client-account@4.22.1": + version "4.22.1" + resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.22.1.tgz#a7fb8b66b9a4f0a428e1426b2561144267d76d43" + integrity sha512-k8m+oegM2zlns/TwZyi4YgCtyToackkOpE+xCaKCYfBfDtdGOaVZCM5YvGPtK+HGaJMIN/DoTL8asbM3NzHonw== + dependencies: + "@algolia/client-common" "4.22.1" + "@algolia/client-search" "4.22.1" + "@algolia/transporter" "4.22.1" + +"@algolia/client-analytics@4.22.1": + version "4.22.1" + resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.22.1.tgz#506558740b4d49b1b1e3393861f729a8ce921851" + integrity sha512-1ssi9pyxyQNN4a7Ji9R50nSdISIumMFDwKNuwZipB6TkauJ8J7ha/uO60sPJFqQyqvvI+px7RSNRQT3Zrvzieg== + dependencies: + "@algolia/client-common" "4.22.1" + "@algolia/client-search" "4.22.1" + "@algolia/requester-common" "4.22.1" + "@algolia/transporter" "4.22.1" + +"@algolia/client-common@4.22.1": + version "4.22.1" + resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.22.1.tgz#042b19c1b6157c485fa1b551349ab313944d2b05" + integrity sha512-IvaL5v9mZtm4k4QHbBGDmU3wa/mKokmqNBqPj0K7lcR8ZDKzUorhcGp/u8PkPC/e0zoHSTvRh7TRkGX3Lm7iOQ== + dependencies: + "@algolia/requester-common" "4.22.1" + "@algolia/transporter" "4.22.1" + +"@algolia/client-personalization@4.22.1": + version "4.22.1" + resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.22.1.tgz#ff088d797648224fb582e9fe5828f8087835fa3d" + integrity sha512-sl+/klQJ93+4yaqZ7ezOttMQ/nczly/3GmgZXJ1xmoewP5jmdP/X/nV5U7EHHH3hCUEHeN7X1nsIhGPVt9E1cQ== + dependencies: + "@algolia/client-common" "4.22.1" + "@algolia/requester-common" "4.22.1" + "@algolia/transporter" "4.22.1" + +"@algolia/client-search@4.22.1": + version "4.22.1" + resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.22.1.tgz#508cc6ab3d1f4e9c02735a630d4dff6fbb8514a2" + integrity sha512-yb05NA4tNaOgx3+rOxAmFztgMTtGBi97X7PC3jyNeGiwkAjOZc2QrdZBYyIdcDLoI09N0gjtpClcackoTN0gPA== + dependencies: + "@algolia/client-common" "4.22.1" + "@algolia/requester-common" "4.22.1" + "@algolia/transporter" "4.22.1" + +"@algolia/events@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@algolia/events/-/events-4.0.1.tgz#fd39e7477e7bc703d7f893b556f676c032af3950" + integrity sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ== + +"@algolia/logger-common@4.22.1": + version "4.22.1" + resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.22.1.tgz#79cf4cd295de0377a94582c6aaac59b1ded731d9" + integrity sha512-OnTFymd2odHSO39r4DSWRFETkBufnY2iGUZNrMXpIhF5cmFE8pGoINNPzwg02QLBlGSaLqdKy0bM8S0GyqPLBg== + +"@algolia/logger-console@4.22.1": + version "4.22.1" + resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.22.1.tgz#0355345f6940f67aaa78ae9b81c06e44e49f2336" + integrity sha512-O99rcqpVPKN1RlpgD6H3khUWylU24OXlzkavUAMy6QZd1776QAcauE3oP8CmD43nbaTjBexZj2nGsBH9Tc0FVA== + dependencies: + "@algolia/logger-common" "4.22.1" + +"@algolia/requester-browser-xhr@4.22.1": + version "4.22.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.22.1.tgz#f04df6fe9690a071b267c77d26b83a3be9280361" + integrity sha512-dtQGYIg6MteqT1Uay3J/0NDqD+UciHy3QgRbk7bNddOJu+p3hzjTRYESqEnoX/DpEkaNYdRHUKNylsqMpgwaEw== + dependencies: + "@algolia/requester-common" "4.22.1" + +"@algolia/requester-common@4.22.1": + version "4.22.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.22.1.tgz#27be35f3718aafcb6b388ff9c3aa2defabd559ff" + integrity sha512-dgvhSAtg2MJnR+BxrIFqlLtkLlVVhas9HgYKMk2Uxiy5m6/8HZBL40JVAMb2LovoPFs9I/EWIoFVjOrFwzn5Qg== + +"@algolia/requester-node-http@4.22.1": + version "4.22.1" + resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.22.1.tgz#589a6fa828ad0f325e727a6fcaf4e1a2343cc62b" + integrity sha512-JfmZ3MVFQkAU+zug8H3s8rZ6h0ahHZL/SpMaSasTCGYR5EEJsCc8SI5UZ6raPN2tjxa5bxS13BRpGSBUens7EA== + dependencies: + "@algolia/requester-common" "4.22.1" + +"@algolia/transporter@4.22.1": + version "4.22.1" + resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.22.1.tgz#8843841b857dc021668f31647aa557ff19cd9cb1" + integrity sha512-kzWgc2c9IdxMa3YqA6TN0NW5VrKYYW/BELIn7vnLyn+U/RFdZ4lxxt9/8yq3DKV5snvoDzzO4ClyejZRdV3lMQ== + dependencies: + "@algolia/cache-common" "4.22.1" + "@algolia/logger-common" "4.22.1" + "@algolia/requester-common" "4.22.1" + +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.8.3": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" + integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== + dependencies: + "@babel/highlight" "^7.23.4" + chalk "^2.4.2" + +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.3", "@babel/compat-data@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" + integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== + +"@babel/core@7.12.9": + version "7.12.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8" + integrity sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.5" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.5" + "@babel/parser" "^7.12.7" + "@babel/template" "^7.12.7" + "@babel/traverse" "^7.12.9" + "@babel/types" "^7.12.7" + 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/core@^7.18.6", "@babel/core@^7.19.6": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.7.tgz#4d8016e06a14b5f92530a13ed0561730b5c6483f" + integrity sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.23.7" + "@babel/parser" "^7.23.6" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.7" + "@babel/types" "^7.23.6" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.12.5", "@babel/generator@^7.18.7", "@babel/generator@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" + integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== + dependencies: + "@babel/types" "^7.23.6" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz#5426b109cf3ad47b91120f8328d8ab1be8b0b956" + integrity sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" + integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== + dependencies: + "@babel/compat-data" "^7.23.5" + "@babel/helper-validator-option" "^7.23.5" + browserslist "^4.22.2" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-create-class-features-plugin@^7.22.15", "@babel/helper-create-class-features-plugin@^7.23.6": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.7.tgz#b2e6826e0e20d337143655198b79d58fdc9bd43d" + integrity sha512-xCoqR/8+BoNnXOY7RVSgv6X+o7pmT5q1d+gGcRlXYkI+9B31glE4jeejhKVpA04O1AtzOt7OSQ6VYKP5FcRl9g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-member-expression-to-functions" "^7.23.0" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + semver "^6.3.1" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.15", "@babel/helper-create-regexp-features-plugin@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1" + integrity sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + regexpu-core "^5.3.1" + semver "^6.3.1" + +"@babel/helper-define-polyfill-provider@^0.4.4": + version "0.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.4.tgz#64df615451cb30e94b59a9696022cffac9a10088" + integrity sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA== + dependencies: + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-function-name@^7.22.5", "@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-member-expression-to-functions@^7.22.15", "@babel/helper-member-expression-to-functions@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz#9263e88cc5e41d39ec18c9a3e0eced59a3e7d366" + integrity sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA== + dependencies: + "@babel/types" "^7.23.0" + +"@babel/helper-module-imports@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" + integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-plugin-utils@7.10.4": + 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-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + +"@babel/helper-remap-async-to-generator@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" + integrity sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-wrap-function" "^7.22.20" + +"@babel/helper-replace-supers@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz#e37d367123ca98fe455a9887734ed2e16eb7a793" + integrity sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-member-expression-to-functions" "^7.22.15" + "@babel/helper-optimise-call-expression" "^7.22.5" + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" + integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/helper-validator-option@^7.22.15", "@babel/helper-validator-option@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== + +"@babel/helper-wrap-function@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz#15352b0b9bfb10fc9c76f79f6342c00e3411a569" + integrity sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw== + dependencies: + "@babel/helper-function-name" "^7.22.5" + "@babel/template" "^7.22.15" + "@babel/types" "^7.22.19" + +"@babel/helpers@^7.12.5", "@babel/helpers@^7.23.7": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.8.tgz#fc6b2d65b16847fd50adddbd4232c76378959e34" + integrity sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ== + dependencies: + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.7" + "@babel/types" "^7.23.6" + +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.12.7", "@babel/parser@^7.18.8", "@babel/parser@^7.22.15", "@babel/parser@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b" + integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz#5cd1c87ba9380d0afb78469292c954fee5d2411a" + integrity sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz#f6652bb16b94f8f9c20c50941e16e9756898dc5d" + integrity sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.23.3" + +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.23.7": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz#516462a95d10a9618f197d39ad291a9b47ae1d7b" + integrity sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-proposal-object-rest-spread@7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz#def9bd03cea0f9b72283dac0ec22d289c7691069" + integrity sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.12.1" + +"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": + version "7.21.0-placeholder-for-preset-env.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== + +"@babel/plugin-syntax-async-generators@^7.8.4": + 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.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@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-import-assertions@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz#9c05a7f592982aff1a2768260ad84bcd3f0c77fc" + integrity sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-attributes@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz#992aee922cf04512461d7dae3ff6951b90a2dc06" + integrity sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-meta@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + 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.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz#9d9d357cc818aa7ae7935917c1257f67677a0926" + integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-jsx@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473" + integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@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.3": + 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.3", "@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": + 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.3": + 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.3": + 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-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz#24f460c85dbbc983cd2b9c4994178bcc01df958f" + integrity sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-arrow-functions@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz#94c6dcfd731af90f27a79509f9ab7fb2120fc38b" + integrity sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-async-generator-functions@^7.23.7": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.7.tgz#3aa0b4f2fa3788b5226ef9346cf6d16ec61f99cd" + integrity sha512-PdxEpL71bJp1byMG0va5gwQcXHxuEYC/BgI/e88mGTtohbZN28O5Yit0Plkkm/dBzCF/BxmbNcses1RH1T+urA== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.20" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-transform-async-to-generator@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz#d1f513c7a8a506d43f47df2bf25f9254b0b051fa" + integrity sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw== + dependencies: + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.20" + +"@babel/plugin-transform-block-scoped-functions@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz#fe1177d715fb569663095e04f3598525d98e8c77" + integrity sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-block-scoping@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz#b2d38589531c6c80fbe25e6b58e763622d2d3cf5" + integrity sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-class-properties@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz#35c377db11ca92a785a718b6aa4e3ed1eb65dc48" + integrity sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-class-static-block@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz#2a202c8787a8964dd11dfcedf994d36bfc844ab5" + integrity sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-transform-classes@^7.23.8": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz#d08ae096c240347badd68cdf1b6d1624a6435d92" + integrity sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" + "@babel/helper-split-export-declaration" "^7.22.6" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz#652e69561fcc9d2b50ba4f7ac7f60dcf65e86474" + integrity sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/template" "^7.22.15" + +"@babel/plugin-transform-destructuring@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz#8c9ee68228b12ae3dff986e56ed1ba4f3c446311" + integrity sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-dotall-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz#3f7af6054882ede89c378d0cf889b854a993da50" + integrity sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-duplicate-keys@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz#664706ca0a5dfe8d066537f99032fc1dc8b720ce" + integrity sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-dynamic-import@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz#c7629e7254011ac3630d47d7f34ddd40ca535143" + integrity sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-transform-exponentiation-operator@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz#ea0d978f6b9232ba4722f3dbecdd18f450babd18" + integrity sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-export-namespace-from@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz#084c7b25e9a5c8271e987a08cf85807b80283191" + integrity sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-transform-for-of@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz#81c37e24171b37b370ba6aaffa7ac86bcb46f94e" + integrity sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + +"@babel/plugin-transform-function-name@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz#8f424fcd862bf84cb9a1a6b42bc2f47ed630f8dc" + integrity sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw== + dependencies: + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-json-strings@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz#a871d9b6bd171976efad2e43e694c961ffa3714d" + integrity sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-transform-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz#8214665f00506ead73de157eba233e7381f3beb4" + integrity sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-logical-assignment-operators@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz#e599f82c51d55fac725f62ce55d3a0886279ecb5" + integrity sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-transform-member-expression-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz#e37b3f0502289f477ac0e776b05a833d853cabcc" + integrity sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-modules-amd@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz#e19b55436a1416829df0a1afc495deedfae17f7d" + integrity sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw== + dependencies: + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-modules-commonjs@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz#661ae831b9577e52be57dd8356b734f9700b53b4" + integrity sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA== + dependencies: + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + +"@babel/plugin-transform-modules-systemjs@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz#fa7e62248931cb15b9404f8052581c302dd9de81" + integrity sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ== + dependencies: + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + +"@babel/plugin-transform-modules-umd@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz#5d4395fccd071dfefe6585a4411aa7d6b7d769e9" + integrity sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg== + dependencies: + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" + integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-new-target@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz#5491bb78ed6ac87e990957cea367eab781c4d980" + integrity sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-nullish-coalescing-operator@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz#45556aad123fc6e52189ea749e33ce090637346e" + integrity sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-transform-numeric-separator@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz#03d08e3691e405804ecdd19dd278a40cca531f29" + integrity sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-transform-object-rest-spread@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz#2b9c2d26bf62710460bdc0d1730d4f1048361b83" + integrity sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g== + dependencies: + "@babel/compat-data" "^7.23.3" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.23.3" + +"@babel/plugin-transform-object-super@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz#81fdb636dcb306dd2e4e8fd80db5b2362ed2ebcd" + integrity sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" + +"@babel/plugin-transform-optional-catch-binding@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz#318066de6dacce7d92fa244ae475aa8d91778017" + integrity sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-transform-optional-chaining@^7.23.3", "@babel/plugin-transform-optional-chaining@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz#6acf61203bdfc4de9d4e52e64490aeb3e52bd017" + integrity sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz#83ef5d1baf4b1072fa6e54b2b0999a7b2527e2af" + integrity sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-private-methods@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz#b2d7a3c97e278bfe59137a978d53b2c2e038c0e4" + integrity sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-private-property-in-object@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz#3ec711d05d6608fd173d9b8de39872d8dbf68bf5" + integrity sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-transform-property-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz#54518f14ac4755d22b92162e4a852d308a560875" + integrity sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-react-constant-elements@^7.18.12": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.23.3.tgz#5efc001d07ef0f7da0d73c3a86c132f73d28e43c" + integrity sha512-zP0QKq/p6O42OL94udMgSfKXyse4RyJ0JqbQ34zDAONWjyrEsghYEyTSK5FIpmXmCpB55SHokL1cRRKHv8L2Qw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-react-display-name@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz#70529f034dd1e561045ad3c8152a267f0d7b6200" + integrity sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-react-jsx-development@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz#e716b6edbef972a92165cd69d92f1255f7e73e87" + integrity sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.22.5" + +"@babel/plugin-transform-react-jsx@^7.22.15", "@babel/plugin-transform-react-jsx@^7.22.5": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312" + integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.23.3" + "@babel/types" "^7.23.4" + +"@babel/plugin-transform-react-pure-annotations@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.23.3.tgz#fabedbdb8ee40edf5da96f3ecfc6958e3783b93c" + integrity sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-regenerator@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz#141afd4a2057298602069fce7f2dc5173e6c561c" + integrity sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + regenerator-transform "^0.15.2" + +"@babel/plugin-transform-reserved-words@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz#4130dcee12bd3dd5705c587947eb715da12efac8" + integrity sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-runtime@^7.18.6": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.7.tgz#52bbd20054855beb9deae3bee9ceb05289c343e6" + integrity sha512-fa0hnfmiXc9fq/weK34MUV0drz2pOL/vfKWvN7Qw127hiUPabFCUMgAbYWcchRzMJit4o5ARsK/s+5h0249pLw== + dependencies: + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + babel-plugin-polyfill-corejs2 "^0.4.7" + babel-plugin-polyfill-corejs3 "^0.8.7" + babel-plugin-polyfill-regenerator "^0.5.4" + semver "^6.3.1" + +"@babel/plugin-transform-shorthand-properties@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz#97d82a39b0e0c24f8a981568a8ed851745f59210" + integrity sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-spread@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz#41d17aacb12bde55168403c6f2d6bdca563d362c" + integrity sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + +"@babel/plugin-transform-sticky-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz#dec45588ab4a723cb579c609b294a3d1bd22ff04" + integrity sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-template-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz#5f0f028eb14e50b5d0f76be57f90045757539d07" + integrity sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-typeof-symbol@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz#9dfab97acc87495c0c449014eb9c547d8966bca4" + integrity sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-typescript@^7.23.3": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz#aa36a94e5da8d94339ae3a4e22d40ed287feb34c" + integrity sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.23.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-typescript" "^7.23.3" + +"@babel/plugin-transform-unicode-escapes@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz#1f66d16cab01fab98d784867d24f70c1ca65b925" + integrity sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-property-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz#19e234129e5ffa7205010feec0d94c251083d7ad" + integrity sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz#26897708d8f42654ca4ce1b73e96140fbad879dc" + integrity sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-sets-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz#4fb6f0a719c2c5859d11f6b55a050cc987f3799e" + integrity sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/preset-env@^7.18.6", "@babel/preset-env@^7.19.4": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.23.8.tgz#7d6f8171ea7c221ecd28059e65ad37c20e441e3e" + integrity sha512-lFlpmkApLkEP6woIKprO6DO60RImpatTQKtz4sUcDjVcK8M8mQ4sZsuxaTMNOZf0sqAq/ReYW1ZBHnOQwKpLWA== + dependencies: + "@babel/compat-data" "^7.23.5" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.23.5" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.23.3" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.23.3" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.23.7" + "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.23.3" + "@babel/plugin-syntax-import-attributes" "^7.23.3" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.23.3" + "@babel/plugin-transform-async-generator-functions" "^7.23.7" + "@babel/plugin-transform-async-to-generator" "^7.23.3" + "@babel/plugin-transform-block-scoped-functions" "^7.23.3" + "@babel/plugin-transform-block-scoping" "^7.23.4" + "@babel/plugin-transform-class-properties" "^7.23.3" + "@babel/plugin-transform-class-static-block" "^7.23.4" + "@babel/plugin-transform-classes" "^7.23.8" + "@babel/plugin-transform-computed-properties" "^7.23.3" + "@babel/plugin-transform-destructuring" "^7.23.3" + "@babel/plugin-transform-dotall-regex" "^7.23.3" + "@babel/plugin-transform-duplicate-keys" "^7.23.3" + "@babel/plugin-transform-dynamic-import" "^7.23.4" + "@babel/plugin-transform-exponentiation-operator" "^7.23.3" + "@babel/plugin-transform-export-namespace-from" "^7.23.4" + "@babel/plugin-transform-for-of" "^7.23.6" + "@babel/plugin-transform-function-name" "^7.23.3" + "@babel/plugin-transform-json-strings" "^7.23.4" + "@babel/plugin-transform-literals" "^7.23.3" + "@babel/plugin-transform-logical-assignment-operators" "^7.23.4" + "@babel/plugin-transform-member-expression-literals" "^7.23.3" + "@babel/plugin-transform-modules-amd" "^7.23.3" + "@babel/plugin-transform-modules-commonjs" "^7.23.3" + "@babel/plugin-transform-modules-systemjs" "^7.23.3" + "@babel/plugin-transform-modules-umd" "^7.23.3" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" + "@babel/plugin-transform-new-target" "^7.23.3" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.23.4" + "@babel/plugin-transform-numeric-separator" "^7.23.4" + "@babel/plugin-transform-object-rest-spread" "^7.23.4" + "@babel/plugin-transform-object-super" "^7.23.3" + "@babel/plugin-transform-optional-catch-binding" "^7.23.4" + "@babel/plugin-transform-optional-chaining" "^7.23.4" + "@babel/plugin-transform-parameters" "^7.23.3" + "@babel/plugin-transform-private-methods" "^7.23.3" + "@babel/plugin-transform-private-property-in-object" "^7.23.4" + "@babel/plugin-transform-property-literals" "^7.23.3" + "@babel/plugin-transform-regenerator" "^7.23.3" + "@babel/plugin-transform-reserved-words" "^7.23.3" + "@babel/plugin-transform-shorthand-properties" "^7.23.3" + "@babel/plugin-transform-spread" "^7.23.3" + "@babel/plugin-transform-sticky-regex" "^7.23.3" + "@babel/plugin-transform-template-literals" "^7.23.3" + "@babel/plugin-transform-typeof-symbol" "^7.23.3" + "@babel/plugin-transform-unicode-escapes" "^7.23.3" + "@babel/plugin-transform-unicode-property-regex" "^7.23.3" + "@babel/plugin-transform-unicode-regex" "^7.23.3" + "@babel/plugin-transform-unicode-sets-regex" "^7.23.3" + "@babel/preset-modules" "0.1.6-no-external-plugins" + babel-plugin-polyfill-corejs2 "^0.4.7" + babel-plugin-polyfill-corejs3 "^0.8.7" + babel-plugin-polyfill-regenerator "^0.5.4" + core-js-compat "^3.31.0" + semver "^6.3.1" + +"@babel/preset-modules@0.1.6-no-external-plugins": + version "0.1.6-no-external-plugins" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" + integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-react@^7.18.6": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.23.3.tgz#f73ca07e7590f977db07eb54dbe46538cc015709" + integrity sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.15" + "@babel/plugin-transform-react-display-name" "^7.23.3" + "@babel/plugin-transform-react-jsx" "^7.22.15" + "@babel/plugin-transform-react-jsx-development" "^7.22.5" + "@babel/plugin-transform-react-pure-annotations" "^7.23.3" + +"@babel/preset-typescript@^7.18.6": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz#14534b34ed5b6d435aa05f1ae1c5e7adcc01d913" + integrity sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.15" + "@babel/plugin-syntax-jsx" "^7.23.3" + "@babel/plugin-transform-modules-commonjs" "^7.23.3" + "@babel/plugin-transform-typescript" "^7.23.3" + +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" + integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== + +"@babel/runtime-corejs3@^7.18.6": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.23.8.tgz#b8aa3d47570bdd08fed77fdfd69542118af0df26" + integrity sha512-2ZzmcDugdm0/YQKFVYsXiwUN7USPX8PM7cytpb4PFl87fM+qYPSvTZX//8tyeJB1j0YDmafBJEbl5f8NfLyuKw== + dependencies: + core-js-pure "^3.30.2" + regenerator-runtime "^0.14.0" + +"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.6", "@babel/runtime@^7.20.13", "@babel/runtime@^7.8.4": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.8.tgz#8ee6fe1ac47add7122902f257b8ddf55c898f650" + integrity sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/template@^7.12.7", "@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.12.9", "@babel/traverse@^7.18.8", "@babel/traverse@^7.23.7": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305" + integrity sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.6" + "@babel/types" "^7.23.6" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.12.7", "@babel/types@^7.20.0", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.23.6", "@babel/types@^7.4.4": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.6.tgz#be33fdb151e1f5a56877d704492c240fc71c7ccd" + integrity sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg== + dependencies: + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + +"@discoveryjs/json-ext@0.5.7": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@docsearch/css@3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docsearch/css/-/css-3.5.2.tgz#610f47b48814ca94041df969d9fcc47b91fc5aac" + integrity sha512-SPiDHaWKQZpwR2siD0KQUwlStvIAnEyK6tAE2h2Wuoq8ue9skzhlyVQ1ddzOxX6khULnAALDiR/isSF3bnuciA== + +"@docsearch/react@^3.1.1": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@docsearch/react/-/react-3.5.2.tgz#2e6bbee00eb67333b64906352734da6aef1232b9" + integrity sha512-9Ahcrs5z2jq/DcAvYtvlqEBHImbm4YJI8M9y0x6Tqg598P40HTEkX7hsMcIuThI+hTFxRGZ9hll0Wygm2yEjng== + dependencies: + "@algolia/autocomplete-core" "1.9.3" + "@algolia/autocomplete-preset-algolia" "1.9.3" + "@docsearch/css" "3.5.2" + algoliasearch "^4.19.1" + +"@docusaurus/core@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.4.1.tgz#4b8ff5766131ce3fbccaad0b1daf2ad4dc76f62d" + integrity sha512-SNsY7PshK3Ri7vtsLXVeAJGS50nJN3RgF836zkyUfAD01Fq+sAk5EwWgLw+nnm5KVNGDu7PRR2kRGDsWvqpo0g== + dependencies: + "@babel/core" "^7.18.6" + "@babel/generator" "^7.18.7" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-transform-runtime" "^7.18.6" + "@babel/preset-env" "^7.18.6" + "@babel/preset-react" "^7.18.6" + "@babel/preset-typescript" "^7.18.6" + "@babel/runtime" "^7.18.6" + "@babel/runtime-corejs3" "^7.18.6" + "@babel/traverse" "^7.18.8" + "@docusaurus/cssnano-preset" "2.4.1" + "@docusaurus/logger" "2.4.1" + "@docusaurus/mdx-loader" "2.4.1" + "@docusaurus/react-loadable" "5.5.2" + "@docusaurus/utils" "2.4.1" + "@docusaurus/utils-common" "2.4.1" + "@docusaurus/utils-validation" "2.4.1" + "@slorber/static-site-generator-webpack-plugin" "^4.0.7" + "@svgr/webpack" "^6.2.1" + autoprefixer "^10.4.7" + babel-loader "^8.2.5" + babel-plugin-dynamic-import-node "^2.3.3" + boxen "^6.2.1" + chalk "^4.1.2" + chokidar "^3.5.3" + clean-css "^5.3.0" + cli-table3 "^0.6.2" + combine-promises "^1.1.0" + commander "^5.1.0" + copy-webpack-plugin "^11.0.0" + core-js "^3.23.3" + css-loader "^6.7.1" + css-minimizer-webpack-plugin "^4.0.0" + cssnano "^5.1.12" + del "^6.1.1" + detect-port "^1.3.0" + escape-html "^1.0.3" + eta "^2.0.0" + file-loader "^6.2.0" + fs-extra "^10.1.0" + html-minifier-terser "^6.1.0" + html-tags "^3.2.0" + html-webpack-plugin "^5.5.0" + import-fresh "^3.3.0" + leven "^3.1.0" + lodash "^4.17.21" + mini-css-extract-plugin "^2.6.1" + postcss "^8.4.14" + postcss-loader "^7.0.0" + prompts "^2.4.2" + react-dev-utils "^12.0.1" + react-helmet-async "^1.3.0" + react-loadable "npm:@docusaurus/react-loadable@5.5.2" + react-loadable-ssr-addon-v5-slorber "^1.0.1" + react-router "^5.3.3" + react-router-config "^5.1.1" + react-router-dom "^5.3.3" + rtl-detect "^1.0.4" + semver "^7.3.7" + serve-handler "^6.1.3" + shelljs "^0.8.5" + terser-webpack-plugin "^5.3.3" + tslib "^2.4.0" + update-notifier "^5.1.0" + url-loader "^4.1.1" + wait-on "^6.0.1" + webpack "^5.73.0" + webpack-bundle-analyzer "^4.5.0" + webpack-dev-server "^4.9.3" + webpack-merge "^5.8.0" + webpackbar "^5.0.2" + +"@docusaurus/cssnano-preset@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-2.4.1.tgz#eacadefb1e2e0f59df3467a0fe83e4ff79eed163" + integrity sha512-ka+vqXwtcW1NbXxWsh6yA1Ckii1klY9E53cJ4O9J09nkMBgrNX3iEFED1fWdv8wf4mJjvGi5RLZ2p9hJNjsLyQ== + dependencies: + cssnano-preset-advanced "^5.3.8" + postcss "^8.4.14" + postcss-sort-media-queries "^4.2.1" + tslib "^2.4.0" + +"@docusaurus/logger@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-2.4.1.tgz#4d2c0626b40752641f9fdd93ad9b5a7a0792f767" + integrity sha512-5h5ysIIWYIDHyTVd8BjheZmQZmEgWDR54aQ1BX9pjFfpyzFo5puKXKYrYJXbjEHGyVhEzmB9UXwbxGfaZhOjcg== + dependencies: + chalk "^4.1.2" + tslib "^2.4.0" + +"@docusaurus/mdx-loader@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-2.4.1.tgz#6425075d7fc136dbfdc121349060cedd64118393" + integrity sha512-4KhUhEavteIAmbBj7LVFnrVYDiU51H5YWW1zY6SmBSte/YLhDutztLTBE0PQl1Grux1jzUJeaSvAzHpTn6JJDQ== + dependencies: + "@babel/parser" "^7.18.8" + "@babel/traverse" "^7.18.8" + "@docusaurus/logger" "2.4.1" + "@docusaurus/utils" "2.4.1" + "@mdx-js/mdx" "^1.6.22" + escape-html "^1.0.3" + file-loader "^6.2.0" + fs-extra "^10.1.0" + image-size "^1.0.1" + mdast-util-to-string "^2.0.0" + remark-emoji "^2.2.0" + stringify-object "^3.3.0" + tslib "^2.4.0" + unified "^9.2.2" + unist-util-visit "^2.0.3" + url-loader "^4.1.1" + webpack "^5.73.0" + +"@docusaurus/module-type-aliases@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-2.4.1.tgz#38b3c2d2ae44bea6d57506eccd84280216f0171c" + integrity sha512-gLBuIFM8Dp2XOCWffUDSjtxY7jQgKvYujt7Mx5s4FCTfoL5dN1EVbnrn+O2Wvh8b0a77D57qoIDY7ghgmatR1A== + dependencies: + "@docusaurus/react-loadable" "5.5.2" + "@docusaurus/types" "2.4.1" + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router-config" "*" + "@types/react-router-dom" "*" + react-helmet-async "*" + react-loadable "npm:@docusaurus/react-loadable@5.5.2" + +"@docusaurus/plugin-content-blog@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-2.4.1.tgz#c705a8b1a36a34f181dcf43b7770532e4dcdc4a3" + integrity sha512-E2i7Knz5YIbE1XELI6RlTnZnGgS52cUO4BlCiCUCvQHbR+s1xeIWz4C6BtaVnlug0Ccz7nFSksfwDpVlkujg5Q== + dependencies: + "@docusaurus/core" "2.4.1" + "@docusaurus/logger" "2.4.1" + "@docusaurus/mdx-loader" "2.4.1" + "@docusaurus/types" "2.4.1" + "@docusaurus/utils" "2.4.1" + "@docusaurus/utils-common" "2.4.1" + "@docusaurus/utils-validation" "2.4.1" + cheerio "^1.0.0-rc.12" + feed "^4.2.2" + fs-extra "^10.1.0" + lodash "^4.17.21" + reading-time "^1.5.0" + tslib "^2.4.0" + unist-util-visit "^2.0.3" + utility-types "^3.10.0" + webpack "^5.73.0" + +"@docusaurus/plugin-content-docs@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-2.4.1.tgz#ed94d9721b5ce7a956fb01cc06c40d8eee8dfca7" + integrity sha512-Lo7lSIcpswa2Kv4HEeUcGYqaasMUQNpjTXpV0N8G6jXgZaQurqp7E8NGYeGbDXnb48czmHWbzDL4S3+BbK0VzA== + dependencies: + "@docusaurus/core" "2.4.1" + "@docusaurus/logger" "2.4.1" + "@docusaurus/mdx-loader" "2.4.1" + "@docusaurus/module-type-aliases" "2.4.1" + "@docusaurus/types" "2.4.1" + "@docusaurus/utils" "2.4.1" + "@docusaurus/utils-validation" "2.4.1" + "@types/react-router-config" "^5.0.6" + combine-promises "^1.1.0" + fs-extra "^10.1.0" + import-fresh "^3.3.0" + js-yaml "^4.1.0" + lodash "^4.17.21" + tslib "^2.4.0" + utility-types "^3.10.0" + webpack "^5.73.0" + +"@docusaurus/plugin-content-pages@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-2.4.1.tgz#c534f7e49967699a45bbe67050d1605ebbf3d285" + integrity sha512-/UjuH/76KLaUlL+o1OvyORynv6FURzjurSjvn2lbWTFc4tpYY2qLYTlKpTCBVPhlLUQsfyFnshEJDLmPneq2oA== + dependencies: + "@docusaurus/core" "2.4.1" + "@docusaurus/mdx-loader" "2.4.1" + "@docusaurus/types" "2.4.1" + "@docusaurus/utils" "2.4.1" + "@docusaurus/utils-validation" "2.4.1" + fs-extra "^10.1.0" + tslib "^2.4.0" + webpack "^5.73.0" + +"@docusaurus/plugin-debug@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-2.4.1.tgz#461a2c77b0c5a91b2c05257c8f9585412aaa59dc" + integrity sha512-7Yu9UPzRShlrH/G8btOpR0e6INFZr0EegWplMjOqelIwAcx3PKyR8mgPTxGTxcqiYj6hxSCRN0D8R7YrzImwNA== + dependencies: + "@docusaurus/core" "2.4.1" + "@docusaurus/types" "2.4.1" + "@docusaurus/utils" "2.4.1" + fs-extra "^10.1.0" + react-json-view "^1.21.3" + tslib "^2.4.0" + +"@docusaurus/plugin-google-analytics@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-2.4.1.tgz#30de1c35773bf9d52bb2d79b201b23eb98022613" + integrity sha512-dyZJdJiCoL+rcfnm0RPkLt/o732HvLiEwmtoNzOoz9MSZz117UH2J6U2vUDtzUzwtFLIf32KkeyzisbwUCgcaQ== + dependencies: + "@docusaurus/core" "2.4.1" + "@docusaurus/types" "2.4.1" + "@docusaurus/utils-validation" "2.4.1" + tslib "^2.4.0" + +"@docusaurus/plugin-google-gtag@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.4.1.tgz#6a3eb91022714735e625c7ca70ef5188fa7bd0dc" + integrity sha512-mKIefK+2kGTQBYvloNEKtDmnRD7bxHLsBcxgnbt4oZwzi2nxCGjPX6+9SQO2KCN5HZbNrYmGo5GJfMgoRvy6uA== + dependencies: + "@docusaurus/core" "2.4.1" + "@docusaurus/types" "2.4.1" + "@docusaurus/utils-validation" "2.4.1" + tslib "^2.4.0" + +"@docusaurus/plugin-google-tag-manager@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-2.4.1.tgz#b99f71aec00b112bbf509ef2416e404a95eb607e" + integrity sha512-Zg4Ii9CMOLfpeV2nG74lVTWNtisFaH9QNtEw48R5QE1KIwDBdTVaiSA18G1EujZjrzJJzXN79VhINSbOJO/r3g== + dependencies: + "@docusaurus/core" "2.4.1" + "@docusaurus/types" "2.4.1" + "@docusaurus/utils-validation" "2.4.1" + tslib "^2.4.0" + +"@docusaurus/plugin-sitemap@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.4.1.tgz#8a7a76ed69dc3e6b4474b6abb10bb03336a9de6d" + integrity sha512-lZx+ijt/+atQ3FVE8FOHV/+X3kuok688OydDXrqKRJyXBJZKgGjA2Qa8RjQ4f27V2woaXhtnyrdPop/+OjVMRg== + dependencies: + "@docusaurus/core" "2.4.1" + "@docusaurus/logger" "2.4.1" + "@docusaurus/types" "2.4.1" + "@docusaurus/utils" "2.4.1" + "@docusaurus/utils-common" "2.4.1" + "@docusaurus/utils-validation" "2.4.1" + fs-extra "^10.1.0" + sitemap "^7.1.1" + tslib "^2.4.0" + +"@docusaurus/preset-classic@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-2.4.1.tgz#072f22d0332588e9c5f512d4bded8d7c99f91497" + integrity sha512-P4//+I4zDqQJ+UDgoFrjIFaQ1MeS9UD1cvxVQaI6O7iBmiHQm0MGROP1TbE7HlxlDPXFJjZUK3x3cAoK63smGQ== + dependencies: + "@docusaurus/core" "2.4.1" + "@docusaurus/plugin-content-blog" "2.4.1" + "@docusaurus/plugin-content-docs" "2.4.1" + "@docusaurus/plugin-content-pages" "2.4.1" + "@docusaurus/plugin-debug" "2.4.1" + "@docusaurus/plugin-google-analytics" "2.4.1" + "@docusaurus/plugin-google-gtag" "2.4.1" + "@docusaurus/plugin-google-tag-manager" "2.4.1" + "@docusaurus/plugin-sitemap" "2.4.1" + "@docusaurus/theme-classic" "2.4.1" + "@docusaurus/theme-common" "2.4.1" + "@docusaurus/theme-search-algolia" "2.4.1" + "@docusaurus/types" "2.4.1" + +"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2": + version "5.5.2" + resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce" + integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ== + dependencies: + "@types/react" "*" + prop-types "^15.6.2" + +"@docusaurus/theme-classic@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-2.4.1.tgz#0060cb263c1a73a33ac33f79bb6bc2a12a56ad9e" + integrity sha512-Rz0wKUa+LTW1PLXmwnf8mn85EBzaGSt6qamqtmnh9Hflkc+EqiYMhtUJeLdV+wsgYq4aG0ANc+bpUDpsUhdnwg== + dependencies: + "@docusaurus/core" "2.4.1" + "@docusaurus/mdx-loader" "2.4.1" + "@docusaurus/module-type-aliases" "2.4.1" + "@docusaurus/plugin-content-blog" "2.4.1" + "@docusaurus/plugin-content-docs" "2.4.1" + "@docusaurus/plugin-content-pages" "2.4.1" + "@docusaurus/theme-common" "2.4.1" + "@docusaurus/theme-translations" "2.4.1" + "@docusaurus/types" "2.4.1" + "@docusaurus/utils" "2.4.1" + "@docusaurus/utils-common" "2.4.1" + "@docusaurus/utils-validation" "2.4.1" + "@mdx-js/react" "^1.6.22" + clsx "^1.2.1" + copy-text-to-clipboard "^3.0.1" + infima "0.2.0-alpha.43" + lodash "^4.17.21" + nprogress "^0.2.0" + postcss "^8.4.14" + prism-react-renderer "^1.3.5" + prismjs "^1.28.0" + react-router-dom "^5.3.3" + rtlcss "^3.5.0" + tslib "^2.4.0" + utility-types "^3.10.0" + +"@docusaurus/theme-common@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-2.4.1.tgz#03e16f7aa96455e952f3243ac99757b01a3c83d4" + integrity sha512-G7Zau1W5rQTaFFB3x3soQoZpkgMbl/SYNG8PfMFIjKa3M3q8n0m/GRf5/H/e5BqOvt8c+ZWIXGCiz+kUCSHovA== + dependencies: + "@docusaurus/mdx-loader" "2.4.1" + "@docusaurus/module-type-aliases" "2.4.1" + "@docusaurus/plugin-content-blog" "2.4.1" + "@docusaurus/plugin-content-docs" "2.4.1" + "@docusaurus/plugin-content-pages" "2.4.1" + "@docusaurus/utils" "2.4.1" + "@docusaurus/utils-common" "2.4.1" + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router-config" "*" + clsx "^1.2.1" + parse-numeric-range "^1.3.0" + prism-react-renderer "^1.3.5" + tslib "^2.4.0" + use-sync-external-store "^1.2.0" + utility-types "^3.10.0" + +"@docusaurus/theme-search-algolia@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-2.4.1.tgz#906bd2cca3fced0241985ef502c892f58ff380fc" + integrity sha512-6BcqW2lnLhZCXuMAvPRezFs1DpmEKzXFKlYjruuas+Xy3AQeFzDJKTJFIm49N77WFCTyxff8d3E4Q9pi/+5McQ== + dependencies: + "@docsearch/react" "^3.1.1" + "@docusaurus/core" "2.4.1" + "@docusaurus/logger" "2.4.1" + "@docusaurus/plugin-content-docs" "2.4.1" + "@docusaurus/theme-common" "2.4.1" + "@docusaurus/theme-translations" "2.4.1" + "@docusaurus/utils" "2.4.1" + "@docusaurus/utils-validation" "2.4.1" + algoliasearch "^4.13.1" + algoliasearch-helper "^3.10.0" + clsx "^1.2.1" + eta "^2.0.0" + fs-extra "^10.1.0" + lodash "^4.17.21" + tslib "^2.4.0" + utility-types "^3.10.0" + +"@docusaurus/theme-translations@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-2.4.1.tgz#4d49df5865dae9ef4b98a19284ede62ae6f98726" + integrity sha512-T1RAGP+f86CA1kfE8ejZ3T3pUU3XcyvrGMfC/zxCtc2BsnoexuNI9Vk2CmuKCb+Tacvhxjv5unhxXce0+NKyvA== + dependencies: + fs-extra "^10.1.0" + tslib "^2.4.0" + +"@docusaurus/types@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-2.4.1.tgz#d8e82f9e0f704984f98df1f93d6b4554d5458705" + integrity sha512-0R+cbhpMkhbRXX138UOc/2XZFF8hiZa6ooZAEEJFp5scytzCw4tC1gChMFXrpa3d2tYE6AX8IrOEpSonLmfQuQ== + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + commander "^5.1.0" + joi "^17.6.0" + react-helmet-async "^1.3.0" + utility-types "^3.10.0" + webpack "^5.73.0" + webpack-merge "^5.8.0" + +"@docusaurus/utils-common@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-2.4.1.tgz#7f72e873e49bd5179588869cc3ab7449a56aae63" + integrity sha512-bCVGdZU+z/qVcIiEQdyx0K13OC5mYwxhSuDUR95oFbKVuXYRrTVrwZIqQljuo1fyJvFTKHiL9L9skQOPokuFNQ== + dependencies: + tslib "^2.4.0" + +"@docusaurus/utils-validation@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-2.4.1.tgz#19959856d4a886af0c5cfb357f4ef68b51151244" + integrity sha512-unII3hlJlDwZ3w8U+pMO3Lx3RhI4YEbY3YNsQj4yzrkZzlpqZOLuAiZK2JyULnD+TKbceKU0WyWkQXtYbLNDFA== + dependencies: + "@docusaurus/logger" "2.4.1" + "@docusaurus/utils" "2.4.1" + joi "^17.6.0" + js-yaml "^4.1.0" + tslib "^2.4.0" + +"@docusaurus/utils@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-2.4.1.tgz#9c5f76eae37b71f3819c1c1f0e26e6807c99a4fc" + integrity sha512-1lvEZdAQhKNht9aPXPoh69eeKnV0/62ROhQeFKKxmzd0zkcuE/Oc5Gpnt00y/f5bIsmOsYMY7Pqfm/5rteT5GA== + dependencies: + "@docusaurus/logger" "2.4.1" + "@svgr/webpack" "^6.2.1" + escape-string-regexp "^4.0.0" + file-loader "^6.2.0" + fs-extra "^10.1.0" + github-slugger "^1.4.0" + globby "^11.1.0" + gray-matter "^4.0.3" + js-yaml "^4.1.0" + lodash "^4.17.21" + micromatch "^4.0.5" + resolve-pathname "^3.0.0" + shelljs "^0.8.5" + tslib "^2.4.0" + url-loader "^4.1.1" + webpack "^5.73.0" + +"@hapi/hoek@^9.0.0": + version "9.3.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" + integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== + +"@hapi/topo@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.21" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz#5dc1df7b3dc4a6209e503a924e1ca56097a2bb15" + integrity sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" + integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== + +"@mdx-js/mdx@^1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba" + integrity sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA== + dependencies: + "@babel/core" "7.12.9" + "@babel/plugin-syntax-jsx" "7.12.1" + "@babel/plugin-syntax-object-rest-spread" "7.8.3" + "@mdx-js/util" "1.6.22" + babel-plugin-apply-mdx-type-prop "1.6.22" + babel-plugin-extract-import-names "1.6.22" + camelcase-css "2.0.1" + detab "2.0.4" + hast-util-raw "6.0.1" + lodash.uniq "4.5.0" + mdast-util-to-hast "10.0.1" + remark-footnotes "2.0.0" + remark-mdx "1.6.22" + remark-parse "8.0.3" + remark-squeeze-paragraphs "4.0.0" + style-to-object "0.3.0" + unified "9.2.0" + unist-builder "2.0.3" + unist-util-visit "2.0.3" + +"@mdx-js/react@^1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-1.6.22.tgz#ae09b4744fddc74714ee9f9d6f17a66e77c43573" + integrity sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg== + +"@mdx-js/util@1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b" + integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@polka/url@^1.0.0-next.24": + version "1.0.0-next.24" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.24.tgz#58601079e11784d20f82d0585865bb42305c4df3" + integrity sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ== + +"@sideway/address@^4.1.3": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" + integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@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== + +"@slorber/static-site-generator-webpack-plugin@^4.0.7": + version "4.0.7" + resolved "https://registry.yarnpkg.com/@slorber/static-site-generator-webpack-plugin/-/static-site-generator-webpack-plugin-4.0.7.tgz#fc1678bddefab014e2145cbe25b3ce4e1cfc36f3" + integrity sha512-Ug7x6z5lwrz0WqdnNFOMYrDQNTPAprvHLSh6+/fmml3qUiz6l5eq+2MzLKWtn/q5K5NpSiFsZTP/fck/3vjSxA== + dependencies: + eval "^0.1.8" + p-map "^4.0.0" + webpack-sources "^3.2.2" + +"@svgr/babel-plugin-add-jsx-attribute@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz#74a5d648bd0347bda99d82409d87b8ca80b9a1ba" + integrity sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ== + +"@svgr/babel-plugin-remove-jsx-attribute@*": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz#69177f7937233caca3a1afb051906698f2f59186" + integrity sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA== + +"@svgr/babel-plugin-remove-jsx-empty-expression@*": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz#c2c48104cfd7dcd557f373b70a56e9e3bdae1d44" + integrity sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA== + +"@svgr/babel-plugin-replace-jsx-attribute-value@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz#fb9d22ea26d2bc5e0a44b763d4c46d5d3f596c60" + integrity sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg== + +"@svgr/babel-plugin-svg-dynamic-title@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz#01b2024a2b53ffaa5efceaa0bf3e1d5a4c520ce4" + integrity sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw== + +"@svgr/babel-plugin-svg-em-dimensions@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz#dd3fa9f5b24eb4f93bcf121c3d40ff5facecb217" + integrity sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA== + +"@svgr/babel-plugin-transform-react-native-svg@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz#1d8e945a03df65b601551097d8f5e34351d3d305" + integrity sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg== + +"@svgr/babel-plugin-transform-svg-component@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz#48620b9e590e25ff95a80f811544218d27f8a250" + integrity sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ== + +"@svgr/babel-preset@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-6.5.1.tgz#b90de7979c8843c5c580c7e2ec71f024b49eb828" + integrity sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "^6.5.1" + "@svgr/babel-plugin-remove-jsx-attribute" "*" + "@svgr/babel-plugin-remove-jsx-empty-expression" "*" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^6.5.1" + "@svgr/babel-plugin-svg-dynamic-title" "^6.5.1" + "@svgr/babel-plugin-svg-em-dimensions" "^6.5.1" + "@svgr/babel-plugin-transform-react-native-svg" "^6.5.1" + "@svgr/babel-plugin-transform-svg-component" "^6.5.1" + +"@svgr/core@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-6.5.1.tgz#d3e8aa9dbe3fbd747f9ee4282c1c77a27410488a" + integrity sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw== + dependencies: + "@babel/core" "^7.19.6" + "@svgr/babel-preset" "^6.5.1" + "@svgr/plugin-jsx" "^6.5.1" + camelcase "^6.2.0" + cosmiconfig "^7.0.1" + +"@svgr/hast-util-to-babel-ast@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz#81800bd09b5bcdb968bf6ee7c863d2288fdb80d2" + integrity sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw== + dependencies: + "@babel/types" "^7.20.0" + entities "^4.4.0" + +"@svgr/plugin-jsx@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz#0e30d1878e771ca753c94e69581c7971542a7072" + integrity sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw== + dependencies: + "@babel/core" "^7.19.6" + "@svgr/babel-preset" "^6.5.1" + "@svgr/hast-util-to-babel-ast" "^6.5.1" + svg-parser "^2.0.4" + +"@svgr/plugin-svgo@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz#0f91910e988fc0b842f88e0960c2862e022abe84" + integrity sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ== + dependencies: + cosmiconfig "^7.0.1" + deepmerge "^4.2.2" + svgo "^2.8.0" + +"@svgr/webpack@^6.2.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-6.5.1.tgz#ecf027814fc1cb2decc29dc92f39c3cf691e40e8" + integrity sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA== + dependencies: + "@babel/core" "^7.19.6" + "@babel/plugin-transform-react-constant-elements" "^7.18.12" + "@babel/preset-env" "^7.19.4" + "@babel/preset-react" "^7.18.6" + "@babel/preset-typescript" "^7.18.6" + "@svgr/core" "^6.5.1" + "@svgr/plugin-jsx" "^6.5.1" + "@svgr/plugin-svgo" "^6.5.1" + +"@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" + +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + +"@tsconfig/docusaurus@^1.0.5": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@tsconfig/docusaurus/-/docusaurus-1.0.7.tgz#a3ee3c8109b3fec091e3d61a61834e563aeee3c3" + integrity sha512-ffTXxGIP/IRMCjuzHd6M4/HdIrw1bMfC7Bv8hMkTadnePkpe0lG0oDSdbRpSDZb2rQMAgpbWiR10BvxvNYwYrg== + +"@types/body-parser@*": + version "1.19.5" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" + integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/bonjour@^3.5.9": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" + integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== + dependencies: + "@types/node" "*" + +"@types/connect-history-api-fallback@^1.3.5": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" + integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.3": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.56.2" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.2.tgz#1c72a9b794aa26a8b94ad26d5b9aa51c8a6384bb" + integrity sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": + version "4.17.41" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz#5077defa630c2e8d28aa9ffc2c01c157c305bef6" + integrity sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*", "@types/express@^4.17.13": + version "4.17.21" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" + integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/hast@^2.0.0": + version "2.3.9" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.9.tgz#a9a1b5bbce46e8a1312e977364bacabc8e93d2cf" + integrity sha512-pTHyNlaMD/oKJmS+ZZUyFUcsZeBZpC0lmGquw98CqRVNgAdJZJeD7GoeLiT6Xbx5rU9VCjSt0RwEvDgzh4obFw== + dependencies: + "@types/unist" "^2" + +"@types/history@^4.7.11": + version "4.7.11" + resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64" + integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA== + +"@types/html-minifier-terser@^6.0.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" + integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== + +"@types/http-errors@*": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" + integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== + +"@types/http-proxy@^1.17.8": + version "1.17.14" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.14.tgz#57f8ccaa1c1c3780644f8a94f9c6b5000b5e2eec" + integrity sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + +"@types/istanbul-lib-report@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/mdast@^3.0.0": + version "3.0.15" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.15.tgz#49c524a263f30ffa28b71ae282f813ed000ab9f5" + integrity sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ== + dependencies: + "@types/unist" "^2" + +"@types/mime@*": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45" + integrity sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw== + +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== + +"@types/node-forge@^1.3.0": + version "1.3.11" + resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" + integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "20.11.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.0.tgz#8e0b99e70c0c1ade1a86c4a282f7b7ef87c9552f" + integrity sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ== + dependencies: + undici-types "~5.26.4" + +"@types/node@^17.0.5": + version "17.0.45" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" + integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== + +"@types/parse-json@^4.0.0": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" + integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== + +"@types/parse5@^5.0.0": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" + integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== + +"@types/prop-types@*": + version "15.7.11" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563" + integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng== + +"@types/qs@*": + version "6.9.11" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.11.tgz#208d8a30bc507bd82e03ada29e4732ea46a6bbda" + integrity sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ== + +"@types/range-parser@*": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + +"@types/react-router-config@*", "@types/react-router-config@^5.0.6": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@types/react-router-config/-/react-router-config-5.0.11.tgz#2761a23acc7905a66a94419ee40294a65aaa483a" + integrity sha512-WmSAg7WgqW7m4x8Mt4N6ZyKz0BubSj/2tVUMsAHp+Yd2AMwcSbeFq9WympT19p5heCFmF97R9eD5uUR/t4HEqw== + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router" "^5.1.0" + +"@types/react-router-dom@*": + version "5.3.3" + resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83" + integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw== + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + "@types/react-router" "*" + +"@types/react-router@*", "@types/react-router@^5.1.0": + version "5.1.20" + resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.20.tgz#88eccaa122a82405ef3efbcaaa5dcdd9f021387c" + integrity sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q== + dependencies: + "@types/history" "^4.7.11" + "@types/react" "*" + +"@types/react@*": + version "18.2.47" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.47.tgz#85074b27ab563df01fbc3f68dc64bf7050b0af40" + integrity sha512-xquNkkOirwyCgoClNk85BjP+aqnIS+ckAJ8i37gAbDs14jfW/J23f2GItAf33oiUPQnqNMALiFeoM9Y5mbjpVQ== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/retry@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" + integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== + +"@types/sax@^1.2.1": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/sax/-/sax-1.2.7.tgz#ba5fe7df9aa9c89b6dff7688a19023dd2963091d" + integrity sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A== + dependencies: + "@types/node" "*" + +"@types/scheduler@*": + version "0.16.8" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" + integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== + +"@types/send@*": + version "0.17.4" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" + integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-index@^1.9.1": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" + integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== + dependencies: + "@types/express" "*" + +"@types/serve-static@*", "@types/serve-static@^1.13.10": + version "1.15.5" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.5.tgz#15e67500ec40789a1e8c9defc2d32a896f05b033" + integrity sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ== + dependencies: + "@types/http-errors" "*" + "@types/mime" "*" + "@types/node" "*" + +"@types/sockjs@^0.3.33": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" + integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== + dependencies: + "@types/node" "*" + +"@types/unist@^2", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.10.tgz#04ffa7f406ab628f7f7e97ca23e290cd8ab15efc" + integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA== + +"@types/ws@^8.5.5": + version "8.5.10" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" + integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== + dependencies: + "@types/node" "*" + +"@types/yargs-parser@*": + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== + +"@types/yargs@^17.0.8": + version "17.0.32" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" + integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== + dependencies: + "@types/yargs-parser" "*" + +"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" + integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + +"@webassemblyjs/helper-buffer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" + integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== + +"@webassemblyjs/helper-wasm-section@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" + integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" + integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-opt" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/wast-printer" "1.11.6" + +"@webassemblyjs/wasm-gen@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" + integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" + integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + +"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" + integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" + integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@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== + +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn-walk@^8.0.0: + version "8.3.2" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" + integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== + +acorn@^8.0.4, acorn@^8.7.1, acorn@^8.8.2: + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + +address@^1.0.1, address@^1.1.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e" + integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.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-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + 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" + +ajv@^8.0.0, ajv@^8.9.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +algoliasearch-helper@^3.10.0: + version "3.16.1" + resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.16.1.tgz#421e3554ec86e14e60e7e0bf796aef61cf4a06ec" + integrity sha512-qxAHVjjmT7USVvrM8q6gZGaJlCK1fl4APfdAA7o8O6iXEc68G0xMNrzRkxoB/HmhhvyHnoteS/iMTiHiTcQQcg== + dependencies: + "@algolia/events" "^4.0.1" + +algoliasearch@^4.13.1, algoliasearch@^4.19.1: + version "4.22.1" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.22.1.tgz#f10fbecdc7654639ec20d62f109c1b3a46bc6afc" + integrity sha512-jwydKFQJKIx9kIZ8Jm44SdpigFwRGPESaxZBaHSV0XWN2yBJAOT4mT7ppvlrpA4UGzz92pqFnVKr/kaZXrcreg== + dependencies: + "@algolia/cache-browser-local-storage" "4.22.1" + "@algolia/cache-common" "4.22.1" + "@algolia/cache-in-memory" "4.22.1" + "@algolia/client-account" "4.22.1" + "@algolia/client-analytics" "4.22.1" + "@algolia/client-common" "4.22.1" + "@algolia/client-personalization" "4.22.1" + "@algolia/client-search" "4.22.1" + "@algolia/logger-common" "4.22.1" + "@algolia/logger-console" "4.22.1" + "@algolia/requester-browser-xhr" "4.22.1" + "@algolia/requester-common" "4.22.1" + "@algolia/requester-node-http" "4.22.1" + "@algolia/transporter" "4.22.1" + +ansi-align@^3.0.0, ansi-align@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + +ansi-html-community@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" + integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +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.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +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" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +autoprefixer@^10.4.12, autoprefixer@^10.4.7: + version "10.4.16" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.16.tgz#fad1411024d8670880bdece3970aa72e3572feb8" + integrity sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ== + dependencies: + browserslist "^4.21.10" + caniuse-lite "^1.0.30001538" + fraction.js "^4.3.6" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + +axios@^0.25.0: + version "0.25.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a" + integrity sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g== + dependencies: + follow-redirects "^1.14.7" + +babel-loader@^8.2.5: + version "8.3.0" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.3.0.tgz#124936e841ba4fe8176786d6ff28add1f134d6a8" + integrity sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q== + dependencies: + find-cache-dir "^3.3.1" + loader-utils "^2.0.0" + make-dir "^3.1.0" + schema-utils "^2.6.5" + +babel-plugin-apply-mdx-type-prop@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz#d216e8fd0de91de3f1478ef3231e05446bc8705b" + integrity sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ== + dependencies: + "@babel/helper-plugin-utils" "7.10.4" + "@mdx-js/util" "1.6.22" + +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-plugin-extract-import-names@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz#de5f9a28eb12f3eb2578bf74472204e66d1a13dc" + integrity sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ== + dependencies: + "@babel/helper-plugin-utils" "7.10.4" + +babel-plugin-polyfill-corejs2@^0.4.7: + version "0.4.7" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.7.tgz#679d1b94bf3360f7682e11f2cb2708828a24fe8c" + integrity sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ== + dependencies: + "@babel/compat-data" "^7.22.6" + "@babel/helper-define-polyfill-provider" "^0.4.4" + semver "^6.3.1" + +babel-plugin-polyfill-corejs3@^0.8.7: + version "0.8.7" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.7.tgz#941855aa7fdaac06ed24c730a93450d2b2b76d04" + integrity sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.4.4" + core-js-compat "^3.33.1" + +babel-plugin-polyfill-regenerator@^0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.4.tgz#c6fc8eab610d3a11eb475391e52584bacfc020f4" + integrity sha512-S/x2iOCvDaCASLYsOOgWOq4bCfKYVqvO/uxjkaYyZ3rVsVE3CeAI/c84NpyuBBymEgNvHgjEot3a9/Z/kXvqsg== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.4.4" + +bail@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" + integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base16@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70" + integrity sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ== + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== + +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@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +bonjour-service@^1.0.11: + version "1.2.1" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.2.1.tgz#eb41b3085183df3321da1264719fbada12478d02" + integrity sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw== + dependencies: + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.5" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +boxen@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + +boxen@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-6.2.1.tgz#b098a2278b2cd2845deef2dff2efc38d329b434d" + integrity sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw== + dependencies: + ansi-align "^3.0.1" + camelcase "^6.2.0" + chalk "^4.1.2" + cli-boxes "^3.0.0" + string-width "^5.0.1" + type-fest "^2.5.0" + widest-line "^4.0.1" + wrap-ansi "^8.0.1" + +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@^3.0.2, 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" + +browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.18.1, browserslist@^4.21.10, browserslist@^4.21.4, browserslist@^4.22.2: + version "4.22.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b" + integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== + dependencies: + caniuse-lite "^1.0.30001565" + electron-to-chromium "^1.4.601" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +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-bind@^1.0.0, call-bind@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== + dependencies: + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +camelcase-css@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +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.30001538, caniuse-lite@^1.0.30001565: + version "1.0.30001576" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz#893be772cf8ee6056d6c1e2d07df365b9ec0a5c4" + integrity sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg== + +ccount@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" + integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== + +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, chalk@^4.1.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +character-entities-legacy@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" + integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== + +character-entities@^1.0.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" + integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== + +character-reference-invalid@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" + integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== + +cheerio-select@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4" + integrity sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g== + dependencies: + boolbase "^1.0.0" + css-select "^5.1.0" + css-what "^6.1.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.0.1" + +cheerio@^1.0.0-rc.12: + version "1.0.0-rc.12" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.12.tgz#788bf7466506b1c6bf5fae51d24a2c4d62e47683" + integrity sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q== + dependencies: + cheerio-select "^2.1.0" + dom-serializer "^2.0.0" + domhandler "^5.0.3" + domutils "^3.0.1" + htmlparser2 "^8.0.1" + parse5 "^7.0.0" + parse5-htmlparser2-tree-adapter "^7.0.0" + +chokidar@^3.4.2, chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +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== + +ci-info@^3.2.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + +clean-css@^5.2.2, clean-css@^5.3.0: + version "5.3.3" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd" + integrity sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg== + dependencies: + source-map "~0.6.0" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + +cli-boxes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145" + integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g== + +cli-table3@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.3.tgz#61ab765aac156b52f222954ffc607a6f01dbeeb2" + integrity sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clone-response@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== + dependencies: + mimic-response "^1.0.0" + +clsx@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + +collapse-white-space@^1.0.2: + version "1.0.6" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" + integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== + +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 sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +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== + +colord@^2.9.1: + version "2.9.3" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + +colorette@^2.0.10: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +combine-promises@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/combine-promises/-/combine-promises-1.2.0.tgz#5f2e68451862acf85761ded4d9e2af7769c2ca6a" + integrity sha512-VcQB1ziGD0NXrhKxiwyNbCDmRzs/OShMs2GqW2DlU2A/Sd0nQxE1oWDAE5O0ygSx5mgQOn9eIFh7yKPgFRVkPQ== + +comma-separated-tokens@^1.0.0: + version "1.0.8" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" + integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== + +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@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + +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 sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +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@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + +consola@^2.15.3: + version "2.15.3" + resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" + integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +convert-source-map@^1.7.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + +copy-text-to-clipboard@^3.0.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz#0202b2d9bdae30a49a53f898626dcc3b49ad960b" + integrity sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q== + +copy-webpack-plugin@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz#96d4dbdb5f73d02dd72d0528d1958721ab72e04a" + integrity sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ== + dependencies: + fast-glob "^3.2.11" + glob-parent "^6.0.1" + globby "^13.1.1" + normalize-path "^3.0.0" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + +core-js-compat@^3.31.0, core-js-compat@^3.33.1: + version "3.35.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.35.0.tgz#c149a3d1ab51e743bc1da61e39cb51f461a41873" + integrity sha512-5blwFAddknKeNgsjBzilkdQ0+YK8L1PfqPYq40NOYMYFSS38qj+hpTcLLWwpIwA2A5bje/x5jmVn2tzUMg9IVw== + dependencies: + browserslist "^4.22.2" + +core-js-pure@^3.30.2: + version "3.35.0" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.35.0.tgz#4660033304a050215ae82e476bd2513a419fbb34" + integrity sha512-f+eRYmkou59uh7BPcyJ8MC76DiGhspj1KMxVIcF24tzP8NA9HVa1uC7BTW2tgx7E1QVCzDzsgp7kArrzhlz8Ew== + +core-js@^3.23.3: + version "3.35.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.35.0.tgz#58e651688484f83c34196ca13f099574ee53d6b4" + integrity sha512-ntakECeqg81KqMueeGJ79Q5ZgQNR+6eaE8sxGCx62zMbAIj65q+uYvatToew3m6eAGdU4gNZwpZ34NMe4GYswg== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cosmiconfig@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" + integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.7.2" + +cosmiconfig@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +cosmiconfig@^8.3.5: + version "8.3.6" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== + dependencies: + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + path-type "^4.0.0" + +cross-fetch@^3.1.5: + version "3.1.8" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" + integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== + dependencies: + node-fetch "^2.6.12" + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +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-declaration-sorter@^6.3.1: + version "6.4.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz#28beac7c20bad7f1775be3a7129d7eae409a3a71" + integrity sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g== + +css-loader@^6.7.1: + version "6.9.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.9.0.tgz#0cc2f14df94ed97c526c5ae42b6b13916d1d8d0e" + integrity sha512-3I5Nu4ytWlHvOP6zItjiHlefBNtrH+oehq8tnQa2kO305qpVyx9XNIT1CXIj5bgCJs7qICBCkgCYxQLKPANoLA== + dependencies: + icss-utils "^5.1.0" + postcss "^8.4.31" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.3" + postcss-modules-scope "^3.1.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.2.0" + semver "^7.5.4" + +css-minimizer-webpack-plugin@^4.0.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz#79f6199eb5adf1ff7ba57f105e3752d15211eb35" + integrity sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA== + dependencies: + cssnano "^5.1.8" + jest-worker "^29.1.2" + postcss "^8.4.17" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + source-map "^0.6.1" + +css-select@^4.1.3: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + +css-tree@^1.1.2, css-tree@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + +css-what@^6.0.1, css-what@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + +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-advanced@^5.3.8: + version "5.3.10" + resolved "https://registry.yarnpkg.com/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.10.tgz#25558a1fbf3a871fb6429ce71e41be7f5aca6eef" + integrity sha512-fnYJyCS9jgMU+cmHO1rPSPf9axbQyD7iUhLO5Df6O4G+fKIOMps+ZbU0PdGFejFBBZ3Pftf18fn1eG7MAPUSWQ== + dependencies: + autoprefixer "^10.4.12" + cssnano-preset-default "^5.2.14" + postcss-discard-unused "^5.1.0" + postcss-merge-idents "^5.1.1" + postcss-reduce-idents "^5.2.0" + postcss-zindex "^5.1.0" + +cssnano-preset-default@^5.2.14: + version "5.2.14" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz#309def4f7b7e16d71ab2438052093330d9ab45d8" + integrity sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A== + dependencies: + css-declaration-sorter "^6.3.1" + cssnano-utils "^3.1.0" + postcss-calc "^8.2.3" + postcss-colormin "^5.3.1" + postcss-convert-values "^5.1.3" + postcss-discard-comments "^5.1.2" + postcss-discard-duplicates "^5.1.0" + postcss-discard-empty "^5.1.1" + postcss-discard-overridden "^5.1.0" + postcss-merge-longhand "^5.1.7" + postcss-merge-rules "^5.1.4" + postcss-minify-font-values "^5.1.0" + postcss-minify-gradients "^5.1.1" + postcss-minify-params "^5.1.4" + postcss-minify-selectors "^5.2.1" + postcss-normalize-charset "^5.1.0" + postcss-normalize-display-values "^5.1.0" + postcss-normalize-positions "^5.1.1" + postcss-normalize-repeat-style "^5.1.1" + postcss-normalize-string "^5.1.0" + postcss-normalize-timing-functions "^5.1.0" + postcss-normalize-unicode "^5.1.1" + postcss-normalize-url "^5.1.0" + postcss-normalize-whitespace "^5.1.1" + postcss-ordered-values "^5.1.3" + postcss-reduce-initial "^5.1.2" + postcss-reduce-transforms "^5.1.0" + postcss-svgo "^5.1.0" + postcss-unique-selectors "^5.1.1" + +cssnano-utils@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz#95684d08c91511edfc70d2636338ca37ef3a6861" + integrity sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA== + +cssnano@^5.1.12, cssnano@^5.1.8: + version "5.1.15" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.15.tgz#ded66b5480d5127fcb44dac12ea5a983755136bf" + integrity sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw== + dependencies: + cssnano-preset-default "^5.2.14" + lilconfig "^2.0.3" + yaml "^1.10.2" + +csso@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" + integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== + dependencies: + css-tree "^1.1.2" + +csstype@^3.0.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + +debounce@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" + integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== + +debug@2.6.9, debug@^2.6.0: + 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@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA== + dependencies: + mimic-response "^1.0.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@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" + integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== + dependencies: + execa "^5.0.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-data-property@^1.0.1, define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +del@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" + integrity sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg== + dependencies: + globby "^11.0.1" + graceful-fs "^4.2.4" + is-glob "^4.0.1" + is-path-cwd "^2.2.0" + is-path-inside "^3.0.2" + p-map "^4.0.0" + rimraf "^3.0.2" + slash "^3.0.0" + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detab@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detab/-/detab-2.0.4.tgz#b927892069aff405fbb9a186fe97a44a92a94b43" + integrity sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g== + dependencies: + repeat-string "^1.5.4" + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +detect-port-alt@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" + integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== + dependencies: + address "^1.0.1" + debug "^2.6.0" + +detect-port@^1.3.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.5.1.tgz#451ca9b6eaf20451acb0799b8ab40dff7718727b" + integrity sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ== + dependencies: + address "^1.0.1" + debug "4" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dns-packet@^5.2.2: + version "5.6.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" + integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + +dom-converter@^0.2.0: + 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@^1.0.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== + dependencies: + domelementtype "^2.2.0" + +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^2.5.2, domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +domutils@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e" + integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +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.5" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e" + integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA== + +duplexer@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.601: + version "1.4.629" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.629.tgz#9cbffe1b08a5627b6a25118360f7fd3965416caf" + integrity sha512-5UUkr3k3CZ/k+9Sw7vaaIMyOzMC0XbPyprKI3n0tbKDqkzTDOjK4izm7DxlkueRMim6ZZQ1ja9F7hoFVplHihA== + +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== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +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== + +emoticon@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/emoticon/-/emoticon-3.2.0.tgz#c008ca7d7620fac742fe1bf4af8ff8fed154ae7f" + integrity sha512-SNujglcLTTg+lDAcApPNgEdudaqQFiAbJCqzjNxJkvN9vAwCGi0uu8IUVvx+f16h+V44KCY6Y2yboroc9pilHg== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +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@^5.15.0: + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +entities@^4.2.0, entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +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-module-lexer@^1.2.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.4.1.tgz#41ea21b43908fe6a287ffcbe4300f790555331f5" + integrity sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w== + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +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 sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +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 sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +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== + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +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.3.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.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +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== + +eta@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/eta/-/eta-2.2.0.tgz#eb8b5f8c4e8b6306561a455e62cd7492fe3a9b8a" + integrity sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eval@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/eval/-/eval-0.1.8.tgz#2b903473b8cc1d1989b83a1e7923f883eb357f85" + integrity sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw== + dependencies: + "@types/node" "*" + require-like ">= 0.1.1" + +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@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +express@^4.17.3: + version "4.18.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.1" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + 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 sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== + dependencies: + is-extendable "^0.1.0" + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + 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@^3.2.11, fast-glob@^3.2.9, fast-glob@^3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +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-url-parser@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" + integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ== + dependencies: + punycode "^1.3.2" + +fastq@^1.6.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.16.0.tgz#83b9a9375692db77a822df081edb6a9cf6839320" + integrity sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA== + dependencies: + reusify "^1.0.4" + +faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +fbemitter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/fbemitter/-/fbemitter-3.0.0.tgz#00b2a1af5411254aab416cd75f9e6289bee4bff3" + integrity sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw== + dependencies: + fbjs "^3.0.0" + +fbjs-css-vars@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" + integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== + +fbjs@^3.0.0, fbjs@^3.0.1: + version "3.0.5" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.5.tgz#aa0edb7d5caa6340011790bd9249dbef8a81128d" + integrity sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg== + dependencies: + cross-fetch "^3.1.5" + fbjs-css-vars "^1.0.0" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^1.0.35" + +feed@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/feed/-/feed-4.2.2.tgz#865783ef6ed12579e2c44bbef3c9113bc4956a7e" + integrity sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ== + dependencies: + xml-js "^1.6.11" + +file-loader@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" + integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +filesize@^8.0.6: + version "8.0.7" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-8.0.7.tgz#695e70d80f4e47012c132d57a059e80c6b580bd8" + integrity sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ== + +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.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-cache-dir@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.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@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.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" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flux@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/flux/-/flux-4.0.4.tgz#9661182ea81d161ee1a6a6af10d20485ef2ac572" + integrity sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw== + dependencies: + fbemitter "^3.0.0" + fbjs "^3.0.1" + +follow-redirects@^1.0.0, follow-redirects@^1.14.7: + version "1.15.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== + +fork-ts-checker-webpack-plugin@^6.5.0: + version "6.5.3" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz#eda2eff6e22476a2688d10661688c47f611b37f3" + integrity sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ== + dependencies: + "@babel/code-frame" "^7.8.3" + "@types/json-schema" "^7.0.5" + chalk "^4.1.0" + chokidar "^3.4.2" + cosmiconfig "^6.0.0" + deepmerge "^4.2.2" + fs-extra "^9.0.0" + glob "^7.1.6" + memfs "^3.1.2" + minimatch "^3.0.4" + schema-utils "2.7.0" + semver "^7.3.2" + tapable "^1.0.0" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fraction.js@^4.3.6: + version "4.3.7" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" + integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-monkey@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.5.tgz#fe450175f0db0d7ea758102e1d84096acb925788" + integrity sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== + +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-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +github-slugger@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.5.0.tgz#17891bbc73232051474d68bd867a34625c955f7d" + integrity sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw== + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^7.0.0, glob@^7.1.3, glob@^7.1.6: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-dirs@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485" + integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA== + dependencies: + ini "2.0.0" + +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +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@^11.0.1, globby@^11.0.4, globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +globby@^13.1.1: + version "13.2.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" + integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.3.0" + ignore "^5.2.4" + merge2 "^1.4.1" + slash "^4.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +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.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +gray-matter@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798" + integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q== + dependencies: + js-yaml "^3.13.1" + kind-of "^6.0.2" + section-matter "^1.0.0" + strip-bom-string "^1.0.0" + +gzip-size@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" + integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== + dependencies: + duplexer "^0.1.2" + +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== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +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-property-descriptors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== + dependencies: + get-intrinsic "^1.2.2" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +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== + +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + +hast-to-hyperscript@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz#9b67fd188e4c81e8ad66f803855334173920218d" + integrity sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA== + dependencies: + "@types/unist" "^2.0.3" + comma-separated-tokens "^1.0.0" + property-information "^5.3.0" + space-separated-tokens "^1.0.0" + style-to-object "^0.3.0" + unist-util-is "^4.0.0" + web-namespaces "^1.0.0" + +hast-util-from-parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz#554e34abdeea25ac76f5bd950a1f0180e0b3bc2a" + integrity sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA== + dependencies: + "@types/parse5" "^5.0.0" + hastscript "^6.0.0" + property-information "^5.0.0" + vfile "^4.0.0" + vfile-location "^3.2.0" + web-namespaces "^1.0.0" + +hast-util-parse-selector@^2.0.0: + version "2.2.5" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz#d57c23f4da16ae3c63b3b6ca4616683313499c3a" + integrity sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ== + +hast-util-raw@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-6.0.1.tgz#973b15930b7529a7b66984c98148b46526885977" + integrity sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig== + dependencies: + "@types/hast" "^2.0.0" + hast-util-from-parse5 "^6.0.0" + hast-util-to-parse5 "^6.0.0" + html-void-elements "^1.0.0" + parse5 "^6.0.0" + unist-util-position "^3.0.0" + vfile "^4.0.0" + web-namespaces "^1.0.0" + xtend "^4.0.0" + zwitch "^1.0.0" + +hast-util-to-parse5@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz#1ec44650b631d72952066cea9b1445df699f8479" + integrity sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ== + dependencies: + hast-to-hyperscript "^9.0.0" + property-information "^5.0.0" + web-namespaces "^1.0.0" + xtend "^4.0.0" + zwitch "^1.0.0" + +hastscript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" + integrity sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w== + dependencies: + "@types/hast" "^2.0.0" + comma-separated-tokens "^1.0.0" + hast-util-parse-selector "^2.0.0" + property-information "^5.0.0" + space-separated-tokens "^1.0.0" + +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== + +history@^4.9.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" + integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== + dependencies: + "@babel/runtime" "^7.1.2" + loose-envify "^1.2.0" + resolve-pathname "^3.0.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + value-equal "^1.0.1" + +hoist-non-react-statics@^3.1.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-entities@^2.3.2: + version "2.4.0" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.4.0.tgz#edd0cee70402584c8c76cc2c0556db09d1f45061" + integrity sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ== + +html-escaper@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +html-minifier-terser@^6.0.2, html-minifier-terser@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" + integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw== + dependencies: + camel-case "^4.1.2" + clean-css "^5.2.2" + commander "^8.3.0" + he "^1.2.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.10.0" + +html-tags@^3.2.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" + integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== + +html-void-elements@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" + integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w== + +html-webpack-plugin@^5.5.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz#50a8fa6709245608cb00e811eacecb8e0d7b7ea0" + integrity sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw== + dependencies: + "@types/html-minifier-terser" "^6.0.0" + html-minifier-terser "^6.0.2" + lodash "^4.17.21" + pretty-error "^4.0.0" + tapable "^2.0.0" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +htmlparser2@^8.0.1: + version "8.0.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.2.tgz#f002151705b383e62433b5cf466f5b716edaec21" + integrity sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.0.1" + entities "^4.4.0" + +http-cache-semantics@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.8" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" + integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== + +http-proxy-middleware@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + 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" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +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-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + +ignore@^5.2.0, ignore@^5.2.4: + version "5.3.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" + integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== + +image-size@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.1.1.tgz#ddd67d4dc340e52ac29ce5f546a09f4e29e840ac" + integrity sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ== + dependencies: + queue "6.0.2" + +immer@^9.0.7: + version "9.0.21" + resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176" + integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA== + +import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.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 sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A== + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +infima@0.2.0-alpha.43: + version "0.2.0-alpha.43" + resolved "https://registry.yarnpkg.com/infima/-/infima-0.2.0-alpha.43.tgz#f7aa1d7b30b6c08afef441c726bac6150228cbe0" + integrity sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, 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.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + +ini@^1.3.5, ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +inline-style-parser@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" + integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== + +interpret@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + +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" + +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== + +ipaddr.js@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f" + integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ== + +is-alphabetical@1.0.4, is-alphabetical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" + integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== + +is-alphanumerical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" + integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +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@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +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-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-decimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" + integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extendable@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +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@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-hexadecimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" + integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== + +is-installed-globally@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + +is-npm@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" + integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== + +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@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg== + +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.2.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-inside@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-obj@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +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-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA== + +is-root@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c" + integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +is-whitespace-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" + integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== + +is-word-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230" + integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA== + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +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@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest-worker@^29.1.2: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== + dependencies: + "@types/node" "*" + jest-util "^29.7.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jiti@^1.20.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" + integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== + +joi@^17.6.0: + version "17.11.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.11.0.tgz#aa9da753578ec7720e6f0ca2c7046996ed04fc1a" + integrity sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.3" + "@sideway/formula" "^3.0.1" + "@sideway/pinpoint" "^2.0.0" + +"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.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +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 sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ== + +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +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-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json5@^2.1.2, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +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@^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== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +latest-version@^5.1.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" + +launch-editor@^2.6.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.6.1.tgz#f259c9ef95cbc9425620bbbd14b468fcdb4ffe3c" + integrity sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw== + dependencies: + picocolors "^1.0.0" + shell-quote "^1.8.1" + +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== + +lilconfig@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +loader-utils@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +loader-utils@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.1.tgz#4fb104b599daafd82ef3e1a41fb9265f87e1f576" + integrity sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw== + +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@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.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.curry@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170" + integrity sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA== + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== + +lodash.flow@^3.3.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/lodash.flow/-/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a" + integrity sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw== + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash.uniq@4.5.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 sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + +lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.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@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +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: + 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@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.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" + +markdown-escapes@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" + integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== + +mdast-squeeze-paragraphs@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97" + integrity sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ== + dependencies: + unist-util-remove "^2.0.0" + +mdast-util-definitions@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2" + integrity sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ== + dependencies: + unist-util-visit "^2.0.0" + +mdast-util-to-hast@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz#0cfc82089494c52d46eb0e3edb7a4eb2aea021eb" + integrity sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + mdast-util-definitions "^4.0.0" + mdurl "^1.0.0" + unist-builder "^2.0.0" + unist-util-generated "^1.0.0" + unist-util-position "^3.0.0" + unist-util-visit "^2.0.0" + +mdast-util-to-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" + integrity sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w== + +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + +mdurl@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +memfs@^3.1.2, memfs@^3.4.3: + version "3.6.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6" + integrity sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ== + dependencies: + fs-monkey "^1.0.4" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + 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 sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== + +mime-types@2.1.18: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== + dependencies: + mime-db "~1.33.0" + +mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.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.1.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== + +mini-css-extract-plugin@^2.6.1: + version "2.7.7" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.7.tgz#4acf02f362c641c38fb913bfcb7ca2fc4a7cf339" + integrity sha512-+0n11YGyRavUR3IlaOzJ0/4Il1avMvJ1VJfhWfCn24ITQXhRr1gghbhhrda6tgtNcpZaWKdSuwKq20Jb7fnlyw== + dependencies: + schema-utils "^4.0.0" + +minimalistic-assert@^1.0.0: + 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== + +minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.5: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mrmime@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4" + integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" + integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-emoji@^1.10.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" + integrity sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A== + dependencies: + lodash "^4.17.21" + +node-fetch@^2.6.12: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +node-forge@^1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + +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 sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + +normalize-url@^4.1.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== + +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +nprogress@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" + integrity sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA== + +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +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 sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +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.assign@^4.1.0: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +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.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + 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 sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +open@^8.0.9, open@^8.4.0: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +opener@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" + integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== + +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-limit@^2.0.0, p-limit@^2.2.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.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.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@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.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-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-retry@^4.5.0: + version "4.6.2" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" + integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== + dependencies: + "@types/retry" "0.12.0" + retry "^0.13.1" + +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" + +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" + integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + +parse-json@^5.0.0, parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse-numeric-range@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz#7c63b61190d61e4d53a1197f0c83c47bb670ffa3" + integrity sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ== + +parse5-htmlparser2-tree-adapter@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz#23c2cc233bcf09bb7beba8b8a69d46b08c62c2f1" + integrity sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g== + dependencies: + domhandler "^5.0.2" + parse5 "^7.0.0" + +parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parse5@^7.0.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + +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== + +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== + +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: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +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 sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +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 sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +path-to-regexp@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" + integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== + +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-dir@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pkg-up@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" + integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== + dependencies: + find-up "^3.0.0" + +postcss-calc@^8.2.3: + version "8.2.4" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.4.tgz#77b9c29bfcbe8a07ff6693dc87050828889739a5" + integrity sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q== + dependencies: + postcss-selector-parser "^6.0.9" + postcss-value-parser "^4.2.0" + +postcss-colormin@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.3.1.tgz#86c27c26ed6ba00d96c79e08f3ffb418d1d1988f" + integrity sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ== + dependencies: + browserslist "^4.21.4" + caniuse-api "^3.0.0" + colord "^2.9.1" + postcss-value-parser "^4.2.0" + +postcss-convert-values@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz#04998bb9ba6b65aa31035d669a6af342c5f9d393" + integrity sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA== + dependencies: + browserslist "^4.21.4" + postcss-value-parser "^4.2.0" + +postcss-discard-comments@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz#8df5e81d2925af2780075840c1526f0660e53696" + integrity sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ== + +postcss-discard-duplicates@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz#9eb4fe8456706a4eebd6d3b7b777d07bad03e848" + integrity sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw== + +postcss-discard-empty@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz#e57762343ff7f503fe53fca553d18d7f0c369c6c" + integrity sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A== + +postcss-discard-overridden@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz#7e8c5b53325747e9d90131bb88635282fb4a276e" + integrity sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw== + +postcss-discard-unused@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-5.1.0.tgz#8974e9b143d887677304e558c1166d3762501142" + integrity sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw== + dependencies: + postcss-selector-parser "^6.0.5" + +postcss-loader@^7.0.0: + version "7.3.4" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.3.4.tgz#aed9b79ce4ed7e9e89e56199d25ad1ec8f606209" + integrity sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A== + dependencies: + cosmiconfig "^8.3.5" + jiti "^1.20.0" + semver "^7.5.4" + +postcss-merge-idents@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz#7753817c2e0b75d0853b56f78a89771e15ca04a1" + integrity sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw== + dependencies: + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-merge-longhand@^5.1.7: + version "5.1.7" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz#24a1bdf402d9ef0e70f568f39bdc0344d568fb16" + integrity sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ== + dependencies: + postcss-value-parser "^4.2.0" + stylehacks "^5.1.1" + +postcss-merge-rules@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz#2f26fa5cacb75b1402e213789f6766ae5e40313c" + integrity sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g== + dependencies: + browserslist "^4.21.4" + caniuse-api "^3.0.0" + cssnano-utils "^3.1.0" + postcss-selector-parser "^6.0.5" + +postcss-minify-font-values@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz#f1df0014a726083d260d3bd85d7385fb89d1f01b" + integrity sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-minify-gradients@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz#f1fe1b4f498134a5068240c2f25d46fcd236ba2c" + integrity sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw== + dependencies: + colord "^2.9.1" + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-minify-params@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz#c06a6c787128b3208b38c9364cfc40c8aa5d7352" + integrity sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw== + dependencies: + browserslist "^4.21.4" + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-minify-selectors@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz#d4e7e6b46147b8117ea9325a915a801d5fe656c6" + integrity sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg== + dependencies: + postcss-selector-parser "^6.0.5" + +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== + +postcss-modules-local-by-default@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz#b08eb4f083050708998ba2c6061b50c2870ca524" + integrity sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.1.0.tgz#fbfddfda93a31f310f1d152c2bb4d3f3c5592ee0" + integrity sha512-SaIbK8XW+MZbd0xHPf7kdfA/3eOt7vxJ72IRecn3EzuZVLr1r0orzf0MX/pN8m+NMDoo6X/SQd8oeKqGZd8PXg== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-normalize-charset@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz#9302de0b29094b52c259e9b2cf8dc0879879f0ed" + integrity sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg== + +postcss-normalize-display-values@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz#72abbae58081960e9edd7200fcf21ab8325c3da8" + integrity sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-positions@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz#ef97279d894087b59325b45c47f1e863daefbb92" + integrity sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-repeat-style@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz#e9eb96805204f4766df66fd09ed2e13545420fb2" + integrity sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-string@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz#411961169e07308c82c1f8c55f3e8a337757e228" + integrity sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-timing-functions@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz#d5614410f8f0b2388e9f240aa6011ba6f52dafbb" + integrity sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-unicode@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz#f67297fca3fea7f17e0d2caa40769afc487aa030" + integrity sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA== + dependencies: + browserslist "^4.21.4" + postcss-value-parser "^4.2.0" + +postcss-normalize-url@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz#ed9d88ca82e21abef99f743457d3729a042adcdc" + integrity sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew== + dependencies: + normalize-url "^6.0.1" + postcss-value-parser "^4.2.0" + +postcss-normalize-whitespace@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz#08a1a0d1ffa17a7cc6efe1e6c9da969cc4493cfa" + integrity sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-ordered-values@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz#b6fd2bd10f937b23d86bc829c69e7732ce76ea38" + integrity sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ== + dependencies: + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-reduce-idents@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-5.2.0.tgz#c89c11336c432ac4b28792f24778859a67dfba95" + integrity sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-reduce-initial@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz#798cd77b3e033eae7105c18c9d371d989e1382d6" + integrity sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg== + dependencies: + browserslist "^4.21.4" + caniuse-api "^3.0.0" + +postcss-reduce-transforms@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz#333b70e7758b802f3dd0ddfe98bb1ccfef96b6e9" + integrity sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: + version "6.0.15" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz#11cc2b21eebc0b99ea374ffb9887174855a01535" + integrity sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-sort-media-queries@^4.2.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/postcss-sort-media-queries/-/postcss-sort-media-queries-4.4.1.tgz#04a5a78db3921eb78f28a1a781a2e68e65258128" + integrity sha512-QDESFzDDGKgpiIh4GYXsSy6sek2yAwQx1JASl5AxBtU1Lq2JfKBljIPNdil989NcSKRQX1ToiaKphImtBuhXWw== + dependencies: + sort-css-media-queries "2.1.0" + +postcss-svgo@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d" + integrity sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA== + dependencies: + postcss-value-parser "^4.2.0" + svgo "^2.7.0" + +postcss-unique-selectors@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz#a9f273d1eacd09e9aa6088f4b0507b18b1b541b6" + integrity sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA== + dependencies: + postcss-selector-parser "^6.0.5" + +postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss-zindex@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-5.1.0.tgz#4a5c7e5ff1050bd4c01d95b1847dfdcc58a496ff" + integrity sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A== + +postcss@^8.3.11, postcss@^8.4.14, postcss@^8.4.17, postcss@^8.4.31: + version "8.4.33" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742" + integrity sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA== + +pretty-error@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" + integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw== + dependencies: + lodash "^4.17.20" + renderkid "^3.0.0" + +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== + +prism-react-renderer@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz#786bb69aa6f73c32ba1ee813fbe17a0115435085" + integrity sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg== + +prismjs@^1.28.0: + version "1.29.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" + integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== + +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== + +promise@^7.1.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" + +prompts@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +prop-types@^15.6.2, prop-types@^15.7.2: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +property-information@^5.0.0, property-information@^5.3.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" + integrity sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA== + dependencies: + xtend "^4.0.0" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.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" + +punycode@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +pupa@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" + integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== + dependencies: + escape-goat "^2.0.0" + +pure-color@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e" + integrity sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +queue@6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65" + integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA== + dependencies: + inherits "~2.0.3" + +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" + +range-parser@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A== + +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.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc@1.2.8, 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" + +react-base16-styling@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/react-base16-styling/-/react-base16-styling-0.6.0.tgz#ef2156d66cf4139695c8a167886cb69ea660792c" + integrity sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ== + dependencies: + base16 "^1.0.0" + lodash.curry "^4.0.1" + lodash.flow "^3.3.0" + pure-color "^1.2.0" + +react-dev-utils@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-12.0.1.tgz#ba92edb4a1f379bd46ccd6bcd4e7bc398df33e73" + integrity sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ== + dependencies: + "@babel/code-frame" "^7.16.0" + address "^1.1.2" + browserslist "^4.18.1" + chalk "^4.1.2" + cross-spawn "^7.0.3" + detect-port-alt "^1.1.6" + escape-string-regexp "^4.0.0" + filesize "^8.0.6" + find-up "^5.0.0" + fork-ts-checker-webpack-plugin "^6.5.0" + global-modules "^2.0.0" + globby "^11.0.4" + gzip-size "^6.0.0" + immer "^9.0.7" + is-root "^2.1.0" + loader-utils "^3.2.0" + open "^8.4.0" + pkg-up "^3.1.0" + prompts "^2.4.2" + react-error-overlay "^6.0.11" + recursive-readdir "^2.2.2" + shell-quote "^1.7.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +react-dom@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" + integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler "^0.20.2" + +react-error-overlay@^6.0.11: + version "6.0.11" + resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb" + integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== + +react-fast-compare@^3.2.0, react-fast-compare@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" + integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ== + +react-helmet-async@*: + version "2.0.4" + resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-2.0.4.tgz#50a4377778f380ed1d0136303916b38eff1bf153" + integrity sha512-yxjQMWposw+akRfvpl5+8xejl4JtUlHnEBcji6u8/e6oc7ozT+P9PNTWMhCbz2y9tc5zPegw2BvKjQA+NwdEjQ== + dependencies: + invariant "^2.2.4" + react-fast-compare "^3.2.2" + shallowequal "^1.1.0" + +react-helmet-async@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.3.0.tgz#7bd5bf8c5c69ea9f02f6083f14ce33ef545c222e" + integrity sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg== + dependencies: + "@babel/runtime" "^7.12.5" + invariant "^2.2.4" + prop-types "^15.7.2" + react-fast-compare "^3.2.0" + shallowequal "^1.1.0" + +react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-json-view@^1.21.3: + version "1.21.3" + resolved "https://registry.yarnpkg.com/react-json-view/-/react-json-view-1.21.3.tgz#f184209ee8f1bf374fb0c41b0813cff54549c475" + integrity sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw== + dependencies: + flux "^4.0.1" + react-base16-styling "^0.6.0" + react-lifecycles-compat "^3.0.4" + react-textarea-autosize "^8.3.2" + +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + +react-loadable-ssr-addon-v5-slorber@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/react-loadable-ssr-addon-v5-slorber/-/react-loadable-ssr-addon-v5-slorber-1.0.1.tgz#2cdc91e8a744ffdf9e3556caabeb6e4278689883" + integrity sha512-lq3Lyw1lGku8zUEJPDxsNm1AfYHBrO9Y1+olAYwpUJ2IGFBskM0DMKok97A6LWUpHm+o7IvQBOWu9MLenp9Z+A== + dependencies: + "@babel/runtime" "^7.10.3" + +react-router-config@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/react-router-config/-/react-router-config-5.1.1.tgz#0f4263d1a80c6b2dc7b9c1902c9526478194a988" + integrity sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg== + dependencies: + "@babel/runtime" "^7.1.2" + +react-router-dom@^5.3.3: + version "5.3.4" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.4.tgz#2ed62ffd88cae6db134445f4a0c0ae8b91d2e5e6" + integrity sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ== + dependencies: + "@babel/runtime" "^7.12.13" + history "^4.9.0" + loose-envify "^1.3.1" + prop-types "^15.6.2" + react-router "5.3.4" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-router@5.3.4, react-router@^5.3.3: + version "5.3.4" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.3.4.tgz#8ca252d70fcc37841e31473c7a151cf777887bb5" + integrity sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA== + dependencies: + "@babel/runtime" "^7.12.13" + history "^4.9.0" + hoist-non-react-statics "^3.1.0" + loose-envify "^1.3.1" + path-to-regexp "^1.7.0" + prop-types "^15.6.2" + react-is "^16.6.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-textarea-autosize@^8.3.2: + version "8.5.3" + resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz#d1e9fe760178413891484847d3378706052dd409" + integrity sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ== + dependencies: + "@babel/runtime" "^7.20.13" + use-composed-ref "^1.3.0" + use-latest "^1.2.1" + +react@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" + integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +readable-stream@^2.0.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + 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: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +reading-time@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/reading-time/-/reading-time-1.5.0.tgz#d2a7f1b6057cb2e169beaf87113cc3411b5bc5bb" + integrity sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg== + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== + dependencies: + resolve "^1.1.6" + +recursive-readdir@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz#e726f328c0d69153bcabd5c322d3195252379372" + integrity sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA== + dependencies: + minimatch "^3.0.5" + +regenerate-unicode-properties@^10.1.0: + version "10.1.1" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz#6b0e05489d9076b04c436f318d9b067bba459480" + integrity sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q== + dependencies: + regenerate "^1.4.2" + +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +regenerator-transform@^0.15.2: + version "0.15.2" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" + integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== + dependencies: + "@babel/runtime" "^7.8.4" + +regexpu-core@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== + dependencies: + "@babel/regjsgen" "^0.8.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^10.1.0" + regjsparser "^0.9.1" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + +registry-auth-token@^4.0.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.2.tgz#f02d49c3668884612ca031419491a13539e21fac" + integrity sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg== + 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" + +regjsparser@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" + integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== + dependencies: + jsesc "~0.5.0" + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== + +remark-emoji@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/remark-emoji/-/remark-emoji-2.2.0.tgz#1c702090a1525da5b80e15a8f963ef2c8236cac7" + integrity sha512-P3cj9s5ggsUvWw5fS2uzCHJMGuXYRb0NnZqYlNecewXt8QBU9n5vW3DUUKOhepS8F9CwdMx9B8a3i7pqFWAI5w== + dependencies: + emoticon "^3.2.0" + node-emoji "^1.10.0" + unist-util-visit "^2.0.3" + +remark-footnotes@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/remark-footnotes/-/remark-footnotes-2.0.0.tgz#9001c4c2ffebba55695d2dd80ffb8b82f7e6303f" + integrity sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ== + +remark-mdx@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-1.6.22.tgz#06a8dab07dcfdd57f3373af7f86bd0e992108bbd" + integrity sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ== + dependencies: + "@babel/core" "7.12.9" + "@babel/helper-plugin-utils" "7.10.4" + "@babel/plugin-proposal-object-rest-spread" "7.12.1" + "@babel/plugin-syntax-jsx" "7.12.1" + "@mdx-js/util" "1.6.22" + is-alphabetical "1.0.4" + remark-parse "8.0.3" + unified "9.2.0" + +remark-parse@8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-8.0.3.tgz#9c62aa3b35b79a486454c690472906075f40c7e1" + integrity sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q== + dependencies: + ccount "^1.0.0" + collapse-white-space "^1.0.2" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + is-word-character "^1.0.0" + markdown-escapes "^1.0.0" + parse-entities "^2.0.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + trim "0.0.1" + trim-trailing-lines "^1.0.0" + unherit "^1.0.4" + unist-util-remove-position "^2.0.0" + vfile-location "^3.0.0" + xtend "^4.0.1" + +remark-squeeze-paragraphs@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz#76eb0e085295131c84748c8e43810159c5653ead" + integrity sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw== + dependencies: + mdast-squeeze-paragraphs "^4.0.0" + +renderkid@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a" + integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg== + dependencies: + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^6.0.1" + +repeat-string@^1.5.4: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + +require-from-string@^2.0.2: + 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-like@>= 0.1.1": + version "0.1.2" + resolved "https://registry.yarnpkg.com/require-like/-/require-like-0.1.2.tgz#ad6f30c13becd797010c468afa775c0c0a6b47fa" + integrity sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-pathname@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" + integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== + +resolve@^1.1.6, resolve@^1.14.2, resolve@^1.3.2: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ== + dependencies: + lowercase-keys "^1.0.0" + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rtl-detect@^1.0.4: + version "1.1.2" + resolved "https://registry.yarnpkg.com/rtl-detect/-/rtl-detect-1.1.2.tgz#ca7f0330af5c6bb626c15675c642ba85ad6273c6" + integrity sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ== + +rtlcss@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/rtlcss/-/rtlcss-3.5.0.tgz#c9eb91269827a102bac7ae3115dd5d049de636c3" + integrity sha512-wzgMaMFHQTnyi9YOwsx9LjOxYXJPzS8sYnFaKm6R5ysvTkwzHiB0vxnbHwchHQT65PTdBjDG21/kQBWI7q9O7A== + dependencies: + find-up "^5.0.0" + picocolors "^1.0.0" + postcss "^8.3.11" + strip-json-comments "^3.1.1" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +rxjs@^7.5.4: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + +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.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.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== + +"safer-buffer@>= 2.1.2 < 3": + 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.3.0" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" + integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== + +scheduler@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" + integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +schema-utils@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" + integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== + dependencies: + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" + +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" + +schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" + integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + +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 sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== + +selfsigned@^2.1.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" + integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== + dependencies: + "@types/node-forge" "^1.3.0" + node-forge "^1" + +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@^5.4.1: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^6.0.0, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.2, semver@^7.3.4, semver@^7.3.7, semver@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + +serve-handler@^6.1.3: + version "6.1.5" + resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.5.tgz#a4a0964f5c55c7e37a02a633232b6f0d6f068375" + integrity sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg== + dependencies: + bytes "3.0.0" + content-disposition "0.5.2" + fast-url-parser "1.1.3" + mime-types "2.1.18" + minimatch "3.1.2" + path-is-inside "1.0.2" + path-to-regexp "2.2.1" + range-parser "1.2.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 sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + 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.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +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.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shallowequal@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" + integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@^1.7.3, shell-quote@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== + +shelljs@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.2, signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +sirv@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.4.tgz#5dd9a725c578e34e449f332703eb2a74e46a29b0" + integrity sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ== + dependencies: + "@polka/url" "^1.0.0-next.24" + mrmime "^2.0.0" + totalist "^3.0.0" + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +sitemap@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/sitemap/-/sitemap-7.1.1.tgz#eeed9ad6d95499161a3eadc60f8c6dce4bea2bef" + integrity sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg== + dependencies: + "@types/node" "^17.0.5" + "@types/sax" "^1.2.1" + arg "^5.0.0" + sax "^1.2.4" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + +sort-css-media-queries@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz#7c85e06f79826baabb232f5560e9745d7a78c4ce" + integrity sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA== + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.5.0: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +source-map@^0.6.0, source-map@^0.6.1, 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== + +space-separated-tokens@^1.0.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" + integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== + +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" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +state-toggle@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" + integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +std-env@^3.0.1: + version "3.7.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" + integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== + +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +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" + +stringify-object@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +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 sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +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 sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +style-to-object@0.3.0, style-to-object@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" + integrity sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA== + dependencies: + inline-style-parser "0.1.1" + +stylehacks@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.1.tgz#7934a34eb59d7152149fa69d6e9e56f2fc34bcc9" + integrity sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw== + dependencies: + browserslist "^4.21.4" + postcss-selector-parser "^6.0.4" + +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" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svg-parser@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" + integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== + +svgo@^2.7.0, svgo@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" + integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^4.1.3" + css-tree "^1.1.3" + csso "^4.2.0" + picocolors "^1.0.0" + stable "^0.1.8" + +tapable@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + +tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.3.3, terser-webpack-plugin@^5.3.7: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.20" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.26.0" + +terser@^5.10.0, terser@^5.26.0: + version "5.26.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.26.0.tgz#ee9f05d929f4189a9c28a0feb889d96d50126fe1" + integrity sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +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== + +tiny-invariant@^1.0.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" + integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== + +tiny-warning@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + +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 sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +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@^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" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +trim-trailing-lines@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" + integrity sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ== + +trim@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" + integrity sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ== + +trough@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" + integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== + +tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^2.5.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + +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" + +typescript@^4.7.4: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +ua-parser-js@^1.0.35: + version "1.0.37" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.37.tgz#b5dc7b163a5c1f0c510b08446aed4da92c46373f" + integrity sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +unherit@^1.0.4: + version "1.1.3" + resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22" + integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ== + dependencies: + inherits "^2.0.0" + xtend "^4.0.0" + +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" + integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== + +unified@9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.0.tgz#67a62c627c40589edebbf60f53edfd4d822027f8" + integrity sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg== + dependencies: + bail "^1.0.0" + extend "^3.0.0" + is-buffer "^2.0.0" + is-plain-obj "^2.0.0" + trough "^1.0.0" + vfile "^4.0.0" + +unified@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.2.tgz#67649a1abfc3ab85d2969502902775eb03146975" + integrity sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ== + dependencies: + bail "^1.0.0" + extend "^3.0.0" + is-buffer "^2.0.0" + is-plain-obj "^2.0.0" + trough "^1.0.0" + vfile "^4.0.0" + +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" + +unist-builder@2.0.3, unist-builder@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" + integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== + +unist-util-generated@^1.0.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b" + integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg== + +unist-util-is@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" + integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== + +unist-util-position@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" + integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== + +unist-util-remove-position@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz#5d19ca79fdba712301999b2b73553ca8f3b352cc" + integrity sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA== + dependencies: + unist-util-visit "^2.0.0" + +unist-util-remove@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-2.1.0.tgz#b0b4738aa7ee445c402fda9328d604a02d010588" + integrity sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q== + dependencies: + unist-util-is "^4.0.0" + +unist-util-stringify-position@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" + integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g== + dependencies: + "@types/unist" "^2.0.2" + +unist-util-visit-parents@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" + integrity sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + +unist-util-visit@2.0.3, unist-util-visit@^2.0.0, unist-util-visit@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" + integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + unist-util-visit-parents "^3.0.0" + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +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 sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +update-notifier@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" + integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== + dependencies: + boxen "^5.0.0" + chalk "^4.1.0" + configstore "^5.0.1" + has-yarn "^2.1.0" + import-lazy "^2.1.0" + is-ci "^2.0.0" + is-installed-globally "^0.4.0" + is-npm "^5.0.0" + is-yarn-global "^0.3.0" + latest-version "^5.1.0" + pupa "^2.1.1" + semver "^7.3.4" + semver-diff "^3.1.1" + xdg-basedir "^4.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +url-loader@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2" + integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA== + dependencies: + loader-utils "^2.0.0" + mime-types "^2.1.27" + schema-utils "^3.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 sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ== + dependencies: + prepend-http "^2.0.0" + +use-composed-ref@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.3.0.tgz#3d8104db34b7b264030a9d916c5e94fbe280dbda" + integrity sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ== + +use-isomorphic-layout-effect@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb" + integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA== + +use-latest@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/use-latest/-/use-latest-1.2.1.tgz#d13dfb4b08c28e3e33991546a2cee53e14038cf2" + integrity sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw== + dependencies: + use-isomorphic-layout-effect "^1.1.1" + +use-sync-external-store@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== + +utility-types@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" + integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +value-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" + integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +vfile-location@^3.0.0, vfile-location@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-3.2.0.tgz#d8e41fbcbd406063669ebf6c33d56ae8721d0f3c" + integrity sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA== + +vfile-message@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" + integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ== + dependencies: + "@types/unist" "^2.0.0" + unist-util-stringify-position "^2.0.0" + +vfile@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624" + integrity sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA== + dependencies: + "@types/unist" "^2.0.0" + is-buffer "^2.0.0" + unist-util-stringify-position "^2.0.0" + vfile-message "^2.0.0" + +wait-on@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.1.tgz#16bbc4d1e4ebdd41c5b4e63a2e16dbd1f4e5601e" + integrity sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw== + dependencies: + axios "^0.25.0" + joi "^17.6.0" + lodash "^4.17.21" + minimist "^1.2.5" + rxjs "^7.5.4" + +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +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" + +web-namespaces@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" + integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +webpack-bundle-analyzer@^4.5.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.1.tgz#84b7473b630a7b8c21c741f81d8fe4593208b454" + integrity sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ== + dependencies: + "@discoveryjs/json-ext" "0.5.7" + acorn "^8.0.4" + acorn-walk "^8.0.0" + commander "^7.2.0" + debounce "^1.2.1" + escape-string-regexp "^4.0.0" + gzip-size "^6.0.0" + html-escaper "^2.0.2" + is-plain-object "^5.0.0" + opener "^1.5.2" + picocolors "^1.0.0" + sirv "^2.0.3" + ws "^7.3.1" + +webpack-dev-middleware@^5.3.1: + version "5.3.3" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f" + integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA== + dependencies: + colorette "^2.0.10" + memfs "^3.4.3" + mime-types "^2.1.31" + range-parser "^1.2.1" + schema-utils "^4.0.0" + +webpack-dev-server@^4.9.3: + version "4.15.1" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz#8944b29c12760b3a45bdaa70799b17cb91b03df7" + integrity sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA== + dependencies: + "@types/bonjour" "^3.5.9" + "@types/connect-history-api-fallback" "^1.3.5" + "@types/express" "^4.17.13" + "@types/serve-index" "^1.9.1" + "@types/serve-static" "^1.13.10" + "@types/sockjs" "^0.3.33" + "@types/ws" "^8.5.5" + ansi-html-community "^0.0.8" + bonjour-service "^1.0.11" + chokidar "^3.5.3" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + default-gateway "^6.0.3" + express "^4.17.3" + graceful-fs "^4.2.6" + html-entities "^2.3.2" + http-proxy-middleware "^2.0.3" + ipaddr.js "^2.0.1" + launch-editor "^2.6.0" + open "^8.0.9" + p-retry "^4.5.0" + rimraf "^3.0.2" + schema-utils "^4.0.0" + selfsigned "^2.1.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^5.3.1" + ws "^8.13.0" + +webpack-merge@^5.8.0: + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.0" + +webpack-sources@^3.2.2, webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@^5.73.0: + version "5.89.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.89.0.tgz#56b8bf9a34356e93a6625770006490bf3a7f32dc" + integrity sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.0" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" + acorn "^8.7.1" + acorn-import-assertions "^1.9.0" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.15.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.7" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + +webpackbar@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-5.0.2.tgz#d3dd466211c73852741dfc842b7556dcbc2b0570" + integrity sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ== + dependencies: + chalk "^4.1.0" + consola "^2.15.3" + pretty-time "^1.1.0" + std-env "^3.0.1" + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + 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@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which@^1.3.1: + 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.1: + 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" + +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" + +widest-line@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-4.0.1.tgz#a0fc673aaba1ea6f0a0d35b3c2795c9a9cc2ebf2" + integrity sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig== + dependencies: + string-width "^5.0.1" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.0.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +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@^7.3.1: + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +ws@^8.13.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== + +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== + +xml-js@^1.6.11: + version "1.6.11" + resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9" + integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g== + dependencies: + sax "^1.2.4" + +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== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zwitch@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" + integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== diff --git a/misc/gendocs/Makefile b/misc/gendocs/Makefile index 8a4f3ba2de5..0f56d83693f 100644 --- a/misc/gendocs/Makefile +++ b/misc/gendocs/Makefile @@ -1,5 +1,8 @@ all: clean gen +install: + go install golang.org/x/pkgsite/cmd/pkgsite@latest + gen: ./gendocs.sh @@ -7,4 +10,4 @@ clean: rm -rf godoc kill_zombies: - kill -9 `lsof -t -i tcp:6060 -s TCP:LISTEN` || true + kill -9 `lsof -t -i tcp:8080 -s TCP:LISTEN` || true diff --git a/misc/gendocs/gendocs.sh b/misc/gendocs/gendocs.sh index d7621acd5a0..b50c597bc39 100755 --- a/misc/gendocs/gendocs.sh +++ b/misc/gendocs/gendocs.sh @@ -1,35 +1,58 @@ -#!/bin/sh - -GODOC_PORT=${GODOC_PORT:-6060} -GO_MODULE=${GO_MODULE:-github.com/gnolang/gno} -GODOC_OUT=${GODOC_OUT:-godoc} -URL=http://localhost:${GODOC_PORT}/pkg/github.com/gnolang/gno/ - -echo "[+] Starting godoc server..." -go run \ - -modfile ../devdeps/go.mod \ - golang.org/x/tools/cmd/godoc \ - -http="localhost:${GODOC_PORT}" & -PID=$! -# Waiting for godoc server -while ! curl --fail --silent "$URL" > /dev/null 2>&1; do - sleep 0.1 +#!/bin/bash +# Heavily modified version of the following script: +# https://gist.github.com/Kegsay/84ce060f237cb9ab4e0d2d321a91d920 +set -u + +DOC_DIR=godoc +PKG=github.com/gnolang/gno +# Used to load /static content +STATIC_PREFIX=/gno + +# Run a pkgsite server which we will scrape. Use env to run it from our repo's root directory. +env -C ../.. pkgsite & +DOC_PID=$! + +# Wait for the server to init +while : +do + curl -s "http://localhost:8080" > /dev/null + if [ $? -eq 0 ] # exit code is 0 if we connected + then + break + fi done -echo "[+] Downloading godoc pages..." +# Scrape the pkg directory for the API docs. Scrap lib for the CSS/JS. Ignore everything else. wget \ - --recursive \ - --no-verbose \ - --convert-links \ - --page-requisites \ - --adjust-extension \ - --execute=robots=off \ - --include-directories="/lib,/pkg/$GO_MODULE,/src/$GO_MODULE" \ - --exclude-directories="*" \ - --directory-prefix="${GODOC_OUT}" \ - --no-host-directories \ - "$URL?m=all" - -echo "[+] Killing godoc server..." -kill -9 "$PID" + --verbose \ + --recursive \ + --mirror \ + --convert-links \ + --adjust-extension \ + --page-requisites \ + -erobots=off \ + --accept-regex='8080/((search|license-policy|about|)$|(static|images)/|github.com/gnolang/)' \ + http://localhost:8080/ \ + http://localhost:8080/static/frontend/frontend.js \ + http://localhost:8080/static/frontend/unit/unit.js \ + http://localhost:8080/static/frontend/unit/main/main.js \ + http://localhost:8080/third_party/dialog-polyfill/dialog-polyfill.js + +# Stop the pkgsite server +kill -9 $DOC_PID + +# Delete the old directory or else mv will put the localhost dir into +# the DOC_DIR if it already exists. +rm -rf $DOC_DIR +mv localhost\:8080 $DOC_DIR + +# Perform various replacements to fix broken links/UI. +# /files/ will point to their github counterparts; we make links to importedby/version go nowhere; +# any other link will point to pkg.go.dev, and fix the /files/... text when viewing a pkg. +find godoc -type f -exec sed -ri 's#http://localhost:8080/files/[^"]*/github.com/gnolang/([^/"]+)/([^"]*)#https://github.com/gnolang/\1/blob/master/\2#g +s#http://localhost:8080/[^"?]*\?tab=(importedby|versions)#\##g +s#http://localhost:8080([^")]*)#https://pkg.go.dev\1#g +s#/files/[^" ]*/(github.com/[^" ]*)/#\1#g +s#s\.src = src;#s.src = "'"$STATIC_PREFIX"'" + src;#g' {} + +echo "Docs can be found in $DOC_DIR" diff --git a/misc/genproto/genproto.go b/misc/genproto/genproto.go index fd5c4e4e2e4..b9b97efbe37 100644 --- a/misc/genproto/genproto.go +++ b/misc/genproto/genproto.go @@ -2,7 +2,6 @@ package main import ( "context" - "fmt" "os" "github.com/gnolang/gno/tm2/pkg/amino" @@ -38,11 +37,7 @@ func main() { execGen, ) - if err := cmd.ParseAndRun(context.Background(), os.Args[1:]); err != nil { - _, _ = fmt.Fprintf(os.Stderr, "%+v\n", err) - - os.Exit(1) - } + cmd.Execute(context.Background(), os.Args[1:]) } func execGen(_ context.Context, _ []string) error { diff --git a/misc/genstd/exprstring.go b/misc/genstd/exprstring.go new file mode 100644 index 00000000000..c95c05c584e --- /dev/null +++ b/misc/genstd/exprstring.go @@ -0,0 +1,290 @@ +// Forked from go/types (go 1.20.3) to implement support for *linkedIdent. +// It cannot be easily split from the original as WriteExpr is highly recursive. + +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements printing of expressions. + +package main + +import ( + "bytes" + "fmt" + "go/ast" + "go/types" +) + +const ( + printerModeGoQualified = iota + printerModeGnoType +) + +type exprPrinter struct { + mode int +} + +// ExprString returns the (possibly shortened) string representation for x. +// Shortened representations are suitable for user interfaces but may not +// necessarily follow Go syntax. +// +// ExprString is identical to [types.ExprString] with the difference that it +// supports *linkedIdent. +func (ep *exprPrinter) ExprString(x ast.Expr) string { + var buf bytes.Buffer + ep.WriteExpr(&buf, x) + return buf.String() +} + +// WriteExpr writes the (possibly shortened) string representation for x to buf. +// Shortened representations are suitable for user interfaces but may not +// necessarily follow Go syntax. +// +// WriteExpr is identical to [types.WriteExpr] with the difference that it +// supports *linkedIdent. +func (ep *exprPrinter) WriteExpr(buf *bytes.Buffer, x ast.Expr) { + // The AST preserves source-level parentheses so there is + // no need to introduce them here to correct for different + // operator precedences. (This assumes that the AST was + // generated by a Go parser.) + + switch x := x.(type) { + default: + // fallback to go original -- for all non-recursive ast.Expr types + types.WriteExpr(buf, x) + + case *linkedIdent: + switch ep.mode { + case printerModeGoQualified: + n := pkgNameFromPath(x.lt.goPackage) + buf.WriteString(n) + buf.WriteByte('.') + buf.WriteString(x.lt.goName) + case printerModeGnoType: + buf.WriteString(x.lt.gnoName) + default: + panic(fmt.Errorf("invalid mode %d", ep.mode)) + } + + case *ast.Ellipsis: + buf.WriteString("...") + if x.Elt != nil { + ep.WriteExpr(buf, x.Elt) + } + + case *ast.FuncLit: + buf.WriteByte('(') + ep.WriteExpr(buf, x.Type) + buf.WriteString(" literal)") // shortened + + case *ast.CompositeLit: + ep.WriteExpr(buf, x.Type) + buf.WriteByte('{') + if len(x.Elts) > 0 { + buf.WriteString("…") + } + buf.WriteByte('}') + + case *ast.ParenExpr: + buf.WriteByte('(') + ep.WriteExpr(buf, x.X) + buf.WriteByte(')') + + case *ast.SelectorExpr: + ep.WriteExpr(buf, x.X) + buf.WriteByte('.') + buf.WriteString(x.Sel.Name) + + case *ast.IndexExpr, *ast.IndexListExpr: + ix := tpUnpackIndexExpr(x) + ep.WriteExpr(buf, ix.X) + buf.WriteByte('[') + ep.writeExprList(buf, ix.Indices) + buf.WriteByte(']') + + case *ast.SliceExpr: + ep.WriteExpr(buf, x.X) + buf.WriteByte('[') + if x.Low != nil { + ep.WriteExpr(buf, x.Low) + } + buf.WriteByte(':') + if x.High != nil { + ep.WriteExpr(buf, x.High) + } + if x.Slice3 { + buf.WriteByte(':') + if x.Max != nil { + ep.WriteExpr(buf, x.Max) + } + } + buf.WriteByte(']') + + case *ast.TypeAssertExpr: + ep.WriteExpr(buf, x.X) + buf.WriteString(".(") + ep.WriteExpr(buf, x.Type) + buf.WriteByte(')') + + case *ast.CallExpr: + ep.WriteExpr(buf, x.Fun) + buf.WriteByte('(') + ep.writeExprList(buf, x.Args) + if x.Ellipsis.IsValid() { + buf.WriteString("...") + } + buf.WriteByte(')') + + case *ast.StarExpr: + buf.WriteByte('*') + ep.WriteExpr(buf, x.X) + + case *ast.UnaryExpr: + buf.WriteString(x.Op.String()) + ep.WriteExpr(buf, x.X) + + case *ast.BinaryExpr: + ep.WriteExpr(buf, x.X) + buf.WriteByte(' ') + buf.WriteString(x.Op.String()) + buf.WriteByte(' ') + ep.WriteExpr(buf, x.Y) + + case *ast.ArrayType: + buf.WriteByte('[') + if x.Len != nil { + ep.WriteExpr(buf, x.Len) + } + buf.WriteByte(']') + ep.WriteExpr(buf, x.Elt) + + case *ast.StructType: + buf.WriteString("struct{") + ep.writeFieldList(buf, x.Fields.List, "; ", false) + buf.WriteByte('}') + + case *ast.FuncType: + buf.WriteString("func") + ep.writeSigExpr(buf, x) + + case *ast.InterfaceType: + buf.WriteString("interface{") + ep.writeFieldList(buf, x.Methods.List, "; ", true) + buf.WriteByte('}') + + case *ast.MapType: + buf.WriteString("map[") + ep.WriteExpr(buf, x.Key) + buf.WriteByte(']') + ep.WriteExpr(buf, x.Value) + + case *ast.ChanType: + var s string + switch x.Dir { + case ast.SEND: + s = "chan<- " + case ast.RECV: + s = "<-chan " + default: + s = "chan " + } + buf.WriteString(s) + ep.WriteExpr(buf, x.Value) + } +} + +func (ep *exprPrinter) writeSigExpr(buf *bytes.Buffer, sig *ast.FuncType) { + buf.WriteByte('(') + ep.writeFieldList(buf, sig.Params.List, ", ", false) + buf.WriteByte(')') + + res := sig.Results + n := res.NumFields() + if n == 0 { + // no result + return + } + + buf.WriteByte(' ') + if n == 1 && len(res.List[0].Names) == 0 { + // single unnamed result + ep.WriteExpr(buf, res.List[0].Type) + return + } + + // multiple or named result(s) + buf.WriteByte('(') + ep.writeFieldList(buf, res.List, ", ", false) + buf.WriteByte(')') +} + +func (ep *exprPrinter) writeFieldList(buf *bytes.Buffer, list []*ast.Field, sep string, iface bool) { + for i, f := range list { + if i > 0 { + buf.WriteString(sep) + } + + // field list names + ep.writeIdentList(buf, f.Names) + + // types of interface methods consist of signatures only + if sig, _ := f.Type.(*ast.FuncType); sig != nil && iface { + ep.writeSigExpr(buf, sig) + continue + } + + // named fields are separated with a blank from the field type + if len(f.Names) > 0 { + buf.WriteByte(' ') + } + + ep.WriteExpr(buf, f.Type) + + // ignore tag + } +} + +func (ep *exprPrinter) writeIdentList(buf *bytes.Buffer, list []*ast.Ident) { + for i, x := range list { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(x.Name) + } +} + +func (ep *exprPrinter) writeExprList(buf *bytes.Buffer, list []ast.Expr) { + for i, x := range list { + if i > 0 { + buf.WriteString(", ") + } + ep.WriteExpr(buf, x) + } +} + +// The following are copied from go/internal/typeparams. +// We cannot use the original directly as it comes from an "internal" package. + +// tpIndexExpr wraps an ast.IndexExpr or ast.IndexListExpr. +// +// Orig holds the original ast.Expr from which this IndexExpr was derived. +type tpIndexExpr struct { + Orig ast.Expr // the wrapped expr, which may be distinct from the IndexListExpr below. + *ast.IndexListExpr +} + +func tpUnpackIndexExpr(n ast.Node) *tpIndexExpr { + switch e := n.(type) { + case *ast.IndexExpr: + return &tpIndexExpr{e, &ast.IndexListExpr{ + X: e.X, + Lbrack: e.Lbrack, + Indices: []ast.Expr{e.Index}, + Rbrack: e.Rbrack, + }} + case *ast.IndexListExpr: + return &tpIndexExpr{e, e} + } + return nil +} diff --git a/misc/genstd/genstd.go b/misc/genstd/genstd.go new file mode 100644 index 00000000000..9d0c21c5229 --- /dev/null +++ b/misc/genstd/genstd.go @@ -0,0 +1,212 @@ +// Command genstd provides static code generation for standard library native +// bindings. +package main + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "io/fs" + "os" + "path/filepath" + "strconv" + "strings" + "text/template" + + _ "embed" +) + +func main() { + path := "." + if len(os.Args) > 1 { + path = os.Args[1] + } + if err := _main(path); err != nil { + fmt.Fprintf(os.Stderr, "%+v\n", err) + os.Exit(1) + } +} + +func _main(stdlibsPath string) error { + stdlibsPath = filepath.Clean(stdlibsPath) + if s, err := os.Stat(stdlibsPath); err != nil { + return err + } else if !s.IsDir() { + return fmt.Errorf("not a directory: %q", stdlibsPath) + } + + // Gather data about each package, getting functions of interest + // (gno bodyless + go exported). + pkgs, err := walkStdlibs(stdlibsPath) + if err != nil { + return err + } + + // Link up each Gno function with its matching Go function. + mappings := linkFunctions(pkgs) + + // Create generated file. + f, err := os.Create("native.go") + if err != nil { + return fmt.Errorf("create native.go: %w", err) + } + defer f.Close() + + // Execute template. + td := &tplData{ + Mappings: mappings, + } + if err := tpl.Execute(f, td); err != nil { + return fmt.Errorf("execute template: %w", err) + } + if err := f.Close(); err != nil { + return err + } + + // gofumpt doesn't do "import fixing" like goimports: + // https://github.com/mvdan/gofumpt#frequently-asked-questions + if err := runTool("golang.org/x/tools/cmd/goimports"); err != nil { + return err + } + return runTool("mvdan.cc/gofumpt") +} + +type pkgData struct { + importPath string + fsDir string + gnoBodyless []funcDecl + goExported []funcDecl +} + +type funcDecl struct { + *ast.FuncDecl + imports []*ast.ImportSpec +} + +func addImports(fds []*ast.FuncDecl, imports []*ast.ImportSpec) []funcDecl { + r := make([]funcDecl, len(fds)) + for i, fd := range fds { + r[i] = funcDecl{fd, imports} + } + return r +} + +// walkStdlibs does a BFS walk through the given directory, expected to be a +// "stdlib" directory, parsing and keeping track of Go and Gno functions of +// interest. +func walkStdlibs(stdlibsPath string) ([]*pkgData, error) { + pkgs := make([]*pkgData, 0, 64) + err := filepath.WalkDir(stdlibsPath, func(fpath string, d fs.DirEntry, err error) error { + // skip dirs and top-level directory. + if d.IsDir() || filepath.Dir(fpath) == stdlibsPath { + return nil + } + + // skip non-source and test files. + ext := filepath.Ext(fpath) + noExt := fpath[:len(fpath)-len(ext)] + if (ext != ".go" && ext != ".gno") || + strings.HasSuffix(noExt, "_test") || + strings.HasSuffix(fpath, ".gen.go") { + return nil + } + + dir := filepath.Dir(fpath) + var pkg *pkgData + // because of bfs, we know that if we've already been in this directory + // in a previous file, it must be in the last entry of pkgs. + if len(pkgs) == 0 || pkgs[len(pkgs)-1].fsDir != dir { + pkg = &pkgData{ + importPath: strings.ReplaceAll(strings.TrimPrefix(dir, stdlibsPath+"/"), string(filepath.Separator), "/"), + fsDir: dir, + } + pkgs = append(pkgs, pkg) + } else { + pkg = pkgs[len(pkgs)-1] + } + fs := token.NewFileSet() + f, err := parser.ParseFile(fs, fpath, nil, parser.SkipObjectResolution) + if err != nil { + return err + } + if ext == ".go" { + // keep track of exported function declarations. + // warn about all exported type, const and var declarations. + if exp := filterExported(f); len(exp) > 0 { + pkg.goExported = append(pkg.goExported, addImports(exp, f.Imports)...) + } + } else if bd := filterBodylessFuncDecls(f); len(bd) > 0 { + // gno file -- keep track of function declarations without body. + pkg.gnoBodyless = append(pkg.gnoBodyless, addImports(bd, f.Imports)...) + } + return nil + }) + return pkgs, err +} + +// filterBodylessFuncDecls returns the function declarations in the given file +// which don't contain a body. +func filterBodylessFuncDecls(f *ast.File) (bodyless []*ast.FuncDecl) { + for _, decl := range f.Decls { + fd, ok := decl.(*ast.FuncDecl) + if !ok || fd.Body != nil { + continue + } + bodyless = append(bodyless, fd) + } + return +} + +// filterExported returns the exported function declarations of the given file. +func filterExported(f *ast.File) (exported []*ast.FuncDecl) { + for _, decl := range f.Decls { + switch d := decl.(type) { + case *ast.GenDecl: + // TODO: complain if there are exported types/vars/consts + continue + case *ast.FuncDecl: + if d.Name.IsExported() { + exported = append(exported, d) + } + } + } + return +} + +//go:embed template.tmpl +var templateText string + +var tpl = template.Must(template.New("").Parse(templateText)) + +// tplData is the data passed to the template. +type tplData struct { + Mappings []mapping +} + +type tplImport struct{ Name, Path string } + +func (t tplData) Imports() (res []tplImport) { + add := func(path string) { + for _, v := range res { + if v.Path == path { + return + } + } + res = append(res, tplImport{Name: pkgNameFromPath(path), Path: path}) + } + for _, m := range t.Mappings { + add(m.GoImportPath) + // There might be a bit more than we need - but we run goimports to fix that. + for _, v := range m.goImports { + s, err := strconv.Unquote(v.Path.Value) + if err != nil { + panic(fmt.Errorf("could not unquote go import string literal: %s", v.Path.Value)) + } + add(s) + } + } + return +} + +func (tplData) PkgName(path string) string { return pkgNameFromPath(path) } diff --git a/misc/genstd/mapping.go b/misc/genstd/mapping.go new file mode 100644 index 00000000000..0e4034a1ab9 --- /dev/null +++ b/misc/genstd/mapping.go @@ -0,0 +1,471 @@ +package main + +import ( + "errors" + "fmt" + "go/ast" + "path" + "strconv" +) + +const gnoPackagePath = "github.com/gnolang/gno/gnovm/pkg/gnolang" + +type mapping struct { + GnoImportPath string // time + GnoFunc string // now + GoImportPath string // github.com/gnolang/gno/gnovm/stdlibs/time + GoFunc string // X_now + Params []mappingType + Results []mappingType + MachineParam bool + + gnoImports []*ast.ImportSpec + goImports []*ast.ImportSpec +} + +type mappingType struct { + // type of ast.Expr is from the normal ast.Expr types + // + *linkedIdent. + Type ast.Expr + + // IsTypedValue is set to true if the parameter or result in go is of type + // gno.TypedValue. This prevents the generated code from performing + // Go2Gno/Gno2Go reflection-based conversion. + IsTypedValue bool +} + +func (mt mappingType) GoQualifiedName() string { + return (&exprPrinter{ + mode: printerModeGoQualified, + }).ExprString(mt.Type) +} + +func (mt mappingType) GnoType() string { + return (&exprPrinter{ + mode: printerModeGnoType, + }).ExprString(mt.Type) +} + +type linkedIdent struct { + ast.BadExpr // Unused, but it makes *linkedIdent implement ast.Expr + + lt linkedType +} + +func linkFunctions(pkgs []*pkgData) []mapping { + var mappings []mapping + for _, pkg := range pkgs { + for _, gb := range pkg.gnoBodyless { + nameWant := gb.Name.Name + if !gb.Name.IsExported() { + nameWant = "X_" + nameWant + } + fn := findFuncByName(pkg.goExported, nameWant) + if fn.FuncDecl == nil { + panic( + fmt.Errorf("package %q: no matching go function declaration (%q) exists for function %q", + pkg.importPath, nameWant, gb.Name.Name), + ) + } + mp := mapping{ + GnoImportPath: pkg.importPath, + GnoFunc: gb.Name.Name, + GoImportPath: "github.com/gnolang/gno/" + relPath() + "/" + pkg.importPath, + GoFunc: fn.Name.Name, + + gnoImports: gb.imports, + goImports: fn.imports, + } + if !mp.signaturesMatch(gb, fn) { + panic( + fmt.Errorf("package %q: signature of gno function %s doesn't match signature of go function %s", + pkg.importPath, gb.Name.Name, fn.Name.Name), + ) + } + mp.loadParamsResults(gb, fn) + mappings = append(mappings, mp) + } + } + return mappings +} + +func findFuncByName(fns []funcDecl, name string) funcDecl { + for _, fn := range fns { + if fn.Name.Name == name { + return fn + } + } + return funcDecl{} +} + +func (m *mapping) loadParamsResults(gnof, gof funcDecl) { + // initialise with lengths + m.Params = make([]mappingType, 0, gnof.Type.Params.NumFields()) + m.Results = make([]mappingType, 0, gnof.Type.Results.NumFields()) + + gofpl := gof.Type.Params.List + if m.MachineParam { + // skip machine parameter + gofpl = gofpl[1:] + } + if gnof.Type.Params != nil { + m._loadParamsResults(&m.Params, gnof.Type.Params.List, gofpl) + } + if gnof.Type.Results != nil { + m._loadParamsResults(&m.Results, gnof.Type.Results.List, gof.Type.Results.List) + } +} + +func (m *mapping) _loadParamsResults(dst *[]mappingType, gnol, gol []*ast.Field) { + iterFields(gnol, gol, func(gnoe, goe ast.Expr) error { + if m.isTypedValue(goe) { + *dst = append(*dst, mappingType{Type: gnoe, IsTypedValue: true}) + } else { + merged := m.mergeTypes(gnoe, goe) + *dst = append(*dst, mappingType{Type: merged}) + } + return nil + }) +} + +// isGnoMachine checks whether field is of type *gno.Machine, +// and it has at most 1 name. +func (m *mapping) isGnoMachine(field *ast.Field) bool { + if len(field.Names) > 1 { + return false + } + + return m.isGnoType(field.Type, true, "Machine") +} + +// isTypedValue checks whether e is type gno.TypedValue. +func (m *mapping) isTypedValue(e ast.Expr) bool { + return m.isGnoType(e, false, "TypedValue") +} + +func (m *mapping) isGnoType(e ast.Expr, star bool, typeName string) bool { + if star { + px, ok := e.(*ast.StarExpr) + if !ok { + return false + } + e = px.X + } + + sx, ok := e.(*ast.SelectorExpr) + if !ok { + return false + } + + imp := resolveSelectorImport(m.goImports, sx) + return imp == gnoPackagePath && sx.Sel.Name == typeName +} + +// iterFields iterates over gnol and gol, calling callback for each matching +// parameter. iterFields assumes the caller already checked for the "true" number +// of parameters in the two arrays to be equal (can be checked using +// (*ast.FieldList).NumFields()). +// +// If callback returns an error, iterFields returns that error immediately. +// No errors are otherwise generated. +func iterFields(gnol, gol []*ast.Field, callback func(gnoType, goType ast.Expr) error) error { + var goIdx, goNameIdx int + + for _, l := range gnol { + n := len(l.Names) + if n == 0 { + n = 1 + } + gnoe := l.Type + for i := 0; i < n; i++ { + goe := gol[goIdx].Type + + if err := callback(gnoe, goe); err != nil { + return err + } + + goNameIdx++ + if goNameIdx >= len(gol[goIdx].Names) { + goIdx++ + goNameIdx = 0 + } + } + } + return nil +} + +// mergeTypes merges gnoe and goe into a single ast.Expr. +// +// gnoe and goe are expected to have the same underlying structure, but they +// may differ in their type identifiers (possibly qualified, ie pkg.T). +// if they differ, mergeTypes returns nil. +// +// When two type identifiers are found, they are checked against the list of +// linkedTypes to determine if they refer to a linkedType. If they are not, +// mergeTypes returns nil. If they are, the *ast.Ident/*ast.SelectorExpr is +// replaced with a *linkedIdent. +// +// mergeTypes does not modify the given gnoe or goe; the returned ast.Expr is +// (recursively) newly allocated. +func (m *mapping) mergeTypes(gnoe, goe ast.Expr) ast.Expr { + resolveGoNamed := func(lt *linkedType) bool { + switch goe := goe.(type) { + case *ast.SelectorExpr: + // selector - resolve pkg ident to path + lt.goPackage = resolveSelectorImport(m.goImports, goe) + lt.goName = goe.Sel.Name + case *ast.Ident: + // local name -- use import path of go pkg + lt.goPackage = m.GoImportPath + lt.goName = goe.Name + default: + return false + } + return true + } + + switch gnoe := gnoe.(type) { + // We're working with a subset of all expressions: + // https://go.dev/ref/spec#Type + + case *ast.SelectorExpr: + lt := linkedType{ + gnoPackage: resolveSelectorImport(m.gnoImports, gnoe), + gnoName: gnoe.Sel.Name, + } + if !resolveGoNamed(<) || !linkedTypeExists(lt) { + return nil + } + return &linkedIdent{lt: lt} + case *ast.Ident: + // easy case - built-in identifiers + goi, ok := goe.(*ast.Ident) + if ok && isBuiltin(gnoe.Name) && gnoe.Name == goi.Name { + return &ast.Ident{Name: gnoe.Name} + } + + lt := linkedType{ + gnoPackage: m.GnoImportPath, + gnoName: gnoe.Name, + } + if !resolveGoNamed(<) || !linkedTypeExists(lt) { + return nil + } + return &linkedIdent{lt: lt} + + // easier cases -- check for equality of structure and underlying types + case *ast.StarExpr: + goe, ok := goe.(*ast.StarExpr) + if !ok { + return nil + } + x := m.mergeTypes(gnoe.X, goe.X) + if x == nil { + return nil + } + return &ast.StarExpr{X: x} + case *ast.ArrayType: + goe, ok := goe.(*ast.ArrayType) + if !ok || !basicLitsEqual(gnoe.Len, goe.Len) { + return nil + } + elt := m.mergeTypes(gnoe.Elt, goe.Elt) + if elt == nil { + return nil + } + var l ast.Expr + if gnoe.Len != nil { + l = &ast.BasicLit{Value: gnoe.Len.(*ast.BasicLit).Value} + } + return &ast.ArrayType{Len: l, Elt: elt} + + case *ast.StructType, + *ast.FuncType, + *ast.InterfaceType, + *ast.MapType, + *ast.Ellipsis: + // TODO + panic("not implemented") + default: + panic(fmt.Errorf("invalid expression as func param/return type: %T (%v)", gnoe, gnoe)) + } +} + +// returns full import path from package ident +func resolveImport(imports []*ast.ImportSpec, ident string) string { + for _, i := range imports { + s, err := strconv.Unquote(i.Path.Value) + if err != nil { + panic(fmt.Errorf("could not unquote import path literal: %s", i.Path.Value)) + } + + // TODO: for simplicity, if i.Name is nil we assume the name to be == + // to the last part of the import path. + // ideally, use importer to resolve package directory on user's FS and + // resolve by parsing and reading package clause + var name string + if i.Name != nil { + name = i.Name.Name + } else { + name = path.Base(s) + } + + if name == ident { + return s + } + } + return "" +} + +func resolveSelectorImport(imports []*ast.ImportSpec, sx *ast.SelectorExpr) string { + pkgIdent, ok := sx.X.(*ast.Ident) + if !ok { + panic(fmt.Errorf("encountered unhandled SelectorExpr.X type: %T (%v)", sx.X, sx)) + } + impPath := resolveImport(imports, pkgIdent.Name) + if impPath == "" { + panic(fmt.Errorf( + "unknown identifier %q (for resolving type %q)", + pkgIdent.Name, pkgIdent.Name+"."+sx.Sel.Name, + )) + } + return impPath +} + +// simple equivalence between two BasicLits. +// Note that this returns true only if the expressions are exactly the same; +// ie. 16 != 0x10, only 16 == 16. +func basicLitsEqual(x1, x2 ast.Expr) bool { + if x1 == nil || x2 == nil { + return x1 == nil && x2 == nil + } + l1, ok1 := x1.(*ast.BasicLit) + l2, ok2 := x2.(*ast.BasicLit) + if !ok1 || !ok2 { + return false + } + return l1.Value == l2.Value +} + +// Signatures match when they accept the same elementary types, or a linked +// type mapping (see [linkedTypes]). +// +// Additionally, if the first parameter to the Go function is +// *[gnolang.Machine], it is ignored when matching to the Gno function. +func (m *mapping) signaturesMatch(gnof, gof funcDecl) bool { + if gnof.Type.TypeParams != nil || gof.Type.TypeParams != nil { + panic("type parameters not supported") + } + + // if first param of go function is *gno.Machine, remove it + gofp := gof.Type.Params + if gofp != nil && len(gofp.List) > 0 && m.isGnoMachine(gofp.List[0]) { + // avoid touching original struct + n := *gofp + n.List = n.List[1:] + gofp = &n + + m.MachineParam = true + } + + return m.fieldListsMatch(gnof.Type.Params, gofp) && + m.fieldListsMatch(gnof.Type.Results, gof.Type.Results) +} + +var errNoMatch = errors.New("no match") + +func (m *mapping) fieldListsMatch(gnofl, gofl *ast.FieldList) bool { + if gnofl == nil || gofl == nil { + return gnofl == nil && gofl == nil + } + if gnofl.NumFields() != gofl.NumFields() { + return false + } + err := iterFields(gnofl.List, gofl.List, func(gnoe, goe ast.Expr) error { + // if the go type is gno.TypedValue, we just don't perform reflect-based conversion. + if m.isTypedValue(goe) { + return nil + } + if m.mergeTypes(gnoe, goe) == nil { + return errNoMatch + } + return nil + }) + return err == nil +} + +// TODO: this is created based on the uverse definitions. This should be +// centralized, or at least have a CI/make check to make sure this stays the +// same +var builtinTypes = [...]string{ + "bool", + "string", + "int", + "int8", + "int16", + "rune", + "int32", + "int64", + "uint", + "byte", + "uint8", + "uint16", + "uint32", + "uint64", + "bigint", + "float32", + "float64", + "error", +} + +func isBuiltin(name string) bool { + for _, x := range builtinTypes { + if x == name { + return true + } + } + return false +} + +type linkedType struct { + gnoPackage string + gnoName string + goPackage string + goName string +} + +var linkedTypes = [...]linkedType{ + { + "std", "Address", + "github.com/gnolang/gno/tm2/pkg/crypto", "Bech32Address", + }, + { + "std", "Coin", + "github.com/gnolang/gno/tm2/pkg/std", "Coin", + }, + { + "std", "Coins", + "github.com/gnolang/gno/tm2/pkg/std", "Coins", + }, + { + "std", "Realm", + "github.com/gnolang/gno/gnovm/stdlibs/std", "Realm", + }, + { + "std", "BankerType", + "github.com/gnolang/gno/gnovm/stdlibs/std", "BankerType", + }, + { + "std", "Banker", + "github.com/gnolang/gno/gnovm/stdlibs/std", "Banker", + }, +} + +func linkedTypeExists(lt linkedType) bool { + for _, ltx := range linkedTypes { + if lt == ltx { + return true + } + } + return false +} diff --git a/misc/genstd/mapping_test.go b/misc/genstd/mapping_test.go new file mode 100644 index 00000000000..b0cfa1bd4a7 --- /dev/null +++ b/misc/genstd/mapping_test.go @@ -0,0 +1,294 @@ +package main + +import ( + "fmt" + "go/ast" + "go/parser" + "os" + "path/filepath" + "sync" + "testing" + + "github.com/jaekwon/testify/assert" + "github.com/jaekwon/testify/require" +) + +const testdataDir = "github.com/gnolang/gno/misc/genstd/testdata/" + +var initWD = func() string { + d, err := os.Getwd() + if err != nil { + panic(err) + } + return d +}() + +func chdir(t *testing.T, s string) { + t.Helper() + + os.Chdir(filepath.Join(initWD, s)) + t.Cleanup(func() { + os.Chdir(initWD) + dirsOnce = sync.Once{} + memoGitRoot, memoRelPath = "", "" + }) +} + +func Test_linkFunctions(t *testing.T) { + chdir(t, "testdata/linkFunctions") + + pkgs, err := walkStdlibs(".") + require.NoError(t, err) + + mappings := linkFunctions(pkgs) + require.Len(t, mappings, 8) + + const ( + ret = 1 << iota + param + machine + ) + str := func(i int) string { + s := "Fn" + if i&machine != 0 { + s += "Machine" + } + if i¶m != 0 { + s += "Param" + } + if i&ret != 0 { + s += "Ret" + } + return s + } + + for i, v := range mappings { + exp := str(i) + assert.Equal(t, v.GnoFunc, exp) + assert.Equal(t, v.GoFunc, exp) + assert.Equal(t, v.GnoImportPath, "std") + assert.Equal(t, v.GoImportPath, testdataDir+"linkFunctions/std") + + assert.Equal(t, v.MachineParam, i&machine != 0, "MachineParam should match expected value") + if i¶m != 0 { + // require, otherwise the following would panic + require.Len(t, v.Params, 1) + p := v.Params[0] + assert.Equal(t, p.GnoType(), "int") + assert.Equal(t, p.GoQualifiedName(), "int") + assert.False(t, p.IsTypedValue) + } else { + assert.Len(t, v.Params, 0) + } + if i&ret != 0 { + // require, otherwise the following would panic + require.Len(t, v.Results, 1) + p := v.Results[0] + assert.Equal(t, p.GnoType(), "int") + assert.Equal(t, p.GoQualifiedName(), "int") + assert.False(t, p.IsTypedValue) + } else { + assert.Len(t, v.Results, 0) + } + } +} + +func Test_linkFunctions_unexp(t *testing.T) { + chdir(t, "testdata/linkFunctions_unexp") + + pkgs, err := walkStdlibs(".") + require.NoError(t, err) + + mappings := linkFunctions(pkgs) + require.Len(t, mappings, 2) + + assert.Equal(t, mappings[0].MachineParam, false) + assert.Equal(t, mappings[0].GnoFunc, "t1") + assert.Equal(t, mappings[0].GoFunc, "X_t1") + + assert.Equal(t, mappings[1].MachineParam, true) + assert.Equal(t, mappings[1].GnoFunc, "t2") + assert.Equal(t, mappings[1].GoFunc, "X_t2") +} + +func Test_linkFunctions_TypedValue(t *testing.T) { + chdir(t, "testdata/linkFunctions_TypedValue") + + pkgs, err := walkStdlibs(".") + require.NoError(t, err) + + mappings := linkFunctions(pkgs) + require.Len(t, mappings, 3) + + assert.Equal(t, mappings[0].MachineParam, false) + assert.Equal(t, mappings[0].GnoFunc, "TVParam") + assert.Equal(t, mappings[0].GoFunc, "TVParam") + assert.Len(t, mappings[0].Results, 0) + _ = assert.Len(t, mappings[0].Params, 1) && + assert.Equal(t, mappings[0].Params[0].IsTypedValue, true) && + assert.Equal(t, mappings[0].Params[0].GnoType(), "struct{m1 map[string]interface{}}") + + assert.Equal(t, mappings[1].MachineParam, false) + assert.Equal(t, mappings[1].GnoFunc, "TVResult") + assert.Equal(t, mappings[1].GoFunc, "TVResult") + assert.Len(t, mappings[1].Params, 0) + _ = assert.Len(t, mappings[1].Results, 1) && + assert.Equal(t, mappings[1].Results[0].IsTypedValue, true) && + assert.Equal(t, mappings[1].Results[0].GnoType(), "interface{S() map[int]Banker}") + + assert.Equal(t, mappings[2].MachineParam, true) + assert.Equal(t, mappings[2].GnoFunc, "TVFull") + assert.Equal(t, mappings[2].GoFunc, "TVFull") + assert.Len(t, mappings[2].Params, 1) + assert.Len(t, mappings[2].Results, 1) +} + +func Test_linkFunctions_noMatch(t *testing.T) { + chdir(t, "testdata/linkFunctions_noMatch") + + pkgs, err := walkStdlibs(".") + require.NoError(t, err) + + defer func() { + r := recover() + assert.NotNil(t, r) + assert.Contains(t, fmt.Sprint(r), "no matching go function declaration") + }() + + linkFunctions(pkgs) +} + +func Test_linkFunctions_noMatchSig(t *testing.T) { + chdir(t, "testdata/linkFunctions_noMatchSig") + + pkgs, err := walkStdlibs(".") + require.NoError(t, err) + + defer func() { + r := recover() + assert.NotNil(t, r) + assert.Contains(t, fmt.Sprint(r), "doesn't match signature of go function") + }() + + linkFunctions(pkgs) +} + +// mergeTypes - separate tests. + +var mergeTypesMapping = &mapping{ + GnoImportPath: "std", + GnoFunc: "Fn", + GoImportPath: "github.com/gnolang/gno/gnovm/stdlibs/std", + GoFunc: "Fn", + goImports: []*ast.ImportSpec{ + { + Name: &ast.Ident{Name: "gno"}, + Path: &ast.BasicLit{Value: `"github.com/gnolang/gno/gnovm/pkg/gnolang"`}, + }, + { + Path: &ast.BasicLit{Value: `"github.com/gnolang/gno/tm2/pkg/crypto"`}, + }, + }, + gnoImports: []*ast.ImportSpec{ + { + // cheating a bit -- but we currently only have linked types in `std`. + Path: &ast.BasicLit{Value: `"std"`}, + }, + { + Path: &ast.BasicLit{Value: `"math"`}, + }, + }, +} + +func Test_mergeTypes(t *testing.T) { + tt := []struct { + gnoe, goe string + result ast.Expr + }{ + {"int", "int", &ast.Ident{Name: "int"}}, + {"*[11][]rune", "*[11][]rune", &ast.StarExpr{ + X: &ast.ArrayType{Len: &ast.BasicLit{Value: "11"}, Elt: &ast.ArrayType{ + Elt: &ast.Ident{Name: "rune"}, + }}, + }}, + + {"Address", "crypto.Bech32Address", &linkedIdent{lt: linkedType{ + gnoPackage: "std", + gnoName: "Address", + goPackage: "github.com/gnolang/gno/tm2/pkg/crypto", + goName: "Bech32Address", + }}}, + {"std.Realm", "Realm", &linkedIdent{lt: linkedType{ + gnoPackage: "std", + gnoName: "Realm", + goPackage: "github.com/gnolang/gno/gnovm/stdlibs/std", + goName: "Realm", + }}}, + } + + for _, tv := range tt { + t.Run(tv.gnoe, func(t *testing.T) { + gnoe, err := parser.ParseExpr(tv.gnoe) + require.NoError(t, err) + goe, err := parser.ParseExpr(tv.goe) + require.NoError(t, err) + + result := mergeTypesMapping.mergeTypes(gnoe, goe) + assert.Equal(t, result, tv.result) + }) + } +} + +func Test_mergeTypes_invalid(t *testing.T) { + tt := []struct { + gnoe, goe string + panic string + }{ + {"int", "string", ""}, + + {"*int", "int", ""}, + {"string", "*string", ""}, + {"*string", "*int", ""}, + + {"[]int", "[1]int", ""}, + {"[1]int", "[]int", ""}, + {"[2]int", "[2]string", ""}, + // valid, but unsupported (only BasicLits) + {"[(11)]int", "[(11)]string", ""}, + + {"Address", "string", ""}, + {"math.X", "X", ""}, + + {"map[string]string", "map[string]string", "not implemented"}, + {"func(s string)", "func(s string)", "not implemented"}, + {"interface{}", "interface{}", "not implemented"}, + {"struct{}", "struct{}", "not implemented"}, + + {"1 + 2", "1 + 2", "invalid expression"}, + + // even though semantically equal, for simplicity we don't implement + // "true" basic lit equivalence + {"[8]int", "[0x8]int", ""}, + } + + for _, tv := range tt { + t.Run(tv.gnoe, func(t *testing.T) { + gnoe, err := parser.ParseExpr(tv.gnoe) + require.NoError(t, err) + goe, err := parser.ParseExpr(tv.goe) + require.NoError(t, err) + + defer func() { + r := recover() + if tv.panic == "" { + assert.Nil(t, r) + } else { + assert.Contains(t, fmt.Sprint(r), tv.panic) + } + }() + + result := mergeTypesMapping.mergeTypes(gnoe, goe) + assert.Nil(t, result) + }) + } +} diff --git a/misc/genstd/template.tmpl b/misc/genstd/template.tmpl new file mode 100644 index 00000000000..f2cad0a851b --- /dev/null +++ b/misc/genstd/template.tmpl @@ -0,0 +1,88 @@ +// This file is autogenerated from the genstd tool (@/misc/genstd); do not edit. +// To regenerate it, run `go generate` from this directory. + +package stdlibs + +import ( + "reflect" + + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" +{{- range .Imports }} + {{ .Name }} {{ printf "%q" .Path }} +{{- end }} +) + +type nativeFunc struct { + gnoPkg string + gnoFunc gno.Name + params []gno.FieldTypeExpr + results []gno.FieldTypeExpr + f func(m *gno.Machine) +} + +var nativeFuncs = [...]nativeFunc{ +{{- range $i, $m := .Mappings }} + { + {{ printf "%q" $m.GnoImportPath }}, + {{ printf "%q" $m.GnoFunc }}, + {{- /* TODO: set nil if empty */}} + []gno.FieldTypeExpr{ + {{- range $i, $p := $m.Params }} + {Name: gno.N("p{{ $i }}"), Type: gno.X({{ printf "%q" $p.GnoType }})}, + {{- end }} + }, + []gno.FieldTypeExpr{ + {{- range $i, $r := $m.Results }} + {Name: gno.N("r{{ $i }}"), Type: gno.X({{ printf "%q" $r.GnoType }})}, + {{- end }} + }, + func(m *gno.Machine) { + {{ if $m.Params -}} + b := m.LastBlock() + var ( + {{- range $pn, $pv := $m.Params -}} + {{- if $pv.IsTypedValue }} + p{{ $pn }} = gno.NewValuePathBlock(1, {{ $pn }}, "")).TV + {{- else }} + p{{ $pn }} {{ $pv.GoQualifiedName }} + rp{{ $pn }} = reflect.ValueOf(&p{{ $pn }}).Elem() + {{- end }} + {{- end }} + ) + + {{ range $pn, $pv := $m.Params -}} + {{- if not $pv.IsTypedValue }} + gno.Gno2GoValue(b.GetPointerTo(nil, gno.NewValuePathBlock(1, {{ $pn }}, "")).TV, rp{{ $pn }}) + {{- end -}} + {{ end }} + {{- end }} + + {{ range $rn, $rv := $m.Results -}} + {{- if gt $rn 0 -}}, {{ end -}} + r{{ $rn }} + {{- end -}} + {{- if $m.Results }} := {{ end -}} + {{ $.PkgName $m.GoImportPath }}.{{ $m.GoFunc }}( + {{- if $m.MachineParam }} + m, + {{ end -}} + {{- range $pn, $pv := $m.Params -}} + p{{ $pn }}, + {{- end -}} + ) + + {{ range $rn, $rv := $m.Results -}} + {{- if $rv.IsTypedValue }} + m.PushValue(r{{ $rn }}) + {{- else }} + m.PushValue(gno.Go2GnoValue( + m.Alloc, + m.Store, + reflect.ValueOf(&r{{ $rn }}).Elem(), {{- /* necessary to support interfaces (ie. error) */}} + )) + {{- end }} + {{- end }} + }, + }, +{{- end }} +} diff --git a/misc/genstd/testdata/linkFunctions/std/std.gno b/misc/genstd/testdata/linkFunctions/std/std.gno new file mode 100644 index 00000000000..ab04b4084ba --- /dev/null +++ b/misc/genstd/testdata/linkFunctions/std/std.gno @@ -0,0 +1,23 @@ +package std + +func Fn() + +func FnRet() int + +func FnParam(n int) + +func FnParamRet(n int) int + +func FnMachine() + +func FnMachineRet() int + +func FnMachineParam(n int) + +func FnMachineParamRet(n int) int + +func Ignored() int { + // Ignored even if it has a matching go definition - + // as this one has a body. + return 1 +} diff --git a/misc/genstd/testdata/linkFunctions/std/std.go b/misc/genstd/testdata/linkFunctions/std/std.go new file mode 100644 index 00000000000..1b7a791c6cc --- /dev/null +++ b/misc/genstd/testdata/linkFunctions/std/std.go @@ -0,0 +1,47 @@ +package std + +import ( + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" +) + +func Fn() { + println("call Fn") +} + +func FnRet() int { + println("call FnRet") + return 1 +} + +func FnParam(n int) { + println("call FnParam", n) +} + +func FnParamRet(n int) int { + println("call FnParamRet", n) + return 1 +} + +func FnMachine(m *gno.Machine) { + println("call FnMachine") +} + +func FnMachineRet(m *gno.Machine) int { + println("call FnMachineRet") + return 1 +} + +func FnMachineParam(m *gno.Machine, n int) { + println("call FnMachineParam", n) +} + +func FnMachineParamRet(m *gno.Machine, n int) int { + println("call FnMachineParamRet", n) + return 1 +} + +func Ignored() int { + // Ignored even if it has a matching go definition - + // as gno's has a body. + return 1 +} diff --git a/misc/genstd/testdata/linkFunctions_TypedValue/std/std.gno b/misc/genstd/testdata/linkFunctions_TypedValue/std/std.gno new file mode 100644 index 00000000000..3bba36774e3 --- /dev/null +++ b/misc/genstd/testdata/linkFunctions_TypedValue/std/std.gno @@ -0,0 +1,11 @@ +package std + +type Banker interface { + B() +} + +func TVParam(m struct{ m1 map[string]interface{} }) + +func TVResult() interface{ S() map[int]Banker } + +func TVFull(map[Banker]map[string]interface{}) (n [86]map[string]bool) diff --git a/misc/genstd/testdata/linkFunctions_TypedValue/std/std.go b/misc/genstd/testdata/linkFunctions_TypedValue/std/std.go new file mode 100644 index 00000000000..03d95721438 --- /dev/null +++ b/misc/genstd/testdata/linkFunctions_TypedValue/std/std.go @@ -0,0 +1,16 @@ +package std + +import ( + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" +) + +func TVParam(p gno.TypedValue) { +} + +func TVResult() gno.TypedValue { + return gno.TypedValue{} +} + +func TVFull(m *gno.Machine, v gno.TypedValue) gno.TypedValue { + return gno.TypedValue{} +} diff --git a/misc/genstd/testdata/linkFunctions_noMatch/std/std.gno b/misc/genstd/testdata/linkFunctions_noMatch/std/std.gno new file mode 100644 index 00000000000..2ef4be8abc6 --- /dev/null +++ b/misc/genstd/testdata/linkFunctions_noMatch/std/std.gno @@ -0,0 +1,3 @@ +package std + +func X() int diff --git a/misc/genstd/testdata/linkFunctions_noMatch/std/std.go b/misc/genstd/testdata/linkFunctions_noMatch/std/std.go new file mode 100644 index 00000000000..97399743533 --- /dev/null +++ b/misc/genstd/testdata/linkFunctions_noMatch/std/std.go @@ -0,0 +1,3 @@ +package std + +func Y() {} diff --git a/misc/genstd/testdata/linkFunctions_noMatchSig/std/std.gno b/misc/genstd/testdata/linkFunctions_noMatchSig/std/std.gno new file mode 100644 index 00000000000..75e8e10e2e3 --- /dev/null +++ b/misc/genstd/testdata/linkFunctions_noMatchSig/std/std.gno @@ -0,0 +1,3 @@ +package std + +func X(n int) int diff --git a/misc/genstd/testdata/linkFunctions_noMatchSig/std/std.go b/misc/genstd/testdata/linkFunctions_noMatchSig/std/std.go new file mode 100644 index 00000000000..7a5a0e5893b --- /dev/null +++ b/misc/genstd/testdata/linkFunctions_noMatchSig/std/std.go @@ -0,0 +1,8 @@ +package std + +import ( + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" +) + +func X(m *gno.Machine, n string) { +} diff --git a/misc/genstd/testdata/linkFunctions_unexp/std/std.gno b/misc/genstd/testdata/linkFunctions_unexp/std/std.gno new file mode 100644 index 00000000000..c4811e5e837 --- /dev/null +++ b/misc/genstd/testdata/linkFunctions_unexp/std/std.gno @@ -0,0 +1,4 @@ +package std + +func t1() int +func t2() int diff --git a/misc/genstd/testdata/linkFunctions_unexp/std/std.go b/misc/genstd/testdata/linkFunctions_unexp/std/std.go new file mode 100644 index 00000000000..023b424e87c --- /dev/null +++ b/misc/genstd/testdata/linkFunctions_unexp/std/std.go @@ -0,0 +1,13 @@ +package std + +import ( + gno "github.com/gnolang/gno/gnovm/pkg/gnolang" +) + +func X_t1() int { + return 1 +} + +func X_t2(m *gno.Machine) int { + return m.NumOps +} diff --git a/misc/genstd/util.go b/misc/genstd/util.go new file mode 100644 index 00000000000..061a9604c67 --- /dev/null +++ b/misc/genstd/util.go @@ -0,0 +1,119 @@ +package main + +import ( + "errors" + "fmt" + "os" + "os/exec" + "path" + "path/filepath" + "strings" + "sync" +) + +func runTool(importPath string) error { + shortName := path.Base(importPath) + gr := gitRoot() + + cmd := exec.Command( + "go", "run", "-modfile", filepath.Join(gr, "misc/devdeps/go.mod"), + importPath, "-w", "native.go", + ) + _, err := cmd.Output() + if err != nil { + if err, ok := err.(*exec.ExitError); ok { + return fmt.Errorf("error executing %s: %w; output: %v", shortName, err, string(err.Stderr)) + } + return fmt.Errorf("error executing %s: %w", shortName, err) + } + return nil +} + +var ( + memoGitRoot string + memoRelPath string + + dirsOnce sync.Once +) + +func gitRoot() string { + dirsOnceDo() + return memoGitRoot +} + +func relPath() string { + dirsOnceDo() + return memoRelPath +} + +func dirsOnceDo() { + dirsOnce.Do(func() { + var err error + memoGitRoot, memoRelPath, err = findDirs() + if err != nil { + panic(fmt.Errorf("could not determine git root: %w", err)) + } + }) +} + +func findDirs() (gitRoot string, relPath string, err error) { + wd, err := os.Getwd() + if err != nil { + return + } + p := wd + for { + if s, e := os.Stat(filepath.Join(p, ".git")); e == nil && s.IsDir() { + // make relPath relative to the git root + rp := strings.TrimPrefix(wd, p+string(filepath.Separator)) + // normalize separator to / + rp = strings.ReplaceAll(rp, string(filepath.Separator), "/") + return p, rp, nil + } + + if strings.HasSuffix(p, string(filepath.Separator)) { + return "", "", errors.New("root git not found") + } + + p = filepath.Dir(p) + } +} + +// pkgNameFromPath derives the package name from the given path, +// unambiguously for the most part (so safe for the code generation). +// +// The path is taken and possibly shortened if it starts with a known prefix. +// For instance, github.com/gnolang/gno/stdlibs/std simply becomes "libs_std". +// "Unsafe" characters are removed (ie. invalid for go identifiers). +func pkgNameFromPath(path string) string { + const ( + repoPrefix = "github.com/gnolang/gno/" + vmPrefix = repoPrefix + "gnovm/" + tm2Prefix = repoPrefix + "tm2/pkg/" + libsPrefix = vmPrefix + "stdlibs/" + testlibsPrefix = vmPrefix + "tests/stdlibs/" + ) + + ns := "ext" + switch { + case strings.HasPrefix(path, testlibsPrefix): + ns, path = "testlibs", path[len(testlibsPrefix):] + case strings.HasPrefix(path, libsPrefix): + ns, path = "libs", path[len(libsPrefix):] + case strings.HasPrefix(path, vmPrefix): + ns, path = "vm", path[len(vmPrefix):] + case strings.HasPrefix(path, tm2Prefix): + ns, path = "tm2", path[len(tm2Prefix):] + case strings.HasPrefix(path, repoPrefix): + ns, path = "repo", path[len(repoPrefix):] + case !strings.Contains(path, "."): + ns = "go" + } + + flds := strings.FieldsFunc(path, func(r rune) bool { + return (r < 'a' || r > 'z') && + (r < 'A' || r > 'Z') && + (r < '0' || r > '9') + }) + return ns + "_" + strings.Join(flds, "_") +} diff --git a/misc/genstd/util_test.go b/misc/genstd/util_test.go new file mode 100644 index 00000000000..f6e804d545f --- /dev/null +++ b/misc/genstd/util_test.go @@ -0,0 +1,31 @@ +package main + +import ( + "fmt" + "testing" + + "github.com/jaekwon/testify/assert" +) + +func Test_pkgNameFromPath(t *testing.T) { + tt := []struct { + input, result string + }{ + {"math", "go_math"}, + {"crypto/sha256", "go_crypto_sha256"}, + {"github.com/import/path", "ext_github_com_import_path"}, + // consecutive unsupported characters => _ + {"kebab----------case", "go_kebab_case"}, + + {"github.com/gnolang/gno/misc/test", "repo_misc_test"}, + {"github.com/gnolang/gno/tm2/pkg/crypto", "tm2_crypto"}, + {"github.com/gnolang/gno/gnovm/test", "vm_test"}, + {"github.com/gnolang/gno/gnovm/stdlibs/std", "libs_std"}, + {"github.com/gnolang/gno/gnovm/tests/stdlibs/std", "testlibs_std"}, + } + for i, tv := range tt { + t.Run(fmt.Sprintf("n%d", i+1), func(t *testing.T) { + assert.Equal(t, pkgNameFromPath(tv.input), tv.result) + }) + } +} diff --git a/misc/goscan/goscan.go b/misc/goscan/goscan.go index 38089868c35..348ada45cdf 100644 --- a/misc/goscan/goscan.go +++ b/misc/goscan/goscan.go @@ -22,11 +22,7 @@ func main() { execScan, ) - if err := cmd.ParseAndRun(context.Background(), os.Args[1:]); err != nil { - _, _ = fmt.Fprintf(os.Stderr, "%+v\n", err) - - os.Exit(1) - } + cmd.Execute(context.Background(), os.Args[1:]) } func execScan(_ context.Context, args []string) error { diff --git a/misc/list-gnophers/main.sh b/misc/list-gnophers/main.sh new file mode 100755 index 00000000000..0b230e97948 --- /dev/null +++ b/misc/list-gnophers/main.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +main() { + cd "$(dirname "$0")" + cd ../.. + fname="$(mktemp --tmpdir gno_file_commits.XXXXXXXXXX.csv)" + for file in $(list_gno_files); do + extract_file_metadata $file + done > "$fname" + cat "$fname" | sort_by_date | unique_by_author +} + +list_gno_files() { + # list .gno file in examples/, remove tests and unit tests + find ./examples -name "*.gno" | grep -v _filetest.gno | grep -v _test.gno | grep -v gno.land/r/demo/tests +} + +extract_file_metadata() { + file=$1 + # get the first commit date of the file + first_commit_date=$(git log --pretty=format:%ct --follow $file | tail -n 1) + # get the email of the first contributor of the file + email=$(git log --mailmap --pretty=format:%aE --follow $file | tail -n 1) + # print the file name, first commit date, and email + echo "$first_commit_date,$email,$file" +} + +sort_by_date() { + sort -t, -k1 +} + +unique_by_author() { + awk -F, '!seek[$2]++' +} + +main diff --git a/misc/logos/buffer.go b/misc/logos/buffer.go index 90665f79bfd..81e8d1abc75 100644 --- a/misc/logos/buffer.go +++ b/misc/logos/buffer.go @@ -7,7 +7,7 @@ import ( "github.com/gdamore/tcell/v2" ) -//---------------------------------------- +// ---------------------------------------- // Buffer // A Buffer is a buffer area in which to draw. @@ -82,7 +82,7 @@ func (bb *Buffer) DrawToScreen(s tcell.Screen) { } } -//---------------------------------------- +// ---------------------------------------- // Cell // A terminal character cell. @@ -131,10 +131,6 @@ var gDefaultSpaceTStyle = tcell.StyleDefault. Dim(true). Background(tcell.ColorGray) -var gDefaultTStyle = gDefaultStyle.GetTStyle(). - Foreground(gDefaultForeground). - Background(gDefaultBackground) - // This is where a bit of dynamic logic is performed, // namely where the attr is used to derive the final style. func (cc *Cell) GetTCellContent() (mainc rune, combc []rune, tstyle tcell.Style) { @@ -161,7 +157,7 @@ func (cc *Cell) GetTCellContent() (mainc rune, combc []rune, tstyle tcell.Style) return } -//---------------------------------------- +// ---------------------------------------- // View // analogy: "Buffer:View :: array:slice". @@ -205,7 +201,7 @@ func (bs View) GetCell(x, y int) *Cell { ) } -//---------------------------------------- +// ---------------------------------------- // BufferedView // A view onto an element. diff --git a/misc/logos/cmd/logos.go b/misc/logos/cmd/logos.go index 228895f852d..3a374fecba2 100644 --- a/misc/logos/cmd/logos.go +++ b/misc/logos/cmd/logos.go @@ -10,11 +10,6 @@ import ( "github.com/gnolang/gno/misc/logos" ) -var ( - row = 0 - style = tcell.StyleDefault -) - func main() { encoding.Register() diff --git a/misc/logos/debug.go b/misc/logos/debug.go deleted file mode 100644 index 8b2a4692bf4..00000000000 --- a/misc/logos/debug.go +++ /dev/null @@ -1,26 +0,0 @@ -package logos - -import ( - "fmt" -) - -// NOTE: the golang compiler doesn't seem to be intelligent -// enough to remove steps when const debug is True, -// so it is still faster to first check the truth value -// before calling debug.Println or debug.Printf. - -const debug debugging = false // or flip - -type debugging bool - -func (d debugging) Println(args ...interface{}) { - if d { - fmt.Println(append([]interface{}{"DEBUG:"}, args...)...) - } -} - -func (d debugging) Printf(format string, args ...interface{}) { - if d { - fmt.Printf("DEBUG: "+format, args...) - } -} diff --git a/misc/logos/types.go b/misc/logos/types.go index 944f02515d3..96e983992eb 100644 --- a/misc/logos/types.go +++ b/misc/logos/types.go @@ -7,7 +7,7 @@ import ( "github.com/gdamore/tcell/v2" ) -//---------------------------------------- +// ---------------------------------------- // Page // A Page has renderable Elem(ents). @@ -389,7 +389,7 @@ func (pg *Page) DecCursor(isVertical bool) { } } -//---------------------------------------- +// ---------------------------------------- // TextElem type TextElem struct { @@ -474,8 +474,6 @@ func (tel *TextElem) Render() (updated bool) { return true } -var ctr = 0 - func (tel *TextElem) Draw(offset Coord, view View) { minX, maxX, minY, maxY := computeIntersection(tel.Size, offset, view.Bounds) for y := minY; y < maxY; y++ { @@ -494,7 +492,7 @@ func (tel *TextElem) ProcessEventKey(ev *EventKey) bool { return false // TODO: clipboard. } -//---------------------------------------- +// ---------------------------------------- // misc. type Color = tcell.Color @@ -728,7 +726,7 @@ func (tt *Attrs) Merge(ot *Attrs) { tt.Other = ot.Other // TODO merge by key. } -//---------------------------------------- +// ---------------------------------------- // AttrFlags // NOTE: AttrFlags are merged with a simple or-assign op. @@ -752,7 +750,7 @@ type KVPair struct { Value interface{} } -//---------------------------------------- +// ---------------------------------------- // computeIntersection() // els: element size @@ -812,7 +810,7 @@ func computeIntersection(els Size, elo Coord, vws Size) (minX, maxX, minY, maxY return } -//---------------------------------------- +// ---------------------------------------- // Misc simple types type Padding struct { diff --git a/misc/logos/unicode.go b/misc/logos/unicode.go index 3bdb46cd88b..924edecc2c5 100644 --- a/misc/logos/unicode.go +++ b/misc/logos/unicode.go @@ -4,7 +4,7 @@ func isCombining(r rune) bool { return inTable(r, combining) } -//---------------------------------------- +// ---------------------------------------- // from https://github.com/mattn/go-runewidth // runewidth doesn't expose whether a character is combining or not. // TODO might as well fork both runewidth and tcell. @@ -62,15 +62,6 @@ type interval struct { type table []interval -func inTables(r rune, ts ...table) bool { - for _, t := range ts { - if inTable(r, t) { - return true - } - } - return false -} - func inTable(r rune, t table) bool { if r < t[0].first { return false diff --git a/misc/loop/Makefile b/misc/loop/Makefile new file mode 100644 index 00000000000..d18c83fb84a --- /dev/null +++ b/misc/loop/Makefile @@ -0,0 +1,44 @@ +# The startup delay (waits until the node is "ready") +DELAY ?= 10 # seconds +# The temporary backup file for transactions +BACKUP_FILE ?= $(abspath ./txs_backup.log) +# The entire txs history across all iterations +HISTORY_OUTPUT := $(abspath ./txs_history.log) + +# The gnoland binary +gnoland_bin := go run github.com/gnolang/gno/gno.land/cmd/gnoland +# The tx archive binary +tx_bin := go run github.com/gnolang/tx-archive/cmd + +# The relative gno.land directory +gnoland_dir := $(abspath ../../gno.land) + +all: loop + +start.gnoland: + cd $(gnoland_dir) && $(gnoland_bin) start -skip-failing-genesis-txs -genesis-txs-file $(HISTORY_OUTPUT) +clean.gnoland: + make -C $(gnoland_dir) fclean +.PHONY: start.gnoland clean.gnoland + +# Starts the backup service +# and backs up transactions into a file +# that is wiped on every loop +tx.backup: + sleep $(DELAY) + $(tx_bin) backup -legacy -watch -overwrite -output-path "$(BACKUP_FILE)" +.PHONY: tx.backup + +# Saves the history from previous iterations into +# a temporary transactions log +save.history: + @test -e $(BACKUP_FILE) || (echo "No existing backup file not found: '$(BACKUP_FILE)'"; exit 1) + cat $(BACKUP_FILE) >> $(HISTORY_OUTPUT) +.PHONY: save.history + +loop: clean.gnoland + # backup history, if needed + $(MAKE) save.history || true + # run our dev loop + ./run_loop.sh +.PHONY: loop diff --git a/misc/loop/go.mod b/misc/loop/go.mod new file mode 100644 index 00000000000..6e81efe3df8 --- /dev/null +++ b/misc/loop/go.mod @@ -0,0 +1,62 @@ +module loop + +go 1.20 + +require ( + github.com/gnolang/gno v0.0.0-20231112174927-b1a53c018ea4 + github.com/gnolang/tx-archive v0.1.1 +) + +require ( + dario.cat/mergo v1.0.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect + github.com/btcsuite/btcd/btcutil v1.1.3 // indirect + github.com/cespare/xxhash v1.1.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cockroachdb/apd/v3 v3.2.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect + github.com/dgraph-io/badger/v3 v3.2103.5 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect + github.com/gnolang/goleveldb v0.0.9 // indirect + github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/glog v1.1.2 // indirect + github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/flatbuffers v1.12.1 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/jaekwon/testify v1.6.1 // indirect + github.com/jmhodges/levigo v1.0.0 // indirect + github.com/klauspost/compress v1.12.3 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/linxGnu/grocksdb v1.8.11 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/peterbourgon/ff/v3 v3.4.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rs/cors v1.10.1 // indirect + github.com/stretchr/testify v1.8.4 // indirect + github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect + go.etcd.io/bbolt v1.3.8 // indirect + go.opencensus.io v0.22.5 // indirect + go.uber.org/multierr v1.10.0 // indirect + go.uber.org/zap v1.26.0 // indirect + go.uber.org/zap/exp v0.2.0 // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/term v0.16.0 // indirect + golang.org/x/tools v0.17.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect + google.golang.org/grpc v1.60.1 // indirect + google.golang.org/protobuf v1.32.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/gnolang/gno => ../../ diff --git a/misc/loop/go.sum b/misc/loop/go.sum new file mode 100644 index 00000000000..ed31b7ca9f7 --- /dev/null +++ b/misc/loop/go.sum @@ -0,0 +1,308 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= +github.com/btcsuite/btcd v0.23.0 h1:V2/ZgjfDFIygAX3ZapeigkVBoVUtOJKSwrhZdlpSvaA= +github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= +github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= +github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +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/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= +github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0= +github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A= +github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +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/gnolang/goleveldb v0.0.9 h1:Q7rGko9oXMKtQA+Apeeed5a3sjba/mcDhzJGoTVLCKE= +github.com/gnolang/goleveldb v0.0.9/go.mod h1:Dz6p9bmpy/FBESTgduiThZt5mToVDipcHGzj/zUOo8E= +github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk= +github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8= +github.com/gnolang/tx-archive v0.1.1 h1:maVdRFsc1ptVhwVw1p5scvu2Rus8Yk3o9qlss5+SRCw= +github.com/gnolang/tx-archive v0.1.1/go.mod h1:MrUmRaU6GB9tOPy+5pCe/x1z1fGYtAypVJzKOExeUHY= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= +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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= +github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jaekwon/testify v1.6.1 h1:4AtAJcR9GzXN5W4DdY7ie74iCPiJV1JJUJL90t2ZUyw= +github.com/jaekwon/testify v1.6.1/go.mod h1:Oun0RXIHI7osufabQ60i4Lqkj0GXLbqI1I7kgzBNm1U= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/linxGnu/grocksdb v1.8.11 h1:BGol9e5gB1BrsTvOxloC88pe70TCqgrfLNwkyWW0kD8= +github.com/linxGnu/grocksdb v1.8.11/go.mod h1:xZCIb5Muw+nhbDK4Y5UJuOrin5MceOuiXkVUR7vp4WY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 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.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/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc= +github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +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/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= +github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw= +go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= +go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap/exp v0.2.0 h1:FtGenNNeCATRB3CmB/yEUnjEFeJWpB/pMcy7e2bKPYs= +go.uber.org/zap/exp v0.2.0/go.mod h1:t0gqAIdh1MfKv9EwN/dLwfZnJxe9ITAZN78HEWPFWDQ= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +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-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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/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-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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= +google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +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/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/misc/loop/run_loop.sh b/misc/loop/run_loop.sh new file mode 100755 index 00000000000..4b186df9a71 --- /dev/null +++ b/misc/loop/run_loop.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# This script is meant to orchestrate +# a parallel execution of a gno.land node +# and a backup tool that preserves +# transactions that happen on-chain while +# the node is running. Additionally, the +# script also closes down any hanging process +# if either the node / backup tool fail + +set -e # exit on error + +# Set up the kill signal callback +teardown() { + echo "Stopping background processes..." + kill 0 +} + +echo "Running local development setup" + +# Start the gnoland node (fresh chain), and in parallel +# - start the backup service for transactions +( + echo "Starting Gno node..." + make start.gnoland + teardown +) & +( + echo "Starting backup..." + make tx.backup + teardown +) & + +# Trap all kill signals +trap 'teardown' INT + +# Wait for all background processes to finish +wait diff --git a/misc/loop/tools.go b/misc/loop/tools.go new file mode 100644 index 00000000000..2c4cd3315fd --- /dev/null +++ b/misc/loop/tools.go @@ -0,0 +1,6 @@ +package tools + +import ( + _ "github.com/gnolang/gno/gno.land/cmd/gnoland" + _ "github.com/gnolang/tx-archive/cmd" +) diff --git a/misc/mod_tidy.sh b/misc/mod_tidy.sh new file mode 100755 index 00000000000..eb65bf8ee25 --- /dev/null +++ b/misc/mod_tidy.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# This script finds and tidies all go.mod +# files recursively from the repository root and +# optionally verifies the last echo based on an +# environment variable VERIFY_MOD_SUMS + +set -e # exit on error + +# CD into the repo root +cd .. + +# Check for the verify argument +verify=${VERIFY_MOD_SUMS:-false} + +# Find all go.mod files +gomods=$(find . -type f -name go.mod) + +if $verify; then + # Calculate sums for all go.mod files + sums=$(shasum $gomods) +fi + +# Tidy each go.mod file +for modfile in $gomods; do + dir=$(dirname "$modfile") + + # Run go mod tidy in the directory + (cd "$dir" && go mod tidy -v) || exit 1 +done + +# Optionally verify the sums +if $verify; then + echo "Verifying sums..." + echo "$sums" | shasum -c +else + echo "Skipping sum verification" +fi diff --git a/netlify.toml b/netlify.toml new file mode 100644 index 00000000000..40903918f30 --- /dev/null +++ b/netlify.toml @@ -0,0 +1,45 @@ + +# Settings in the [build] context are global and are applied to +# all contexts unless otherwise overridden by more specific contexts. +[build] + # Directory where the build system installs dependencies + # and runs your build. Store your package.json, .nvmrc, etc here. + # If not set, defaults to the root directory. + base = "misc/docusaurus" + + # Directory that contains the deploy-ready HTML files and + # assets generated by the build. This is an absolute path relative + # to the base directory, which is the root by default (/). + # This sample publishes the directory located at the absolute + # path "root/project/build-output" + + publish = "build" + + # Default build command. + command = "yarn build && yarn install" + + ignore = "git diff --quiet $COMMIT_REF $CACHED_COMMIT_REF -- . ../../docs ../../netlify.toml" + +[[plugins]] + # Installs the Lighthouse Build Plugin for all deploy contexts + package = "@netlify/plugin-lighthouse" + +# [[headers]] +# # Define which paths this specific [[headers]] block will cover. +# for = "/*" + +# [headers.values] +# X-Frame-Options = "DENY" +# X-XSS-Protection = "1; mode=block" +# Content-Security-Policy = "frame-ancestors https://www.facebook.com" + +# # Multi-value headers are expressed with multi-line strings. +# cache-control = ''' +# max-age=0, +# no-cache, +# no-store, +# must-revalidate''' + +# # Basic-Auth allows you to password protect your whole site. +# # This feature may not be available on all plans. +# Basic-Auth = "someuser:somepassword anotheruser:anotherpassword" diff --git a/tm2/Makefile b/tm2/Makefile index a5a8b8ade0d..0fa231415b2 100644 --- a/tm2/Makefile +++ b/tm2/Makefile @@ -27,6 +27,11 @@ GOFMT_FLAGS ?= -w fmt: $(rundep) mvdan.cc/gofumpt $(GOFMT_FLAGS) . +.PHONY: imports +GOIMPORTS_FLAGS ?= -w +imports: + $(rundep) golang.org/x/tools/cmd/goimports $(GOIMPORTS_FLAGS) . + .PHONY: lint lint: $(rundep) github.com/golangci/golangci-lint/cmd/golangci-lint run --config ../.github/golangci.yml ./... diff --git a/tm2/README.md b/tm2/README.md index 101fa793e82..c4d6aa8d287 100644 --- a/tm2/README.md +++ b/tm2/README.md @@ -22,7 +22,7 @@ * Minimal code - keep total footprint small. * Minimal dependencies - all dependencies must get audited, and become part of the repo. -* Modular dependencies - whereever reasonable, make components modular. +* Modular dependencies - wherever reasonable, make components modular. * Completeness - software projects that don't become finished are projects that are forever vulnerable. One of the primary goals of the Gno language and related works is to become finished within a reasonable timeframe. diff --git a/tm2/pkg/amino/amino_test.go b/tm2/pkg/amino/amino_test.go index a06601a43da..d7dd0dc5b98 100644 --- a/tm2/pkg/amino/amino_test.go +++ b/tm2/pkg/amino/amino_test.go @@ -10,6 +10,8 @@ import ( ) func TestMarshal(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() type SimpleStruct struct { @@ -35,6 +37,8 @@ func TestMarshal(t *testing.T) { } func TestUnmarshalReader(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() type SimpleStruct struct { @@ -65,6 +69,8 @@ type stringWrapper struct { } func TestUnmarshalReaderSize(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() s1 := stringWrapper{"foo"} @@ -82,6 +88,8 @@ func TestUnmarshalReaderSize(t *testing.T) { } func TestUnmarshalReaderSizeLimit(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() s1 := stringWrapper{"foo"} @@ -101,6 +109,8 @@ func TestUnmarshalReaderSizeLimit(t *testing.T) { } func TestUnmarshalReaderTooLong(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() type SimpleStruct struct { @@ -125,6 +135,8 @@ func TestUnmarshalReaderTooLong(t *testing.T) { } func TestUnmarshalBufferedWritesReads(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() buf := bytes.NewBuffer(nil) @@ -155,6 +167,8 @@ func TestUnmarshalBufferedWritesReads(t *testing.T) { } func TestBoolPointers(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() type SimpleStruct struct { BoolPtrTrue *bool diff --git a/tm2/pkg/amino/binary_decode.go b/tm2/pkg/amino/binary_decode.go index 8ac4161cede..333994d60b0 100644 --- a/tm2/pkg/amino/binary_decode.go +++ b/tm2/pkg/amino/binary_decode.go @@ -12,14 +12,6 @@ const bdOptionByte = 0x01 // ---------------------------------------- // cdc.decodeReflectBinary -var ErrOverflowInt = errors.New("encoded integer value overflows int(32)") - -const ( - // architecture dependent int limits: - maxInt = int(^uint(0) >> 1) - minInt = -maxInt - 1 -) - // This is the main entrypoint for decoding all types from binary form. This // function calls decodeReflectBinary*, and generally those functions should // only call this one, for overrides all happen here. diff --git a/tm2/pkg/amino/binary_encode_test.go b/tm2/pkg/amino/binary_encode_test.go index 8208bb74c67..a775a911cf6 100644 --- a/tm2/pkg/amino/binary_encode_test.go +++ b/tm2/pkg/amino/binary_encode_test.go @@ -8,6 +8,8 @@ import ( ) func TestEncodeFieldNumberAndTyp3_1(t *testing.T) { + t.Parallel() + buf := new(bytes.Buffer) err := encodeFieldNumberAndTyp3(buf, 1, Typ3ByteLength) assert.Nil(t, err) @@ -15,6 +17,8 @@ func TestEncodeFieldNumberAndTyp3_1(t *testing.T) { } func TestEncodeFieldNumberAndTyp3_2(t *testing.T) { + t.Parallel() + buf := new(bytes.Buffer) err := encodeFieldNumberAndTyp3(buf, 2, Typ3ByteLength) assert.Nil(t, err) diff --git a/tm2/pkg/amino/binary_test.go b/tm2/pkg/amino/binary_test.go index 8516ac2e1fa..e50427afeef 100644 --- a/tm2/pkg/amino/binary_test.go +++ b/tm2/pkg/amino/binary_test.go @@ -12,6 +12,8 @@ import ( ) func TestNilSliceEmptySlice(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() type TestStruct struct { @@ -59,6 +61,8 @@ func TestNilSliceEmptySlice(t *testing.T) { } func TestNewFieldBackwardsCompatibility(t *testing.T) { + t.Parallel() + type V1 struct { String string String2 string @@ -109,6 +113,8 @@ func TestNewFieldBackwardsCompatibility(t *testing.T) { } func TestWriteEmpty(t *testing.T) { + t.Parallel() + type Inner struct { Val int } @@ -136,6 +142,8 @@ func TestWriteEmpty(t *testing.T) { } func TestForceWriteEmpty(t *testing.T) { + t.Parallel() + type InnerWriteEmpty struct { // sth. that isn't zero-len if default, e.g. fixed32: ValIn int32 `amino:"write_empty" binary:"fixed32"` @@ -158,6 +166,8 @@ func TestForceWriteEmpty(t *testing.T) { } func TestStructSlice(t *testing.T) { + t.Parallel() + type Foo struct { A uint B uint @@ -182,6 +192,8 @@ func TestStructSlice(t *testing.T) { } func TestStructPointerSlice1(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() type Foo struct { @@ -220,6 +232,8 @@ func TestStructPointerSlice1(t *testing.T) { // Like TestStructPointerSlice2, but without nil_elements field tag. func TestStructPointerSlice2(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() type Foo struct { @@ -251,6 +265,8 @@ func TestStructPointerSlice2(t *testing.T) { } func TestBasicTypes(t *testing.T) { + t.Parallel() + // we explicitly disallow type definitions like the following: type byteAlias []byte @@ -268,6 +284,8 @@ func TestBasicTypes(t *testing.T) { } func TestUnmarshalMapBinary(t *testing.T) { + t.Parallel() + obj := new(map[string]int) cdc := amino.NewCodec() @@ -291,6 +309,8 @@ func TestUnmarshalMapBinary(t *testing.T) { } func TestUnmarshalFuncBinary(t *testing.T) { + t.Parallel() + obj := func() {} cdc := amino.NewCodec() // Binary doesn't support decoding to a func... @@ -316,6 +336,8 @@ func TestUnmarshalFuncBinary(t *testing.T) { } func TestDuration(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() d0 := time.Duration(0) bz := cdc.MustMarshal(d0) diff --git a/tm2/pkg/amino/byteslice_test.go b/tm2/pkg/amino/byteslice_test.go index 310b294a8b7..80faca8e4e9 100644 --- a/tm2/pkg/amino/byteslice_test.go +++ b/tm2/pkg/amino/byteslice_test.go @@ -6,6 +6,8 @@ import ( ) func TestReadByteSliceEquality(t *testing.T) { + t.Parallel() + var encoded []byte var err error cdc := NewCodec() diff --git a/tm2/pkg/amino/codec.go b/tm2/pkg/amino/codec.go index 6300ae04673..602c7090d91 100644 --- a/tm2/pkg/amino/codec.go +++ b/tm2/pkg/amino/codec.go @@ -532,7 +532,7 @@ func (cdc *Codec) getTypeInfoFromFullnameRLock(fullname string, fopts FieldOptio info, ok := cdc.fullnameToTypeInfo[fullname] if !ok { - err = fmt.Errorf("unrecognized concrete type full name %s of %v", fullname, cdc.fullnameToTypeInfo) + err = fmt.Errorf("amino: unrecognized concrete type full name %s", fullname) cdc.mtx.RUnlock() return } diff --git a/tm2/pkg/amino/codec_test.go b/tm2/pkg/amino/codec_test.go index 7e025f73271..9368d4ef40e 100644 --- a/tm2/pkg/amino/codec_test.go +++ b/tm2/pkg/amino/codec_test.go @@ -29,6 +29,8 @@ func newSimpleStruct() SimpleStruct { } func TestMarshalUnmarshalPointer0(t *testing.T) { + t.Parallel() + s := newSimpleStruct() cdc := amino.NewCodec() b, err := cdc.MarshalSized(s) // no indirection @@ -41,6 +43,8 @@ func TestMarshalUnmarshalPointer0(t *testing.T) { } func TestMarshalUnmarshalPointer1(t *testing.T) { + t.Parallel() + s := newSimpleStruct() cdc := amino.NewCodec() b, err := cdc.MarshalSized(&s) // extra indirection @@ -53,6 +57,8 @@ func TestMarshalUnmarshalPointer1(t *testing.T) { } func TestMarshalUnmarshalPointer2(t *testing.T) { + t.Parallel() + s := newSimpleStruct() ptr := &s cdc := amino.NewCodec() @@ -63,6 +69,8 @@ func TestMarshalUnmarshalPointer2(t *testing.T) { } func TestMarshalUnmarshalPointer3(t *testing.T) { + t.Parallel() + s := newSimpleStruct() cdc := amino.NewCodec() b, err := cdc.MarshalSized(s) // no indirection @@ -75,6 +83,8 @@ func TestMarshalUnmarshalPointer3(t *testing.T) { } func TestDecodeVarint8(t *testing.T) { + t.Parallel() + // DecodeVarint8 uses binary.Varint so we need to make // sure that all the values out of the range of [-128, 127] // return an error. @@ -122,6 +132,8 @@ func TestDecodeVarint8(t *testing.T) { } func TestDecodeVarint16(t *testing.T) { + t.Parallel() + // DecodeVarint16 uses binary.Varint so we need to make // sure that all the values out of the range of [-32768, 32767] // return an error. @@ -170,6 +182,8 @@ func TestDecodeVarint16(t *testing.T) { } func TestEncodeDecodeString(t *testing.T) { + t.Parallel() + s := "🔌🎉⛵︎♠️⎍" bs := []byte(s) di := len(bs) * 3 / 4 @@ -214,8 +228,7 @@ func TestEncodeDecodeString(t *testing.T) { } func TestCodecSeal(t *testing.T) { - type Foo interface{} - type Bar interface{} + t.Parallel() cdc := amino.NewCodec() cdc.Seal() diff --git a/tm2/pkg/amino/deep_copy_test.go b/tm2/pkg/amino/deep_copy_test.go index 55aff4c6a60..f2a793bfca1 100644 --- a/tm2/pkg/amino/deep_copy_test.go +++ b/tm2/pkg/amino/deep_copy_test.go @@ -15,6 +15,8 @@ func (dcf *DCFoo1) MarshalAmino() (string, error) { return dcf.a, nil } func (dcf *DCFoo1) UnmarshalAmino(s string) error { dcf.a = s; return nil } func TestDeepCopyFoo1(t *testing.T) { + t.Parallel() + dcf1 := newDCFoo1("foobar") dcf2 := amino.DeepCopy(dcf1).(*DCFoo1) assert.Equal(t, "foobar", dcf2.a) @@ -27,6 +29,8 @@ func (dcf DCFoo2) MarshalAmino() (string, error) { return dcf.a, nil } // non-p func (dcf *DCFoo2) UnmarshalAmino(s string) error { dcf.a = s; return nil } func TestDeepCopyFoo2(t *testing.T) { + t.Parallel() + dcf1 := newDCFoo2("foobar") dcf2 := amino.DeepCopy(dcf1).(*DCFoo2) assert.Equal(t, "foobar", dcf2.a) @@ -39,6 +43,8 @@ func (dcf DCFoo3) MarshalAmino() (string, error) { return dcf.a, nil } func (dcf *DCFoo3) UnmarshalAmino(s []byte) error { dcf.a = string(s); return nil } // mismatch type func TestDeepCopyFoo3(t *testing.T) { + t.Parallel() + dcf1 := newDCFoo3("foobar") dcf2 := amino.DeepCopy(dcf1).(*DCFoo3) assert.Equal(t, "", dcf2.a) @@ -52,6 +58,8 @@ func (dcf DCFoo4) MarshalAmino() (string, error) { return dcf.a, nil } func (dcf *DCFoo4) UnmarshalAmino(s string) error { dcf.a = s; return nil } // mismatch type func TestDeepCopyFoo4(t *testing.T) { + t.Parallel() + dcf1 := newDCFoo4("foobar") dcf2 := amino.DeepCopy(dcf1).(*DCFoo4) assert.Equal(t, "good", dcf2.a) @@ -65,6 +73,8 @@ func (dcf DCFoo5) MarshalAmino() (string, error) { return dcf.a, nil } func (dcf *DCFoo5) UnmarshalAmino(s string) error { dcf.a = s; return nil } // mismatch type func TestDeepCopyFoo5(t *testing.T) { + t.Parallel() + dcf1 := newDCFoo5("foobar") dcf2 := amino.DeepCopy(dcf1).(*DCFoo5) assert.Equal(t, "good", dcf2.a) @@ -76,6 +86,8 @@ func newDCFoo6(a string) *DCFoo6 { return &DCFoo6{a: a} } func (dcf *DCFoo6) DeepCopy() DCFoo6 { return DCFoo6{"good"} } func TestDeepCopyFoo6(t *testing.T) { + t.Parallel() + dcf1 := newDCFoo6("foobar") dcf2 := amino.DeepCopy(dcf1).(*DCFoo6) assert.Equal(t, "good", dcf2.a) @@ -87,6 +99,8 @@ func newDCFoo7(a string) *DCFoo7 { return &DCFoo7{a: a} } func (dcf DCFoo7) DeepCopy() *DCFoo7 { return &DCFoo7{"good"} } func TestDeepCopyFoo7(t *testing.T) { + t.Parallel() + dcf1 := newDCFoo7("foobar") dcf2 := amino.DeepCopy(dcf1).(*DCFoo7) assert.Equal(t, "good", dcf2.a) @@ -99,6 +113,8 @@ func (dcf DCFoo8) MarshalAmino() (string, error) { return "", errors.New("uh oh func (dcf *DCFoo8) UnmarshalAmino(s string) error { dcf.a = s; return nil } func TestDeepCopyFoo8(t *testing.T) { + t.Parallel() + dcf1 := newDCFoo8("foobar") assert.Panics(t, func() { amino.DeepCopy(dcf1) }) } @@ -110,6 +126,8 @@ func (dcf DCFoo9) MarshalAmino() (string, error) { return dcf.a, nil } func (dcf *DCFoo9) UnmarshalAmino(s string) error { return errors.New("uh oh") } // error func TestDeepCopyFoo9(t *testing.T) { + t.Parallel() + dcf1 := newDCFoo9("foobar") assert.Panics(t, func() { amino.DeepCopy(dcf1) }) } @@ -119,12 +137,16 @@ type DCInterface1 struct { } func TestDeepCopyInterface1(t *testing.T) { + t.Parallel() + dci1 := DCInterface1{Foo: nil} dci2 := amino.DeepCopy(dci1).(DCInterface1) assert.Nil(t, dci2.Foo) } func TestDeepCopyInterface2(t *testing.T) { + t.Parallel() + dci1 := DCInterface1{Foo: "foo"} dci2 := amino.DeepCopy(dci1).(DCInterface1) assert.Equal(t, "foo", dci2.Foo) diff --git a/tm2/pkg/amino/encoder_test.go b/tm2/pkg/amino/encoder_test.go index 5c8afc4833d..02ece6fd667 100644 --- a/tm2/pkg/amino/encoder_test.go +++ b/tm2/pkg/amino/encoder_test.go @@ -7,6 +7,8 @@ import ( ) func TestUvarintSize(t *testing.T) { + t.Parallel() + testCases := []struct { name string u uint64 @@ -22,7 +24,10 @@ func TestUvarintSize(t *testing.T) { {"64 bits", 1 << 63, 10}, } for i, tc := range testCases { + tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() + require.Equal(t, tc.want, UvarintSize(tc.u), "#%d", i) //nolint:scopelint }) } diff --git a/tm2/pkg/amino/gengo/gengo_test.go b/tm2/pkg/amino/gengo/gengo_test.go index 88b25d9acdd..404b1c77c23 100644 --- a/tm2/pkg/amino/gengo/gengo_test.go +++ b/tm2/pkg/amino/gengo/gengo_test.go @@ -15,6 +15,8 @@ type SampleStruct struct { } func TestBasic(t *testing.T) { + t.Parallel() + p := press.NewPress() fmt.Println(p) ss := SampleStruct{"cat", "dog"} diff --git a/tm2/pkg/amino/genproto/bindings.go b/tm2/pkg/amino/genproto/bindings.go index 458d8b14578..5d3b46c59fc 100644 --- a/tm2/pkg/amino/genproto/bindings.go +++ b/tm2/pkg/amino/genproto/bindings.go @@ -14,6 +14,7 @@ import ( "strings" "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/amino/genproto/stringutil" "github.com/gnolang/gno/tm2/pkg/amino/pkg" ) @@ -105,7 +106,7 @@ func generateMethodsForType(imports *ast.GenDecl, scope *ast.Scope, pkg *amino.P } dpbote_ := pbote_[1:] - ////////////////// + // ----------- // ToPBMessage() { scope2 := ast.NewScope(scope) @@ -127,7 +128,7 @@ func generateMethodsForType(imports *ast.GenDecl, scope *ast.Scope, pkg *amino.P )) } - ////////////////// + // ----------- // EmptyPBMessage() // Use to create the pbm to proto.Unmarshal to before FromPBMessage. { @@ -148,7 +149,7 @@ func generateMethodsForType(imports *ast.GenDecl, scope *ast.Scope, pkg *amino.P )) } - ////////////////// + // ----------- // FromPBMessage() { scope2 := ast.NewScope(scope) @@ -169,7 +170,7 @@ func generateMethodsForType(imports *ast.GenDecl, scope *ast.Scope, pkg *amino.P )) } - ////////////////// + // ----------- // TypeUrl() { methods = append(methods, _func("GetTypeURL", @@ -182,7 +183,7 @@ func generateMethodsForType(imports *ast.GenDecl, scope *ast.Scope, pkg *amino.P )) } - ////////////////// + // ----------- // Is*ReprEmpty() { rinfo := info.ReprType @@ -524,7 +525,8 @@ func go2pbStmts(rootPkg *amino.Package, isRoot bool, imports *ast.GenDecl, scope goorfIsPtr := field.IsPtr() goorfType := field.TypeInfo goorf := _sel(goor, field.Name) // next goo - pbof := _sel(pbo, field.Name) // next pbo + // The protobuf field is lower_snake_case and protoc converts it to CamelCase. + pbof := _sel(pbo, CamelCase(stringutil.ToLowerSnakeCase(field.Name))) // next pbo // Translate in place. scope2 := ast.NewScope(scope) @@ -821,7 +823,8 @@ func pb2goStmts(rootPkg *amino.Package, isRoot bool, imports *ast.GenDecl, scope case reflect.Struct: for _, field := range goorType.Fields { - pbof := _sel(pbo, field.Name) // next pbo. + // The protobuf field is lower_snake_case and protoc converts it to CamelCase. + pbof := _sel(pbo, CamelCase(stringutil.ToLowerSnakeCase(field.Name))) // next pbo. goorfIsPtr := field.IsPtr() goorfType := field.TypeInfo goorf := _sel(goor, field.Name) // next goor. @@ -965,7 +968,7 @@ func isReprEmptyStmts(rootPkg *amino.Package, isRoot bool, imports *ast.GenDecl, return b } -//---------------------------------------- +// ---------------------------------------- // other.... // Splits a Go expression into left and right parts. @@ -1013,7 +1016,7 @@ func chopRight(expr string) (left string, tok rune, right string) { return } -//---------------------------------------- +// ---------------------------------------- // AST Construction (Expr) func _i(name string) *ast.Ident { @@ -1023,14 +1026,6 @@ func _i(name string) *ast.Ident { return &ast.Ident{Name: name} } -func _iOrNil(name string) *ast.Ident { - if name == "" { - return nil - } else { - return _i(name) - } -} - // recvTypeName is empty if there are no receivers. // recvTypeName cannot contain any dots. func _func(name string, recvRef string, recvTypeName string, params *ast.FieldList, results *ast.FieldList, b *ast.BlockStmt) *ast.FuncDecl { @@ -1344,29 +1339,6 @@ func _x(expr string, args ...interface{}) ast.Expr { // 3 == != < <= > >= // 2 && // 1 || -var sp = " " - -var ( - prec5 = strings.Split("* / % << >> & &^", sp) - prec4 = strings.Split("+ - | ^", sp) - prec3 = strings.Split("== != < <= > >=", sp) - prec2 = strings.Split("&&", sp) - prec1 = strings.Split("||", sp) - precs = [][]string{prec1, prec2, prec3, prec4, prec5} -) - -// 0 for prec1... -1 if no match. -func lowestMatch(op string) int { - for i, prec := range precs { - for _, op2 := range prec { - if op == op2 { - return i - } - } - } - return -1 -} - func _kv(k, v interface{}) *ast.KeyValueExpr { var kx, vx ast.Expr if ks, ok := k.(string); ok { @@ -1391,10 +1363,6 @@ func _block(b ...ast.Stmt) *ast.BlockStmt { } } -func _xs(exprs ...ast.Expr) []ast.Expr { - return exprs -} - // Usage: _a(lhs1, lhs2, ..., ":=", rhs1, rhs2, ...) // Token can be ":=", "=", "+=", etc. // Other strings are automatically parsed as _x(arg). @@ -1470,13 +1438,6 @@ func _call(fn ast.Expr, args ...ast.Expr) *ast.CallExpr { } } -func _ta(x ast.Expr, t ast.Expr) *ast.TypeAssertExpr { - return &ast.TypeAssertExpr{ - X: x, - Type: t, - } -} - func _sel(x ast.Expr, sel string) *ast.SelectorExpr { return &ast.SelectorExpr{ X: x, @@ -1532,7 +1493,7 @@ func _sl(x ast.Expr) *ast.ArrayType { } } -//---------------------------------------- +// ---------------------------------------- // AST Construction (Stmt) func _if(cond ast.Expr, b ...ast.Stmt) *ast.IfStmt { @@ -1564,34 +1525,6 @@ func _return(results ...ast.Expr) *ast.ReturnStmt { } } -func _continue(label string) *ast.BranchStmt { - return &ast.BranchStmt{ - Tok: token.CONTINUE, - Label: _i(label), - } -} - -func _break(label string) *ast.BranchStmt { - return &ast.BranchStmt{ - Tok: token.BREAK, - Label: _i(label), - } -} - -func _goto(label string) *ast.BranchStmt { - return &ast.BranchStmt{ - Tok: token.GOTO, - Label: _i(label), - } -} - -func _fallthrough(label string) *ast.BranchStmt { - return &ast.BranchStmt{ - Tok: token.FALLTHROUGH, - Label: _i(label), - } -} - // even/odd args are paired, // name1, path1, name2, path2, etc. func _imports(nameAndPaths ...string) *ast.GenDecl { @@ -1618,15 +1551,6 @@ func _for(init ast.Stmt, cond ast.Expr, post ast.Stmt, b ...ast.Stmt) *ast.ForSt } } -func _loop(b ...ast.Stmt) *ast.ForStmt { - return _for(nil, nil, nil, b...) -} - -func _once(b ...ast.Stmt) *ast.ForStmt { - b = append(b, _break("")) - return _for(nil, nil, nil, b...) -} - func _len(x ast.Expr) *ast.CallExpr { return _call(_i("len"), x) } @@ -1766,7 +1690,7 @@ func _aop(op string) token.Token { } } -//---------------------------------------- +// ---------------------------------------- // AST Compile-Time func _ctif(cond bool, then_, else_ ast.Stmt) ast.Stmt { @@ -1779,7 +1703,7 @@ func _ctif(cond bool, then_, else_ ast.Stmt) ast.Stmt { } } -//---------------------------------------- +// ---------------------------------------- // AST query and manipulation. func importPathForName(name string, imports *ast.GenDecl) (path string, exists bool) { @@ -1800,24 +1724,6 @@ func importPathForName(name string, imports *ast.GenDecl) (path string, exists b return "", false } -func importNameForPath(path string, imports *ast.GenDecl) (name string, exists bool) { - if imports.Tok != token.IMPORT { - panic("unexpected ast.GenDecl token " + imports.Tok.String()) - } - for _, spec := range imports.Specs { - if ispec, ok := spec.(*ast.ImportSpec); ok { - specPath, err := strconv.Unquote(ispec.Path.Value) - if err != nil { - panic("malformed path " + ispec.Path.Value) - } - if specPath == path { - return ispec.Name.Name, true - } - } - } - return "", false -} - func rootScope(scope *ast.Scope) *ast.Scope { for scope.Outer != nil { scope = scope.Outer diff --git a/tm2/pkg/amino/genproto/bindings_test.go b/tm2/pkg/amino/genproto/bindings_test.go index 295c388def3..d1911d43542 100644 --- a/tm2/pkg/amino/genproto/bindings_test.go +++ b/tm2/pkg/amino/genproto/bindings_test.go @@ -11,6 +11,8 @@ import ( ) func TestGenerateProtoBindings(t *testing.T) { + t.Parallel() + file, err := GenerateProtoBindingsForTypes(tests.Package, tests.Package.ReflectTypes()...) assert.NoError(t, err) t.Logf("%v", file) diff --git a/tm2/pkg/amino/genproto/comments_test.go b/tm2/pkg/amino/genproto/comments_test.go new file mode 100644 index 00000000000..5910a244b6b --- /dev/null +++ b/tm2/pkg/amino/genproto/comments_test.go @@ -0,0 +1,61 @@ +package genproto + +import ( + "path" + "testing" + + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/jaekwon/testify/assert" +) + +// message comment +type TestMessageName struct { + // field comment 1 + FieldName1 string + // field comment 2 + FieldName2 []uint64 +} + +// message comment 2 +type TestMessageName2 struct { + // another field comment + FieldName string +} + +func TestComments(t *testing.T) { + pkg := amino.RegisterPackage( + amino.NewPackage( + "github.com/gnolang/gno/tm2/pkg/amino/genproto", + "amino_test", + amino.GetCallersDirname(), + ).WithTypes( + &TestMessageName{}, + &TestMessageName2{}, + // Add comments from this same source file. + ).WithComments(path.Join(amino.GetCallersDirname(), "comments_test.go"))) + + p3c := NewP3Context() + p3c.RegisterPackage(pkg) + p3c.ValidateBasic() + p3doc := p3c.GenerateProto3SchemaForTypes(pkg, pkg.ReflectTypes()...) + proto3Schema := p3doc.Print() + assert.Equal(t, proto3Schema, `syntax = "proto3"; +package amino_test; + +option go_package = "github.com/gnolang/gno/tm2/pkg/amino/genproto/pb"; + +// messages +// message comment +message TestMessageName { + // field comment 1 + string field_name1 = 1 [json_name = "FieldName1"]; + // field comment 2 + repeated uint64 field_name2 = 2 [json_name = "FieldName2"]; +} + +// message comment 2 +message TestMessageName2 { + // another field comment + string field_name = 1 [json_name = "FieldName"]; +}`) +} diff --git a/tm2/pkg/amino/genproto/example/main.go b/tm2/pkg/amino/genproto/example/main.go index 24132305893..6e4023b596d 100644 --- a/tm2/pkg/amino/genproto/example/main.go +++ b/tm2/pkg/amino/genproto/example/main.go @@ -11,16 +11,12 @@ import ( // amino type StructA struct { - fieldA int - fieldB int FieldC int FieldD uint32 } // amino type StructB struct { - fieldA int - fieldB int FieldC int FieldD uint32 FieldE submodule.StructSM diff --git a/tm2/pkg/amino/genproto/genproto.go b/tm2/pkg/amino/genproto/genproto.go index 8af73b2690a..fb71f32783e 100644 --- a/tm2/pkg/amino/genproto/genproto.go +++ b/tm2/pkg/amino/genproto/genproto.go @@ -6,7 +6,6 @@ import ( "bytes" "errors" "fmt" - "io/ioutil" "os" "os/exec" "path" @@ -16,6 +15,7 @@ import ( "time" "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/amino/genproto/stringutil" "github.com/gnolang/gno/tm2/pkg/amino/pkg" ) @@ -120,7 +120,6 @@ func (p3c *P3Context) GetP3ImportPath(p3type P3Type, implicit bool) string { func (p3c *P3Context) GenerateProto3MessagePartial(p3doc *P3Doc, rt reflect.Type) (p3msg P3Message) { if p3doc.PackageName == "" { panic(fmt.Sprintf("cannot generate message partials in the root package \"\".")) - return } if rt.Kind() == reflect.Ptr { panic("pointers not yet supported. if you meant pointer-preferred (for decoding), pass in rt.Elem()") @@ -191,6 +190,15 @@ func (p3c *P3Context) GenerateProto3MessagePartial(p3doc *P3Doc, rt reflect.Type p3msg.Name = info.Name // not rinfo. + var fieldComments map[string]string + if rinfo.Package != nil { + if pkgType, ok := rinfo.Package.GetType(rt); ok { + p3msg.Comment = pkgType.Comment + // We will check for optional field comments below. + fieldComments = pkgType.FieldComments + } + } + // Append to p3msg.Fields, fields of the struct. for _, field := range rsfields { // rinfo. fp3, fp3IsRepeated, implicit := typeToP3Type(info.Package, field.TypeInfo, field.FieldOptions) @@ -206,9 +214,13 @@ func (p3c *P3Context) GenerateProto3MessagePartial(p3doc *P3Doc, rt reflect.Type p3Field := P3Field{ Repeated: fp3IsRepeated, Type: fp3, - Name: field.Name, + Name: stringutil.ToLowerSnakeCase(field.Name), + JSONName: field.JSONName, Number: field.FieldOptions.BinFieldNum, } + if fieldComments != nil { + p3Field.Comment = fieldComments[field.Name] + } p3msg.Fields = append(p3msg.Fields, p3Field) } @@ -220,7 +232,6 @@ func (p3c *P3Context) GenerateProto3MessagePartial(p3doc *P3Doc, rt reflect.Type func (p3c *P3Context) GenerateProto3ListPartial(p3doc *P3Doc, nl NList) (p3msg P3Message) { if p3doc.PackageName == "" { panic(fmt.Sprintf("cannot generate message partials in the root package \"\".")) - return } ep3 := nl.ElemP3Type() @@ -496,7 +507,7 @@ func RunProtoc(pkg *amino.Package, protosDir string) { } } // First generate output to a temp dir. - tempDir, err := ioutil.TempDir("", "amino-genproto") + tempDir, err := os.MkdirTemp("", "amino-genproto") if err != nil { return } diff --git a/tm2/pkg/amino/genproto/genproto_test.go b/tm2/pkg/amino/genproto/genproto_test.go index 20a45361519..22e7ea654d2 100644 --- a/tm2/pkg/amino/genproto/genproto_test.go +++ b/tm2/pkg/amino/genproto/genproto_test.go @@ -9,15 +9,17 @@ import ( ) func TestBasic(t *testing.T) { + t.Parallel() + p3c := NewP3Context() p3c.RegisterPackage(sm1.Package) p3doc := P3Doc{PackageName: "test"} obj := sm1.StructSM{} p3message := p3c.GenerateProto3MessagePartial(&p3doc, reflect.TypeOf(obj)) assert.Equal(t, p3message.Print(), `message StructSM { - sint64 FieldA = 1; - string FieldB = 2; - submodule2.StructSM2 FieldC = 3; + sint64 field_a = 1 [json_name = "FieldA"]; + string field_b = 2 [json_name = "FieldB"]; + submodule2.StructSM2 field_c = 3 [json_name = "FieldC"]; } `) @@ -38,8 +40,8 @@ import "github.com/gnolang/gno/tm2/pkg/amino/genproto/example/submodule2/submodu // messages message StructSM { - sint64 FieldA = 1; - string FieldB = 2; - submodule2.StructSM2 FieldC = 3; + sint64 field_a = 1 [json_name = "FieldA"]; + string field_b = 2 [json_name = "FieldB"]; + submodule2.StructSM2 field_c = 3 [json_name = "FieldC"]; }`) } diff --git a/tm2/pkg/amino/genproto/scanner.go b/tm2/pkg/amino/genproto/scanner.go deleted file mode 100644 index 6c5f770ab48..00000000000 --- a/tm2/pkg/amino/genproto/scanner.go +++ /dev/null @@ -1,192 +0,0 @@ -package genproto - -import "fmt" - -type runestate int - -const ( - runestateCode runestate = 0 - runestateRune runestate = 1 - runestateStringQuote runestate = 2 - runestateStringBacktick runestate = 3 -) - -type scanner struct { - str string - rnz []rune - idx int - runestate - curly int - round int - square int -} - -// returns a new scanner. -func newScanner(str string) *scanner { - rnz := make([]rune, 0, len(str)) - for _, r := range str { - rnz = append(rnz, r) - } - return &scanner{ - str: str, - runestate: runestateCode, - rnz: rnz, - } -} - -// Peeks the next n runes and returns a string. returns a shorter string if -// there are less than n runes left. -func (ss *scanner) peek(n int) string { - if ss.idx+n > len(ss.rnz) { - return string(ss.rnz[ss.idx:len(ss.rnz)]) - } - return string(ss.rnz[ss.idx : ss.idx+n]) -} - -// Advance a single rune, e.g. by incrementing ss.curly if ss.rnz[ss.idx] is -// '{' before advancing. If ss.runestate is runestateRune or runestateQuote, -// advances escape sequences to completion so ss.idx may increment more than -// one. Returns true if done. -func (ss *scanner) advance() bool { - rn := ss.rnz[ss.idx] // just panic if out of scope, caller error. - switch ss.runestate { - case runestateCode: - switch rn { - case '}': - ss.curly-- - if ss.curly < 0 { - panic("mismatched curly: " + ss.str) - } - case ')': - ss.round-- - if ss.round < 0 { - panic("mismatched round: " + ss.str) - } - case ']': - ss.square-- - if ss.square < 0 { - panic("mismatched square: " + ss.str) - } - case '{': - ss.curly++ - case '(': - ss.round++ - case '[': - ss.square++ - case '\'': - ss.runestate = runestateRune - case '"': - ss.runestate = runestateStringQuote - case '`': - ss.runestate = runestateStringBacktick - } - case runestateRune: - switch rn { - case '\\': - return ss.advanceEscapeSequence() - case '\'': - ss.runestate = runestateCode - } - case runestateStringQuote: - switch rn { - case '\\': - return ss.advanceEscapeSequence() - case '"': - ss.runestate = runestateCode - } - case runestateStringBacktick: - switch rn { - case '`': - ss.runestate = runestateCode - } - } - ss.idx++ - return ss.done() -} - -// returns true if no runes left to advance. -func (ss *scanner) done() bool { - return ss.idx == len(ss.rnz) -} - -// returns true if outside the scope of any -// parentheses, brackets, strings, or rune literals. -func (ss *scanner) out() bool { - return ss.runestate == runestateCode && - ss.curly == int(0) && - ss.round == int(0) && - ss.square == int(0) -} - -func isOctal(r rune) bool { - switch r { - case '0', '1', '2', '3', '4', '5', '6', '7': - return true - default: - return false - } -} - -func isHex(r rune) bool { - switch r { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'a', 'b', 'c', 'd', 'e', 'f', - 'A', 'B', 'C', 'D', 'E', 'F': - return true - default: - return false - } -} - -// Advances runes, while checking that each passes `check`. if error, panics -// with info including `back` runes back. -func (ss *scanner) eatRunes(back int, eat int, check func(rune) bool) { - for i := 0; i < eat; i++ { - if ss.idx+i == len(ss.rnz) { - panic(fmt.Sprintf("eof while parsing: %s", - string(ss.rnz[ss.idx-back:]))) - } - if !check(ss.rnz[ss.idx+i]) { - panic(fmt.Sprintf("invalid character while parsing: %s", - string(ss.rnz[ss.idx-back:ss.idx+i+1]))) - } - ss.idx++ - } -} - -// increments ss.idx until escape sequence is complete. returns true if done. -func (ss *scanner) advanceEscapeSequence() bool { - rn1 := ss.rnz[ss.idx] - if rn1 != '\\' { - panic("should not happen") - } - if ss.idx == len(ss.rnz)-1 { - panic("eof while parsing escape sequence") - } - rn2 := ss.rnz[ss.idx+1] - switch rn2 { - case 'x': - ss.idx += 2 - ss.eatRunes(2, 2, isHex) - return ss.done() - case 'u': - ss.idx += 2 - ss.eatRunes(2, 4, isHex) - return ss.done() - case 'U': - ss.idx += 2 - ss.eatRunes(2, 8, isHex) - return ss.done() - case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '\'', '"': - ss.idx += 2 - return ss.done() - default: - ss.idx += 1 - if isOctal(rn2) { - ss.eatRunes(1, 3, isOctal) - } else { - panic("invalid escape sequence") - } - return ss.done() - } -} diff --git a/tm2/pkg/amino/genproto/stringutil/snakecase.go b/tm2/pkg/amino/genproto/stringutil/snakecase.go new file mode 100644 index 00000000000..b7eb8c987da --- /dev/null +++ b/tm2/pkg/amino/genproto/stringutil/snakecase.go @@ -0,0 +1,335 @@ +// Copyright 2020-2023 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file has ToLowerSnakeCase which the Protobuf linter uses to make +// the expected spelling of a message field name. It is a copy of: +// https://github.com/bufbuild/buf/blob/main/private/pkg/stringutil/stringutil.go + +package stringutil + +import ( + "sort" + "strings" + "unicode" +) + +// TrimLines splits the output into individual lines and trims the spaces from each line. +// +// This also trims the start and end spaces from the original output. +func TrimLines(output string) string { + return strings.TrimSpace(strings.Join(SplitTrimLines(output), "\n")) +} + +// SplitTrimLines splits the output into individual lines and trims the spaces from each line. +func SplitTrimLines(output string) []string { + // this should work for windows as well as \r will be trimmed + split := strings.Split(output, "\n") + lines := make([]string, len(split)) + for i, line := range split { + lines[i] = strings.TrimSpace(line) + } + return lines +} + +// SplitTrimLinesNoEmpty splits the output into individual lines and trims the spaces from each line. +// +// This removes any empty lines. +func SplitTrimLinesNoEmpty(output string) []string { + // this should work for windows as well as \r will be trimmed + split := strings.Split(output, "\n") + lines := make([]string, 0, len(split)) + for _, line := range split { + line = strings.TrimSpace(line) + if line != "" { + lines = append(lines, line) + } + } + return lines +} + +// MapToSortedSlice transforms m to a sorted slice. +func MapToSortedSlice(m map[string]struct{}) []string { + s := MapToSlice(m) + sort.Strings(s) + return s +} + +// MapToSlice transforms m to a slice. +func MapToSlice(m map[string]struct{}) []string { + s := make([]string, 0, len(m)) + for e := range m { + s = append(s, e) + } + return s +} + +// SliceToMap transforms s to a map. +func SliceToMap(s []string) map[string]struct{} { + m := make(map[string]struct{}, len(s)) + for _, e := range s { + m[e] = struct{}{} + } + return m +} + +// SliceToUniqueSortedSlice returns a sorted copy of s with no duplicates. +func SliceToUniqueSortedSlice(s []string) []string { + return MapToSortedSlice(SliceToMap(s)) +} + +// SliceToUniqueSortedSliceFilterEmptyStrings returns a sorted copy of s with no duplicates and no empty strings. +// +// Strings with only spaces are considered empty. +func SliceToUniqueSortedSliceFilterEmptyStrings(s []string) []string { + m := SliceToMap(s) + for key := range m { + if strings.TrimSpace(key) == "" { + delete(m, key) + } + } + return MapToSortedSlice(m) +} + +// SliceToChunks splits s into chunks of the given chunk size. +// +// If s is nil or empty, returns empty. +// If chunkSize is <=0, returns [][]string{s}. +func SliceToChunks(s []string, chunkSize int) [][]string { + var chunks [][]string + if len(s) == 0 { + return chunks + } + if chunkSize <= 0 { + return [][]string{s} + } + c := make([]string, len(s)) + copy(c, s) + // https://github.com/golang/go/wiki/SliceTricks#batching-with-minimal-allocation + for chunkSize < len(c) { + c, chunks = c[chunkSize:], append(chunks, c[0:chunkSize:chunkSize]) + } + return append(chunks, c) +} + +// SliceElementsEqual returns true if the two slices have equal elements. +// +// Nil and empty slices are treated as equals. +func SliceElementsEqual(one []string, two []string) bool { + if len(one) != len(two) { + return false + } + for i, elem := range one { + if two[i] != elem { + return false + } + } + return true +} + +// SliceElementsContained returns true if superset contains subset. +// +// Nil and empty slices are treated as equals. +func SliceElementsContained(superset []string, subset []string) bool { + m := SliceToMap(superset) + for _, elem := range subset { + if _, ok := m[elem]; !ok { + return false + } + } + return true +} + +// JoinSliceQuoted joins the slice with quotes. +func JoinSliceQuoted(s []string, sep string) string { + if len(s) == 0 { + return "" + } + return `"` + strings.Join(s, `"`+sep+`"`) + `"` +} + +// SliceToString prints the slice as [e1,e2]. +func SliceToString(s []string) string { + if len(s) == 0 { + return "" + } + return "[" + strings.Join(s, ",") + "]" +} + +// SliceToHumanString prints the slice as "e1, e2, and e3". +func SliceToHumanString(s []string) string { + switch len(s) { + case 0: + return "" + case 1: + return s[0] + case 2: + return s[0] + " and " + s[1] + default: + return strings.Join(s[:len(s)-1], ", ") + ", and " + s[len(s)-1] + } +} + +// SliceToHumanStringQuoted prints the slice as `"e1", "e2", and "e3"`. +func SliceToHumanStringQuoted(s []string) string { + switch len(s) { + case 0: + return "" + case 1: + return `"` + s[0] + `"` + case 2: + return `"` + s[0] + `" and "` + s[1] + `"` + default: + return `"` + strings.Join(s[:len(s)-1], `", "`) + `", and "` + s[len(s)-1] + `"` + } +} + +// SliceToHumanStringOr prints the slice as "e1, e2, or e3". +func SliceToHumanStringOr(s []string) string { + switch len(s) { + case 0: + return "" + case 1: + return s[0] + case 2: + return s[0] + " or " + s[1] + default: + return strings.Join(s[:len(s)-1], ", ") + ", or " + s[len(s)-1] + } +} + +// SliceToHumanStringOrQuoted prints the slice as `"e1", "e2", or "e3"`. +func SliceToHumanStringOrQuoted(s []string) string { + switch len(s) { + case 0: + return "" + case 1: + return `"` + s[0] + `"` + case 2: + return `"` + s[0] + `" or "` + s[1] + `"` + default: + return `"` + strings.Join(s[:len(s)-1], `", "`) + `", or "` + s[len(s)-1] + `"` + } +} + +// SnakeCaseOption is an option for snake_case conversions. +type SnakeCaseOption func(*snakeCaseOptions) + +// SnakeCaseWithNewWordOnDigits is a SnakeCaseOption that signifies +// to split on digits, ie foo_bar_1 instead of foo_bar1. +func SnakeCaseWithNewWordOnDigits() SnakeCaseOption { + return func(snakeCaseOptions *snakeCaseOptions) { + snakeCaseOptions.newWordOnDigits = true + } +} + +// ToLowerSnakeCase transforms s to lower_snake_case. +func ToLowerSnakeCase(s string, options ...SnakeCaseOption) string { + return strings.ToLower(toSnakeCase(s, options...)) +} + +// ToUpperSnakeCase transforms s to UPPER_SNAKE_CASE. +func ToUpperSnakeCase(s string, options ...SnakeCaseOption) string { + return strings.ToUpper(toSnakeCase(s, options...)) +} + +// ToPascalCase converts s to PascalCase. +// +// Splits on '-', '_', ' ', '\t', '\n', '\r'. +// Uppercase letters will stay uppercase, +func ToPascalCase(s string) string { + output := "" + var previous rune + for i, c := range strings.TrimSpace(s) { + if !isDelimiter(c) { + if i == 0 || isDelimiter(previous) || unicode.IsUpper(c) { + output += string(unicode.ToUpper(c)) + } else { + output += string(unicode.ToLower(c)) + } + } + previous = c + } + return output +} + +// IsAlphanumeric returns true for [0-9a-zA-Z]. +func IsAlphanumeric(r rune) bool { + return IsNumeric(r) || IsAlpha(r) +} + +// IsAlpha returns true for [a-zA-Z]. +func IsAlpha(r rune) bool { + return IsLowerAlpha(r) || IsUpperAlpha(r) +} + +// IsLowerAlpha returns true for [a-z]. +func IsLowerAlpha(r rune) bool { + return 'a' <= r && r <= 'z' +} + +// IsUpperAlpha returns true for [A-Z]. +func IsUpperAlpha(r rune) bool { + return 'A' <= r && r <= 'Z' +} + +// IsNumeric returns true for [0-9]. +func IsNumeric(r rune) bool { + return '0' <= r && r <= '9' +} + +// IsLowerAlphanumeric returns true for [0-9a-z]. +func IsLowerAlphanumeric(r rune) bool { + return IsNumeric(r) || IsLowerAlpha(r) +} + +func toSnakeCase(s string, options ...SnakeCaseOption) string { + snakeCaseOptions := &snakeCaseOptions{} + for _, option := range options { + option(snakeCaseOptions) + } + output := "" + s = strings.TrimFunc(s, isDelimiter) + for i, c := range s { + if isDelimiter(c) { + c = '_' + } + if i == 0 { + output += string(c) + } else if isSnakeCaseNewWord(c, snakeCaseOptions.newWordOnDigits) && + output[len(output)-1] != '_' && + ((i < len(s)-1 && !isSnakeCaseNewWord(rune(s[i+1]), true) && !isDelimiter(rune(s[i+1]))) || + (snakeCaseOptions.newWordOnDigits && unicode.IsDigit(c)) || + (unicode.IsLower(rune(s[i-1])))) { + output += "_" + string(c) + } else if !(isDelimiter(c) && output[len(output)-1] == '_') { + output += string(c) + } + } + return output +} + +func isSnakeCaseNewWord(r rune, newWordOnDigits bool) bool { + if newWordOnDigits { + return unicode.IsUpper(r) || unicode.IsDigit(r) + } + return unicode.IsUpper(r) +} + +func isDelimiter(r rune) bool { + return r == '.' || r == '-' || r == '_' || r == ' ' || r == '\t' || r == '\n' || r == '\r' +} + +type snakeCaseOptions struct { + newWordOnDigits bool +} diff --git a/tm2/pkg/amino/genproto/types.go b/tm2/pkg/amino/genproto/types.go index a01794de01a..4d59092144e 100644 --- a/tm2/pkg/amino/genproto/types.go +++ b/tm2/pkg/amino/genproto/types.go @@ -152,6 +152,7 @@ type P3Field struct { Repeated bool Type P3Type Name string + JSONName string Number uint32 } @@ -225,11 +226,15 @@ func (msg P3Message) PrintCode(p *press.Press) *press.Press { } func (fld P3Field) PrintCode(p *press.Press) *press.Press { + fieldOptions := "" + if fld.JSONName != "" && fld.JSONName != fld.Name { + fieldOptions = " [json_name = \"" + fld.JSONName + "\"]" + } printComments(p, fld.Comment) if fld.Repeated { - p.Pl("repeated %v %v = %v;", fld.Type, fld.Name, fld.Number) + p.Pl("repeated %v %v = %v%v;", fld.Type, fld.Name, fld.Number, fieldOptions) } else { - p.Pl("%v %v = %v;", fld.Type, fld.Name, fld.Number) + p.Pl("%v %v = %v%v;", fld.Type, fld.Name, fld.Number, fieldOptions) } return p } diff --git a/tm2/pkg/amino/genproto/types_test.go b/tm2/pkg/amino/genproto/types_test.go index 8b7398ab184..4e57427e809 100644 --- a/tm2/pkg/amino/genproto/types_test.go +++ b/tm2/pkg/amino/genproto/types_test.go @@ -7,6 +7,8 @@ import ( ) func TestPrintP3Types(t *testing.T) { + t.Parallel() + doc := P3Doc{ Comment: "doc comment", Messages: []P3Message{ diff --git a/tm2/pkg/amino/json_test.go b/tm2/pkg/amino/json_test.go index 81118080d89..e3f7d413fcb 100644 --- a/tm2/pkg/amino/json_test.go +++ b/tm2/pkg/amino/json_test.go @@ -29,6 +29,8 @@ func registerTransports(cdc *amino.Codec) { } func TestMarshalJSON(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() registerTransports(cdc) cases := []struct { @@ -130,6 +132,8 @@ func TestMarshalJSON(t *testing.T) { } func TestMarshalJSONTime(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() registerTransports(cdc) @@ -180,6 +184,8 @@ type innerFP struct { // We don't support maps. func TestUnmarshalMap(t *testing.T) { + t.Parallel() + jsonBytes := []byte("dontcare") obj := new(map[string]int) cdc := amino.NewCodec() @@ -198,6 +204,8 @@ func TestUnmarshalMap(t *testing.T) { } func TestUnmarshalFunc(t *testing.T) { + t.Parallel() + jsonBytes := []byte(`"dontcare"`) obj := func() {} cdc := amino.NewCodec() @@ -218,6 +226,8 @@ func TestUnmarshalFunc(t *testing.T) { } func TestUnmarshalJSON(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() registerTransports(cdc) cases := []struct { @@ -310,6 +320,8 @@ func TestUnmarshalJSON(t *testing.T) { } func TestJSONCodecRoundTrip(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() registerTransports(cdc) type allInclusive struct { @@ -480,6 +492,8 @@ func interfacePtr(v interface{}) *interface{} { // Test to ensure that Amino codec's time encoding/decoding roundtrip // produces the same result as the standard library json's. func TestAminoJSONTimeEncodeDecodeRoundTrip(t *testing.T) { + t.Parallel() + loc, err := time.LoadLocation("America/Los_Angeles") require.NoError(t, err) din := time.Date(2008, 9, 15, 14, 13, 12, 11109876, loc).Round(time.Millisecond).UTC() @@ -503,6 +517,8 @@ func TestAminoJSONTimeEncodeDecodeRoundTrip(t *testing.T) { } func TestMarshalJSONIndent(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() registerTransports(cdc) obj := Transport{Vehicle: Car("Tesla")} diff --git a/tm2/pkg/amino/libs/press/press_test.go b/tm2/pkg/amino/libs/press/press_test.go index 3978e5a5f52..e05042f4556 100644 --- a/tm2/pkg/amino/libs/press/press_test.go +++ b/tm2/pkg/amino/libs/press/press_test.go @@ -7,11 +7,15 @@ import ( ) func TestEmpty(t *testing.T) { + t.Parallel() + p := NewPress() assert.Equal(t, p.Print(), "") } func TestBasic(t *testing.T) { + t.Parallel() + p := NewPress() p.P("this ") p.P("is ") @@ -20,6 +24,8 @@ func TestBasic(t *testing.T) { } func TestBasicLn(t *testing.T) { + t.Parallel() + p := NewPress() p.P("this ") p.P("is ") @@ -28,6 +34,8 @@ func TestBasicLn(t *testing.T) { } func TestNewlineStr(t *testing.T) { + t.Parallel() + p := NewPress().SetNewlineStr("\r\n") p.P("this ") p.P("is ") @@ -38,6 +46,8 @@ func TestNewlineStr(t *testing.T) { } func TestIndent(t *testing.T) { + t.Parallel() + p := NewPress() p.P("first line ") p.Pl("{").I(func(p *Press) { @@ -51,6 +61,8 @@ func TestIndent(t *testing.T) { } func TestIndent2(t *testing.T) { + t.Parallel() + p := NewPress() p.P("first line ") p.Pl("{").I(func(p *Press) { @@ -66,6 +78,8 @@ func TestIndent2(t *testing.T) { } func TestIndent3(t *testing.T) { + t.Parallel() + p := NewPress() p.P("first line ") p.Pl("{").I(func(p *Press) { @@ -78,6 +92,8 @@ func TestIndent3(t *testing.T) { } func TestIndentLn(t *testing.T) { + t.Parallel() + p := NewPress() p.P("first line ") p.Pl("{").I(func(p *Press) { @@ -92,6 +108,8 @@ func TestIndentLn(t *testing.T) { } func TestNestedIndent(t *testing.T) { + t.Parallel() + p := NewPress() p.P("first line ") p.Pl("{").I(func(p *Press) { diff --git a/tm2/pkg/amino/pkg/pkg.go b/tm2/pkg/amino/pkg/pkg.go index fad46c4cad7..b6ffab9748e 100644 --- a/tm2/pkg/amino/pkg/pkg.go +++ b/tm2/pkg/amino/pkg/pkg.go @@ -2,6 +2,9 @@ package pkg import ( "fmt" + "go/ast" + "go/parser" + "go/token" "path/filepath" "reflect" "regexp" @@ -11,8 +14,10 @@ import ( type Type struct { Type reflect.Type - Name string // proto3 name (override) - PointerPreferred bool // whether pointer is preferred for decoding interface. + Name string // proto3 name (override) + PointerPreferred bool // whether pointer is preferred for decoding interface. + Comment string // optional doc comment for the type + FieldComments map[string]string // If not nil, the optional doc comment for each field name } func (t *Type) FullName(pkg *Package) string { @@ -196,6 +201,69 @@ func (pkg *Package) WithP3SchemaFile(file string) *Package { return pkg } +// Parse the Go code in filename and scan the AST looking for struct doc comments. +// Find the Type in pkg.Types and set its Comment and FieldComments, which are +// used by genproto.GenerateProto3MessagePartial to set the Comment in the P3Doc +// and related P3Field objects. +func (pkg *Package) WithComments(filename string) *Package { + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments) + if err != nil { + panic(err) + } + + ast.Inspect(f, func(node ast.Node) bool { + genDecl, ok := node.(*ast.GenDecl) + if !ok { + return true + } + for _, spec := range genDecl.Specs { + typeSpec, ok := spec.(*ast.TypeSpec) + if !ok { + continue + } + + pkgType := pkg.getTypeByName(typeSpec.Name.Name) + if pkgType == nil { + continue + } + if genDecl.Doc != nil { + // Set the type comment. + pkgType.Comment = strings.TrimSpace(genDecl.Doc.Text()) + } + + structType, ok := typeSpec.Type.(*ast.StructType) + if !ok { + continue + } + for _, field := range structType.Fields.List { + if field.Names != nil && len(field.Names) == 1 && field.Doc != nil { + // Set the field comment. + if pkgType.FieldComments == nil { + pkgType.FieldComments = make(map[string]string) + } + + pkgType.FieldComments[field.Names[0].Name] = strings.TrimSpace(field.Doc.Text()) + } + } + } + return true + }) + + return pkg +} + +// Get the Type by name. If not found, return nil. +func (pkg *Package) getTypeByName(name string) *Type { + for _, t := range pkg.Types { + if t.Name == name { + return t + } + } + + return nil +} + // Result cannot be modified. func (pkg *Package) GetType(rt reflect.Type) (t Type, ok bool) { if rt.Kind() == reflect.Ptr { @@ -314,30 +382,25 @@ func GetCallersDirname() string { return dirName } +const ( + reDomain = `[[:alnum:]-_]+[[:alnum:]-_.]+\.[a-zA-Z]{2,4}` + reGoPkgPart = `[[:alpha:]-_]+` + reR3PkgPart = `[[:alpha:]_]+` +) + var ( - RE_DOMAIN = `[[:alnum:]-_]+[[:alnum:]-_.]+\.[a-zA-Z]{2,4}` - RE_GOPKG_PART = `[[:alpha:]-_]+` - RE_GOPKG = fmt.Sprintf(`(?:%v|%v)(?:/%v)*`, RE_DOMAIN, RE_GOPKG_PART, RE_GOPKG_PART) - RE_P3PKG_PART = `[[:alpha:]_]+` - RE_P3PKG = fmt.Sprintf(`%v(?:\.:%v)*`, RE_P3PKG_PART, RE_P3PKG_PART) + reGoPkg = regexp.MustCompile(fmt.Sprintf(`(?:%v|%v)(?:/%v)*`, reDomain, reGoPkgPart, reGoPkgPart)) + reR3Pkg = regexp.MustCompile(fmt.Sprintf(`%v(?:\.:%v)*`, reR3PkgPart, reR3PkgPart)) ) func assertValidGoPkgPath(gopkgPath string) { - matched, err := regexp.Match(RE_GOPKG, []byte(gopkgPath)) - if err != nil { - panic(err) - } - if !matched { + if !reGoPkg.Match([]byte(gopkgPath)) { panic(fmt.Sprintf("not a valid go package path: %v", gopkgPath)) } } func assertValidP3PkgName(p3pkgName string) { - matched, err := regexp.Match(RE_P3PKG, []byte(p3pkgName)) - if err != nil { - panic(err) - } - if !matched { + if !reR3Pkg.Match([]byte(p3pkgName)) { panic(fmt.Sprintf("not a valid proto3 package path: %v", p3pkgName)) } } diff --git a/tm2/pkg/amino/pkg/pkg_test.go b/tm2/pkg/amino/pkg/pkg_test.go index 2be0b0cefe3..1be87e88f4e 100644 --- a/tm2/pkg/amino/pkg/pkg_test.go +++ b/tm2/pkg/amino/pkg/pkg_test.go @@ -14,6 +14,8 @@ type Foo struct { } func TestNewPackage(t *testing.T) { + t.Parallel() + // This should panic, as slashes in p3pkg is not allowed. assert.Panics(t, func() { NewPackage("foobar.com/some/path", "some/path", "").WithTypes(Foo{}) @@ -39,6 +41,8 @@ func TestNewPackage(t *testing.T) { } func TestFullNameForType(t *testing.T) { + t.Parallel() + // The Go package depends on how this test is invoked. // Sometimes it is "github.com/gnolang/gno/tm2/pkg/amino/packagepkg_test". // Sometimes it is "command-line-arguments" @@ -55,6 +59,8 @@ func TestFullNameForType(t *testing.T) { // If the struct wasn't registered, you can't get a name or type_url for it. func TestFullNameForUnexpectedType(t *testing.T) { + t.Parallel() + gopkg := reflect.TypeOf(Foo{}).PkgPath() pkg := NewPackage(gopkg, "some.path", "") diff --git a/tm2/pkg/amino/reflect.go b/tm2/pkg/amino/reflect.go index bc4fa57e626..01402a320d2 100644 --- a/tm2/pkg/amino/reflect.go +++ b/tm2/pkg/amino/reflect.go @@ -1,26 +1,21 @@ package amino import ( - "encoding/json" "fmt" "reflect" "unicode" ) -//---------------------------------------- +// ---------------------------------------- // Constants -var ( - jsonMarshalerType = reflect.TypeOf(new(json.Marshaler)).Elem() - jsonUnmarshalerType = reflect.TypeOf(new(json.Unmarshaler)).Elem() - errorType = reflect.TypeOf(new(error)).Elem() -) +var errorType = reflect.TypeOf(new(error)).Elem() -//---------------------------------------- +// ---------------------------------------- // encode: see binary-encode.go and json-encode.go // decode: see binary-decode.go and json-decode.go -//---------------------------------------- +// ---------------------------------------- // Misc. // CONTRACT: by the time this is called, len(bz) >= _n @@ -182,18 +177,6 @@ func constructConcreteType(cinfo *TypeInfo) (crv, irvSet reflect.Value) { return } -// Like constructConcreteType(), but if pointer preferred, returns a nil one. -// We like nil pointers for efficiency. -func constructConcreteTypeNilPreferred(cinfo *TypeInfo) (crv reflect.Value) { - // Construct new concrete type. - if cinfo.PointerPreferred { - crv = reflect.Zero(cinfo.PtrToType) - } else { - crv = reflect.New(cinfo.Type).Elem() - } - return -} - func toReprObject(rv reflect.Value) (rrv reflect.Value, err error) { var mwrm reflect.Value if rv.CanAddr() { @@ -269,12 +252,6 @@ func unmarshalAminoReprType(rm reflect.Method) (rrt reflect.Type) { return } -func toPBMessage(cdc *Codec, rv reflect.Value) (pbrv reflect.Value) { - rm := rv.MethodByName("ToPBMessage") - pbrv = rm.Call([]reflect.Value{reflect.ValueOf(cdc)})[0] - return -} - // NOTE: do not change this definition. // It is also defined for genproto. func isListType(rt reflect.Type) bool { diff --git a/tm2/pkg/amino/reflect_test.go b/tm2/pkg/amino/reflect_test.go index c5c7b6b5c83..9adfa0b35d0 100644 --- a/tm2/pkg/amino/reflect_test.go +++ b/tm2/pkg/amino/reflect_test.go @@ -18,44 +18,70 @@ import ( "github.com/gnolang/gno/tm2/pkg/amino/tests" ) -//------------------------------------- +// ------------------------------------- // Non-interface Google fuzz tests func TestCodecStruct(t *testing.T) { + t.Parallel() + for _, ptr := range tests.StructTypes { t.Logf("case %v", reflect.TypeOf(ptr)) rt := getTypeFromPointer(ptr) name := rt.Name() - t.Run(name+":binary", func(t *testing.T) { _testCodec(t, rt, "binary") }) - t.Run(name+":json", func(t *testing.T) { _testCodec(t, rt, "json") }) + t.Run(name+":binary", func(t *testing.T) { + t.Parallel() + _testCodec(t, rt, "binary") + }) + t.Run(name+":json", func(t *testing.T) { + t.Parallel() + _testCodec(t, rt, "json") + }) } } func TestCodecDef(t *testing.T) { + t.Parallel() + for _, ptr := range tests.DefTypes { t.Logf("case %v", reflect.TypeOf(ptr)) rt := getTypeFromPointer(ptr) name := rt.Name() - t.Run(name+":binary", func(t *testing.T) { _testCodec(t, rt, "binary") }) - t.Run(name+":json", func(t *testing.T) { _testCodec(t, rt, "json") }) + t.Run(name+":binary", func(t *testing.T) { + t.Parallel() + _testCodec(t, rt, "binary") + }) + t.Run(name+":json", func(t *testing.T) { + t.Parallel() + _testCodec(t, rt, "json") + }) } } func TestDeepCopyStruct(t *testing.T) { + t.Parallel() + for _, ptr := range tests.StructTypes { t.Logf("case %v", reflect.TypeOf(ptr)) rt := getTypeFromPointer(ptr) name := rt.Name() - t.Run(name+":deepcopy", func(t *testing.T) { _testDeepCopy(t, rt) }) + t.Run(name+":deepcopy", func(t *testing.T) { + t.Parallel() + _testDeepCopy(t, rt) + }) } } func TestDeepCopyDef(t *testing.T) { + t.Parallel() + for _, ptr := range tests.DefTypes { t.Logf("case %v", reflect.TypeOf(ptr)) rt := getTypeFromPointer(ptr) name := rt.Name() - t.Run(name+":deepcopy", func(t *testing.T) { _testDeepCopy(t, rt) }) + t.Run(name+":deepcopy", func(t *testing.T) { + t.Parallel() + _testDeepCopy(t, rt) + }) } } @@ -190,10 +216,12 @@ func _testDeepCopy(t *testing.T, rt reflect.Type) { } } -//---------------------------------------- +// ---------------------------------------- // Register/interface tests func TestCodecMashalFailsOnUnregisteredConcrete(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() bz, err := cdc.Marshal(struct{ tests.Interface1 }{tests.Concrete1{}}) @@ -202,6 +230,8 @@ func TestCodecMashalFailsOnUnregisteredConcrete(t *testing.T) { } func TestCodecMarshalPassesOnRegistered(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() cdc.RegisterTypeFrom(reflect.TypeOf(tests.Concrete1{}), tests.Package) @@ -218,6 +248,8 @@ func TestCodecMarshalPassesOnRegistered(t *testing.T) { } func TestCodecRegisterAndMarshalMultipleConcrete(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() cdc.RegisterTypeFrom(reflect.TypeOf(tests.Concrete1{}), tests.Package) cdc.RegisterTypeFrom(reflect.TypeOf(tests.Concrete2{}), tests.Package) @@ -251,6 +283,8 @@ func TestCodecRegisterAndMarshalMultipleConcrete(t *testing.T) { // Serialize and deserialize a registered typedef. func TestCodecRoundtripNonNilRegisteredTypeDef(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() cdc.RegisterTypeFrom(reflect.TypeOf(tests.ConcreteTypeDef{}), tests.Package) @@ -322,6 +356,8 @@ func TestCodecRoundtripNonNilRegisteredTypeDef(t *testing.T) { // Exactly like TestCodecRoundtripNonNilRegisteredTypeDef but with struct // around the value instead of a type def. func TestCodecRoundtripNonNilRegisteredWrappedValue(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() cdc.RegisterTypeFrom(reflect.TypeOf(tests.ConcreteWrappedBytes{}), tests.Package) @@ -351,6 +387,8 @@ func TestCodecRoundtripNonNilRegisteredWrappedValue(t *testing.T) { // MarshalAny(msg) and Marshal(&msg) are the same. func TestCodecMarshalAny(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() cdc.RegisterTypeFrom(reflect.TypeOf(tests.ConcreteWrappedBytes{}), tests.Package) @@ -368,6 +406,8 @@ func TestCodecMarshalAny(t *testing.T) { // Like TestCodecRoundtripNonNilRegisteredTypeDef, but JSON. func TestCodecJSONRoundtripNonNilRegisteredTypeDef(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() cdc.RegisterTypeFrom(reflect.TypeOf(tests.ConcreteTypeDef{}), tests.Package) @@ -388,6 +428,8 @@ func TestCodecJSONRoundtripNonNilRegisteredTypeDef(t *testing.T) { // Like TestCodecRoundtripNonNilRegisteredTypeDef, but serialize the concrete value directly. func TestCodecRoundtripMarshalOnConcreteNonNilRegisteredTypeDef(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() cdc.RegisterTypeFrom(reflect.TypeOf(tests.ConcreteTypeDef{}), tests.Package) @@ -418,6 +460,8 @@ func TestCodecRoundtripMarshalOnConcreteNonNilRegisteredTypeDef(t *testing.T) { // Like TestCodecRoundtripNonNilRegisteredTypeDef but read into concrete var. func TestCodecRoundtripUnmarshalOnConcreteNonNilRegisteredTypeDef(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() cdc.RegisterTypeFrom(reflect.TypeOf(tests.ConcreteTypeDef{}), tests.Package) @@ -437,6 +481,8 @@ func TestCodecRoundtripUnmarshalOnConcreteNonNilRegisteredTypeDef(t *testing.T) } func TestCodecBinaryStructFieldNilInterface(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() cdc.RegisterTypeFrom(reflect.TypeOf(tests.InterfaceFieldsStruct{}), tests.Package) @@ -451,7 +497,7 @@ func TestCodecBinaryStructFieldNilInterface(t *testing.T) { require.Equal(t, i2, i1, "i1 and i2 should be the same after decoding") } -//---------------------------------------- +// ---------------------------------------- // Misc. func spw(o interface{}) string { @@ -613,7 +659,7 @@ func getTypeFromPointer(ptr interface{}) reflect.Type { return rt.Elem() } -//---------------------------------------- +// ---------------------------------------- // From https://github.com/google/gofuzz/blob/master/fuzz.go // (Apache2.0 License) diff --git a/tm2/pkg/amino/repr_test.go b/tm2/pkg/amino/repr_test.go index 5fb31515fb6..4be50b5d93d 100644 --- a/tm2/pkg/amino/repr_test.go +++ b/tm2/pkg/amino/repr_test.go @@ -55,6 +55,8 @@ var ( ) func TestMarshalAminoBinary(t *testing.T) { + t.Parallel() + cdc := NewCodec() cdc.RegisterPackage(testPackage) @@ -78,6 +80,8 @@ func TestMarshalAminoBinary(t *testing.T) { } func TestMarshalAminoJSON(t *testing.T) { + t.Parallel() + cdc := NewCodec() cdc.RegisterPackage(testPackage) diff --git a/tm2/pkg/amino/tests/common.go b/tm2/pkg/amino/tests/common.go index b7213a56efc..1abf3aaf601 100644 --- a/tm2/pkg/amino/tests/common.go +++ b/tm2/pkg/amino/tests/common.go @@ -32,8 +32,6 @@ type PrimitivesStruct struct { Time time.Time Duration time.Duration Empty EmptyStruct - - unexposed int8 } type ShortArraysStruct struct { diff --git a/tm2/pkg/amino/tests/pb/tests.pb.go b/tm2/pkg/amino/tests/pb/tests.pb.go index e323d01bada..6776f5ecb57 100644 --- a/tm2/pkg/amino/tests/pb/tests.pb.go +++ b/tm2/pkg/amino/tests/pb/tests.pb.go @@ -1,19 +1,20 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 -// protoc v3.21.12 +// protoc-gen-go v1.31.0 +// protoc v4.24.3 // source: tests.proto package pb import ( + reflect "reflect" + sync "sync" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" durationpb "google.golang.org/protobuf/types/known/durationpb" timestamppb "google.golang.org/protobuf/types/known/timestamppb" - reflect "reflect" - sync "sync" ) const ( @@ -67,26 +68,26 @@ type PrimitivesStruct struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Int8 int32 `protobuf:"zigzag32,1,opt,name=Int8,proto3" json:"Int8,omitempty"` - Int16 int32 `protobuf:"zigzag32,2,opt,name=Int16,proto3" json:"Int16,omitempty"` - Int32 int32 `protobuf:"zigzag32,3,opt,name=Int32,proto3" json:"Int32,omitempty"` - Int32Fixed int32 `protobuf:"fixed32,4,opt,name=Int32Fixed,proto3" json:"Int32Fixed,omitempty"` - Int64 int64 `protobuf:"zigzag64,5,opt,name=Int64,proto3" json:"Int64,omitempty"` - Int64Fixed int64 `protobuf:"fixed64,6,opt,name=Int64Fixed,proto3" json:"Int64Fixed,omitempty"` - Int int64 `protobuf:"zigzag64,7,opt,name=Int,proto3" json:"Int,omitempty"` - Byte uint32 `protobuf:"varint,8,opt,name=Byte,proto3" json:"Byte,omitempty"` - Uint8 uint32 `protobuf:"varint,9,opt,name=Uint8,proto3" json:"Uint8,omitempty"` - Uint16 uint32 `protobuf:"varint,10,opt,name=Uint16,proto3" json:"Uint16,omitempty"` - Uint32 uint32 `protobuf:"varint,11,opt,name=Uint32,proto3" json:"Uint32,omitempty"` - Uint32Fixed uint32 `protobuf:"fixed32,12,opt,name=Uint32Fixed,proto3" json:"Uint32Fixed,omitempty"` - Uint64 uint64 `protobuf:"varint,13,opt,name=Uint64,proto3" json:"Uint64,omitempty"` - Uint64Fixed uint64 `protobuf:"fixed64,14,opt,name=Uint64Fixed,proto3" json:"Uint64Fixed,omitempty"` - Uint uint64 `protobuf:"varint,15,opt,name=Uint,proto3" json:"Uint,omitempty"` - Str string `protobuf:"bytes,16,opt,name=Str,proto3" json:"Str,omitempty"` - Bytes []byte `protobuf:"bytes,17,opt,name=Bytes,proto3" json:"Bytes,omitempty"` - Time *timestamppb.Timestamp `protobuf:"bytes,18,opt,name=Time,proto3" json:"Time,omitempty"` - Duration *durationpb.Duration `protobuf:"bytes,19,opt,name=Duration,proto3" json:"Duration,omitempty"` - Empty *EmptyStruct `protobuf:"bytes,20,opt,name=Empty,proto3" json:"Empty,omitempty"` + Int8 int32 `protobuf:"zigzag32,1,opt,name=int8,json=Int8,proto3" json:"int8,omitempty"` + Int16 int32 `protobuf:"zigzag32,2,opt,name=int16,json=Int16,proto3" json:"int16,omitempty"` + Int32 int32 `protobuf:"zigzag32,3,opt,name=int32,json=Int32,proto3" json:"int32,omitempty"` + Int32Fixed int32 `protobuf:"fixed32,4,opt,name=int32_fixed,json=Int32Fixed,proto3" json:"int32_fixed,omitempty"` + Int64 int64 `protobuf:"zigzag64,5,opt,name=int64,json=Int64,proto3" json:"int64,omitempty"` + Int64Fixed int64 `protobuf:"fixed64,6,opt,name=int64_fixed,json=Int64Fixed,proto3" json:"int64_fixed,omitempty"` + Int int64 `protobuf:"zigzag64,7,opt,name=int,json=Int,proto3" json:"int,omitempty"` + Byte uint32 `protobuf:"varint,8,opt,name=byte,json=Byte,proto3" json:"byte,omitempty"` + Uint8 uint32 `protobuf:"varint,9,opt,name=uint8,json=Uint8,proto3" json:"uint8,omitempty"` + Uint16 uint32 `protobuf:"varint,10,opt,name=uint16,json=Uint16,proto3" json:"uint16,omitempty"` + Uint32 uint32 `protobuf:"varint,11,opt,name=uint32,json=Uint32,proto3" json:"uint32,omitempty"` + Uint32Fixed uint32 `protobuf:"fixed32,12,opt,name=uint32_fixed,json=Uint32Fixed,proto3" json:"uint32_fixed,omitempty"` + Uint64 uint64 `protobuf:"varint,13,opt,name=uint64,json=Uint64,proto3" json:"uint64,omitempty"` + Uint64Fixed uint64 `protobuf:"fixed64,14,opt,name=uint64_fixed,json=Uint64Fixed,proto3" json:"uint64_fixed,omitempty"` + Uint uint64 `protobuf:"varint,15,opt,name=uint,json=Uint,proto3" json:"uint,omitempty"` + Str string `protobuf:"bytes,16,opt,name=str,json=Str,proto3" json:"str,omitempty"` + Bytes []byte `protobuf:"bytes,17,opt,name=bytes,json=Bytes,proto3" json:"bytes,omitempty"` + Time *timestamppb.Timestamp `protobuf:"bytes,18,opt,name=time,json=Time,proto3" json:"time,omitempty"` + Duration *durationpb.Duration `protobuf:"bytes,19,opt,name=duration,json=Duration,proto3" json:"duration,omitempty"` + Empty *EmptyStruct `protobuf:"bytes,20,opt,name=empty,json=Empty,proto3" json:"empty,omitempty"` } func (x *PrimitivesStruct) Reset() { @@ -266,8 +267,8 @@ type ShortArraysStruct struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TimeAr []*timestamppb.Timestamp `protobuf:"bytes,1,rep,name=TimeAr,proto3" json:"TimeAr,omitempty"` - DurationAr []*durationpb.Duration `protobuf:"bytes,2,rep,name=DurationAr,proto3" json:"DurationAr,omitempty"` + TimeAr []*timestamppb.Timestamp `protobuf:"bytes,1,rep,name=time_ar,json=TimeAr,proto3" json:"time_ar,omitempty"` + DurationAr []*durationpb.Duration `protobuf:"bytes,2,rep,name=duration_ar,json=DurationAr,proto3" json:"duration_ar,omitempty"` } func (x *ShortArraysStruct) Reset() { @@ -321,26 +322,26 @@ type ArraysStruct struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Int8Ar []int32 `protobuf:"zigzag32,1,rep,packed,name=Int8Ar,proto3" json:"Int8Ar,omitempty"` - Int16Ar []int32 `protobuf:"zigzag32,2,rep,packed,name=Int16Ar,proto3" json:"Int16Ar,omitempty"` - Int32Ar []int32 `protobuf:"zigzag32,3,rep,packed,name=Int32Ar,proto3" json:"Int32Ar,omitempty"` - Int32FixedAr []int32 `protobuf:"fixed32,4,rep,packed,name=Int32FixedAr,proto3" json:"Int32FixedAr,omitempty"` - Int64Ar []int64 `protobuf:"zigzag64,5,rep,packed,name=Int64Ar,proto3" json:"Int64Ar,omitempty"` - Int64FixedAr []int64 `protobuf:"fixed64,6,rep,packed,name=Int64FixedAr,proto3" json:"Int64FixedAr,omitempty"` - IntAr []int64 `protobuf:"zigzag64,7,rep,packed,name=IntAr,proto3" json:"IntAr,omitempty"` - ByteAr []byte `protobuf:"bytes,8,opt,name=ByteAr,proto3" json:"ByteAr,omitempty"` - Uint8Ar []byte `protobuf:"bytes,9,opt,name=Uint8Ar,proto3" json:"Uint8Ar,omitempty"` - Uint16Ar []uint32 `protobuf:"varint,10,rep,packed,name=Uint16Ar,proto3" json:"Uint16Ar,omitempty"` - Uint32Ar []uint32 `protobuf:"varint,11,rep,packed,name=Uint32Ar,proto3" json:"Uint32Ar,omitempty"` - Uint32FixedAr []uint32 `protobuf:"fixed32,12,rep,packed,name=Uint32FixedAr,proto3" json:"Uint32FixedAr,omitempty"` - Uint64Ar []uint64 `protobuf:"varint,13,rep,packed,name=Uint64Ar,proto3" json:"Uint64Ar,omitempty"` - Uint64FixedAr []uint64 `protobuf:"fixed64,14,rep,packed,name=Uint64FixedAr,proto3" json:"Uint64FixedAr,omitempty"` - UintAr []uint64 `protobuf:"varint,15,rep,packed,name=UintAr,proto3" json:"UintAr,omitempty"` - StrAr []string `protobuf:"bytes,16,rep,name=StrAr,proto3" json:"StrAr,omitempty"` - BytesAr [][]byte `protobuf:"bytes,17,rep,name=BytesAr,proto3" json:"BytesAr,omitempty"` - TimeAr []*timestamppb.Timestamp `protobuf:"bytes,18,rep,name=TimeAr,proto3" json:"TimeAr,omitempty"` - DurationAr []*durationpb.Duration `protobuf:"bytes,19,rep,name=DurationAr,proto3" json:"DurationAr,omitempty"` - EmptyAr []*EmptyStruct `protobuf:"bytes,20,rep,name=EmptyAr,proto3" json:"EmptyAr,omitempty"` + Int8Ar []int32 `protobuf:"zigzag32,1,rep,packed,name=int8_ar,json=Int8Ar,proto3" json:"int8_ar,omitempty"` + Int16Ar []int32 `protobuf:"zigzag32,2,rep,packed,name=int16_ar,json=Int16Ar,proto3" json:"int16_ar,omitempty"` + Int32Ar []int32 `protobuf:"zigzag32,3,rep,packed,name=int32_ar,json=Int32Ar,proto3" json:"int32_ar,omitempty"` + Int32FixedAr []int32 `protobuf:"fixed32,4,rep,packed,name=int32_fixed_ar,json=Int32FixedAr,proto3" json:"int32_fixed_ar,omitempty"` + Int64Ar []int64 `protobuf:"zigzag64,5,rep,packed,name=int64_ar,json=Int64Ar,proto3" json:"int64_ar,omitempty"` + Int64FixedAr []int64 `protobuf:"fixed64,6,rep,packed,name=int64_fixed_ar,json=Int64FixedAr,proto3" json:"int64_fixed_ar,omitempty"` + IntAr []int64 `protobuf:"zigzag64,7,rep,packed,name=int_ar,json=IntAr,proto3" json:"int_ar,omitempty"` + ByteAr []byte `protobuf:"bytes,8,opt,name=byte_ar,json=ByteAr,proto3" json:"byte_ar,omitempty"` + Uint8Ar []byte `protobuf:"bytes,9,opt,name=uint8_ar,json=Uint8Ar,proto3" json:"uint8_ar,omitempty"` + Uint16Ar []uint32 `protobuf:"varint,10,rep,packed,name=uint16_ar,json=Uint16Ar,proto3" json:"uint16_ar,omitempty"` + Uint32Ar []uint32 `protobuf:"varint,11,rep,packed,name=uint32_ar,json=Uint32Ar,proto3" json:"uint32_ar,omitempty"` + Uint32FixedAr []uint32 `protobuf:"fixed32,12,rep,packed,name=uint32_fixed_ar,json=Uint32FixedAr,proto3" json:"uint32_fixed_ar,omitempty"` + Uint64Ar []uint64 `protobuf:"varint,13,rep,packed,name=uint64_ar,json=Uint64Ar,proto3" json:"uint64_ar,omitempty"` + Uint64FixedAr []uint64 `protobuf:"fixed64,14,rep,packed,name=uint64_fixed_ar,json=Uint64FixedAr,proto3" json:"uint64_fixed_ar,omitempty"` + UintAr []uint64 `protobuf:"varint,15,rep,packed,name=uint_ar,json=UintAr,proto3" json:"uint_ar,omitempty"` + StrAr []string `protobuf:"bytes,16,rep,name=str_ar,json=StrAr,proto3" json:"str_ar,omitempty"` + BytesAr [][]byte `protobuf:"bytes,17,rep,name=bytes_ar,json=BytesAr,proto3" json:"bytes_ar,omitempty"` + TimeAr []*timestamppb.Timestamp `protobuf:"bytes,18,rep,name=time_ar,json=TimeAr,proto3" json:"time_ar,omitempty"` + DurationAr []*durationpb.Duration `protobuf:"bytes,19,rep,name=duration_ar,json=DurationAr,proto3" json:"duration_ar,omitempty"` + EmptyAr []*EmptyStruct `protobuf:"bytes,20,rep,name=empty_ar,json=EmptyAr,proto3" json:"empty_ar,omitempty"` } func (x *ArraysStruct) Reset() { @@ -520,26 +521,26 @@ type ArraysArraysStruct struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Int8ArAr []*TESTS_Int8List `protobuf:"bytes,1,rep,name=Int8ArAr,proto3" json:"Int8ArAr,omitempty"` - Int16ArAr []*TESTS_Int16List `protobuf:"bytes,2,rep,name=Int16ArAr,proto3" json:"Int16ArAr,omitempty"` - Int32ArAr []*TESTS_Int32ValueList `protobuf:"bytes,3,rep,name=Int32ArAr,proto3" json:"Int32ArAr,omitempty"` - Int32FixedArAr []*TESTS_Fixed32Int32ValueList `protobuf:"bytes,4,rep,name=Int32FixedArAr,proto3" json:"Int32FixedArAr,omitempty"` - Int64ArAr []*TESTS_Int64ValueList `protobuf:"bytes,5,rep,name=Int64ArAr,proto3" json:"Int64ArAr,omitempty"` - Int64FixedArAr []*TESTS_Fixed64Int64ValueList `protobuf:"bytes,6,rep,name=Int64FixedArAr,proto3" json:"Int64FixedArAr,omitempty"` - IntArAr []*TESTS_Int64ValueList `protobuf:"bytes,7,rep,name=IntArAr,proto3" json:"IntArAr,omitempty"` - ByteArAr [][]byte `protobuf:"bytes,8,rep,name=ByteArAr,proto3" json:"ByteArAr,omitempty"` - Uint8ArAr [][]byte `protobuf:"bytes,9,rep,name=Uint8ArAr,proto3" json:"Uint8ArAr,omitempty"` - Uint16ArAr []*TESTS_UInt16List `protobuf:"bytes,10,rep,name=Uint16ArAr,proto3" json:"Uint16ArAr,omitempty"` - Uint32ArAr []*TESTS_UInt32ValueList `protobuf:"bytes,11,rep,name=Uint32ArAr,proto3" json:"Uint32ArAr,omitempty"` - Uint32FixedArAr []*TESTS_Fixed32UInt32ValueList `protobuf:"bytes,12,rep,name=Uint32FixedArAr,proto3" json:"Uint32FixedArAr,omitempty"` - Uint64ArAr []*TESTS_UInt64ValueList `protobuf:"bytes,13,rep,name=Uint64ArAr,proto3" json:"Uint64ArAr,omitempty"` - Uint64FixedArAr []*TESTS_Fixed64UInt64ValueList `protobuf:"bytes,14,rep,name=Uint64FixedArAr,proto3" json:"Uint64FixedArAr,omitempty"` - UintArAr []*TESTS_UInt64ValueList `protobuf:"bytes,15,rep,name=UintArAr,proto3" json:"UintArAr,omitempty"` - StrArAr []*TESTS_StringValueList `protobuf:"bytes,16,rep,name=StrArAr,proto3" json:"StrArAr,omitempty"` - BytesArAr []*TESTS_BytesList `protobuf:"bytes,17,rep,name=BytesArAr,proto3" json:"BytesArAr,omitempty"` - TimeArAr []*TESTS_TimestampList `protobuf:"bytes,18,rep,name=TimeArAr,proto3" json:"TimeArAr,omitempty"` - DurationArAr []*TESTS_DurationList `protobuf:"bytes,19,rep,name=DurationArAr,proto3" json:"DurationArAr,omitempty"` - EmptyArAr []*TESTS_EmptyStructList `protobuf:"bytes,20,rep,name=EmptyArAr,proto3" json:"EmptyArAr,omitempty"` + Int8ArAr []*TESTS_Int8List `protobuf:"bytes,1,rep,name=int8_ar_ar,json=Int8ArAr,proto3" json:"int8_ar_ar,omitempty"` + Int16ArAr []*TESTS_Int16List `protobuf:"bytes,2,rep,name=int16_ar_ar,json=Int16ArAr,proto3" json:"int16_ar_ar,omitempty"` + Int32ArAr []*TESTS_Int32ValueList `protobuf:"bytes,3,rep,name=int32_ar_ar,json=Int32ArAr,proto3" json:"int32_ar_ar,omitempty"` + Int32FixedArAr []*TESTS_Fixed32Int32ValueList `protobuf:"bytes,4,rep,name=int32_fixed_ar_ar,json=Int32FixedArAr,proto3" json:"int32_fixed_ar_ar,omitempty"` + Int64ArAr []*TESTS_Int64ValueList `protobuf:"bytes,5,rep,name=int64_ar_ar,json=Int64ArAr,proto3" json:"int64_ar_ar,omitempty"` + Int64FixedArAr []*TESTS_Fixed64Int64ValueList `protobuf:"bytes,6,rep,name=int64_fixed_ar_ar,json=Int64FixedArAr,proto3" json:"int64_fixed_ar_ar,omitempty"` + IntArAr []*TESTS_Int64ValueList `protobuf:"bytes,7,rep,name=int_ar_ar,json=IntArAr,proto3" json:"int_ar_ar,omitempty"` + ByteArAr [][]byte `protobuf:"bytes,8,rep,name=byte_ar_ar,json=ByteArAr,proto3" json:"byte_ar_ar,omitempty"` + Uint8ArAr [][]byte `protobuf:"bytes,9,rep,name=uint8_ar_ar,json=Uint8ArAr,proto3" json:"uint8_ar_ar,omitempty"` + Uint16ArAr []*TESTS_UInt16List `protobuf:"bytes,10,rep,name=uint16_ar_ar,json=Uint16ArAr,proto3" json:"uint16_ar_ar,omitempty"` + Uint32ArAr []*TESTS_UInt32ValueList `protobuf:"bytes,11,rep,name=uint32_ar_ar,json=Uint32ArAr,proto3" json:"uint32_ar_ar,omitempty"` + Uint32FixedArAr []*TESTS_Fixed32UInt32ValueList `protobuf:"bytes,12,rep,name=uint32_fixed_ar_ar,json=Uint32FixedArAr,proto3" json:"uint32_fixed_ar_ar,omitempty"` + Uint64ArAr []*TESTS_UInt64ValueList `protobuf:"bytes,13,rep,name=uint64_ar_ar,json=Uint64ArAr,proto3" json:"uint64_ar_ar,omitempty"` + Uint64FixedArAr []*TESTS_Fixed64UInt64ValueList `protobuf:"bytes,14,rep,name=uint64_fixed_ar_ar,json=Uint64FixedArAr,proto3" json:"uint64_fixed_ar_ar,omitempty"` + UintArAr []*TESTS_UInt64ValueList `protobuf:"bytes,15,rep,name=uint_ar_ar,json=UintArAr,proto3" json:"uint_ar_ar,omitempty"` + StrArAr []*TESTS_StringValueList `protobuf:"bytes,16,rep,name=str_ar_ar,json=StrArAr,proto3" json:"str_ar_ar,omitempty"` + BytesArAr []*TESTS_BytesList `protobuf:"bytes,17,rep,name=bytes_ar_ar,json=BytesArAr,proto3" json:"bytes_ar_ar,omitempty"` + TimeArAr []*TESTS_TimestampList `protobuf:"bytes,18,rep,name=time_ar_ar,json=TimeArAr,proto3" json:"time_ar_ar,omitempty"` + DurationArAr []*TESTS_DurationList `protobuf:"bytes,19,rep,name=duration_ar_ar,json=DurationArAr,proto3" json:"duration_ar_ar,omitempty"` + EmptyArAr []*TESTS_EmptyStructList `protobuf:"bytes,20,rep,name=empty_ar_ar,json=EmptyArAr,proto3" json:"empty_ar_ar,omitempty"` } func (x *ArraysArraysStruct) Reset() { @@ -719,26 +720,26 @@ type SlicesStruct struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Int8Sl []int32 `protobuf:"zigzag32,1,rep,packed,name=Int8Sl,proto3" json:"Int8Sl,omitempty"` - Int16Sl []int32 `protobuf:"zigzag32,2,rep,packed,name=Int16Sl,proto3" json:"Int16Sl,omitempty"` - Int32Sl []int32 `protobuf:"zigzag32,3,rep,packed,name=Int32Sl,proto3" json:"Int32Sl,omitempty"` - Int32FixedSl []int32 `protobuf:"fixed32,4,rep,packed,name=Int32FixedSl,proto3" json:"Int32FixedSl,omitempty"` - Int64Sl []int64 `protobuf:"zigzag64,5,rep,packed,name=Int64Sl,proto3" json:"Int64Sl,omitempty"` - Int64FixedSl []int64 `protobuf:"fixed64,6,rep,packed,name=Int64FixedSl,proto3" json:"Int64FixedSl,omitempty"` - IntSl []int64 `protobuf:"zigzag64,7,rep,packed,name=IntSl,proto3" json:"IntSl,omitempty"` - ByteSl []byte `protobuf:"bytes,8,opt,name=ByteSl,proto3" json:"ByteSl,omitempty"` - Uint8Sl []byte `protobuf:"bytes,9,opt,name=Uint8Sl,proto3" json:"Uint8Sl,omitempty"` - Uint16Sl []uint32 `protobuf:"varint,10,rep,packed,name=Uint16Sl,proto3" json:"Uint16Sl,omitempty"` - Uint32Sl []uint32 `protobuf:"varint,11,rep,packed,name=Uint32Sl,proto3" json:"Uint32Sl,omitempty"` - Uint32FixedSl []uint32 `protobuf:"fixed32,12,rep,packed,name=Uint32FixedSl,proto3" json:"Uint32FixedSl,omitempty"` - Uint64Sl []uint64 `protobuf:"varint,13,rep,packed,name=Uint64Sl,proto3" json:"Uint64Sl,omitempty"` - Uint64FixedSl []uint64 `protobuf:"fixed64,14,rep,packed,name=Uint64FixedSl,proto3" json:"Uint64FixedSl,omitempty"` - UintSl []uint64 `protobuf:"varint,15,rep,packed,name=UintSl,proto3" json:"UintSl,omitempty"` - StrSl []string `protobuf:"bytes,16,rep,name=StrSl,proto3" json:"StrSl,omitempty"` - BytesSl [][]byte `protobuf:"bytes,17,rep,name=BytesSl,proto3" json:"BytesSl,omitempty"` - TimeSl []*timestamppb.Timestamp `protobuf:"bytes,18,rep,name=TimeSl,proto3" json:"TimeSl,omitempty"` - DurationSl []*durationpb.Duration `protobuf:"bytes,19,rep,name=DurationSl,proto3" json:"DurationSl,omitempty"` - EmptySl []*EmptyStruct `protobuf:"bytes,20,rep,name=EmptySl,proto3" json:"EmptySl,omitempty"` + Int8Sl []int32 `protobuf:"zigzag32,1,rep,packed,name=int8_sl,json=Int8Sl,proto3" json:"int8_sl,omitempty"` + Int16Sl []int32 `protobuf:"zigzag32,2,rep,packed,name=int16_sl,json=Int16Sl,proto3" json:"int16_sl,omitempty"` + Int32Sl []int32 `protobuf:"zigzag32,3,rep,packed,name=int32_sl,json=Int32Sl,proto3" json:"int32_sl,omitempty"` + Int32FixedSl []int32 `protobuf:"fixed32,4,rep,packed,name=int32_fixed_sl,json=Int32FixedSl,proto3" json:"int32_fixed_sl,omitempty"` + Int64Sl []int64 `protobuf:"zigzag64,5,rep,packed,name=int64_sl,json=Int64Sl,proto3" json:"int64_sl,omitempty"` + Int64FixedSl []int64 `protobuf:"fixed64,6,rep,packed,name=int64_fixed_sl,json=Int64FixedSl,proto3" json:"int64_fixed_sl,omitempty"` + IntSl []int64 `protobuf:"zigzag64,7,rep,packed,name=int_sl,json=IntSl,proto3" json:"int_sl,omitempty"` + ByteSl []byte `protobuf:"bytes,8,opt,name=byte_sl,json=ByteSl,proto3" json:"byte_sl,omitempty"` + Uint8Sl []byte `protobuf:"bytes,9,opt,name=uint8_sl,json=Uint8Sl,proto3" json:"uint8_sl,omitempty"` + Uint16Sl []uint32 `protobuf:"varint,10,rep,packed,name=uint16_sl,json=Uint16Sl,proto3" json:"uint16_sl,omitempty"` + Uint32Sl []uint32 `protobuf:"varint,11,rep,packed,name=uint32_sl,json=Uint32Sl,proto3" json:"uint32_sl,omitempty"` + Uint32FixedSl []uint32 `protobuf:"fixed32,12,rep,packed,name=uint32_fixed_sl,json=Uint32FixedSl,proto3" json:"uint32_fixed_sl,omitempty"` + Uint64Sl []uint64 `protobuf:"varint,13,rep,packed,name=uint64_sl,json=Uint64Sl,proto3" json:"uint64_sl,omitempty"` + Uint64FixedSl []uint64 `protobuf:"fixed64,14,rep,packed,name=uint64_fixed_sl,json=Uint64FixedSl,proto3" json:"uint64_fixed_sl,omitempty"` + UintSl []uint64 `protobuf:"varint,15,rep,packed,name=uint_sl,json=UintSl,proto3" json:"uint_sl,omitempty"` + StrSl []string `protobuf:"bytes,16,rep,name=str_sl,json=StrSl,proto3" json:"str_sl,omitempty"` + BytesSl [][]byte `protobuf:"bytes,17,rep,name=bytes_sl,json=BytesSl,proto3" json:"bytes_sl,omitempty"` + TimeSl []*timestamppb.Timestamp `protobuf:"bytes,18,rep,name=time_sl,json=TimeSl,proto3" json:"time_sl,omitempty"` + DurationSl []*durationpb.Duration `protobuf:"bytes,19,rep,name=duration_sl,json=DurationSl,proto3" json:"duration_sl,omitempty"` + EmptySl []*EmptyStruct `protobuf:"bytes,20,rep,name=empty_sl,json=EmptySl,proto3" json:"empty_sl,omitempty"` } func (x *SlicesStruct) Reset() { @@ -918,26 +919,26 @@ type SlicesSlicesStruct struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Int8SlSl []*TESTS_Int8List `protobuf:"bytes,1,rep,name=Int8SlSl,proto3" json:"Int8SlSl,omitempty"` - Int16SlSl []*TESTS_Int16List `protobuf:"bytes,2,rep,name=Int16SlSl,proto3" json:"Int16SlSl,omitempty"` - Int32SlSl []*TESTS_Int32ValueList `protobuf:"bytes,3,rep,name=Int32SlSl,proto3" json:"Int32SlSl,omitempty"` - Int32FixedSlSl []*TESTS_Fixed32Int32ValueList `protobuf:"bytes,4,rep,name=Int32FixedSlSl,proto3" json:"Int32FixedSlSl,omitempty"` - Int64SlSl []*TESTS_Int64ValueList `protobuf:"bytes,5,rep,name=Int64SlSl,proto3" json:"Int64SlSl,omitempty"` - Int64FixedSlSl []*TESTS_Fixed64Int64ValueList `protobuf:"bytes,6,rep,name=Int64FixedSlSl,proto3" json:"Int64FixedSlSl,omitempty"` - IntSlSl []*TESTS_Int64ValueList `protobuf:"bytes,7,rep,name=IntSlSl,proto3" json:"IntSlSl,omitempty"` - ByteSlSl [][]byte `protobuf:"bytes,8,rep,name=ByteSlSl,proto3" json:"ByteSlSl,omitempty"` - Uint8SlSl [][]byte `protobuf:"bytes,9,rep,name=Uint8SlSl,proto3" json:"Uint8SlSl,omitempty"` - Uint16SlSl []*TESTS_UInt16List `protobuf:"bytes,10,rep,name=Uint16SlSl,proto3" json:"Uint16SlSl,omitempty"` - Uint32SlSl []*TESTS_UInt32ValueList `protobuf:"bytes,11,rep,name=Uint32SlSl,proto3" json:"Uint32SlSl,omitempty"` - Uint32FixedSlSl []*TESTS_Fixed32UInt32ValueList `protobuf:"bytes,12,rep,name=Uint32FixedSlSl,proto3" json:"Uint32FixedSlSl,omitempty"` - Uint64SlSl []*TESTS_UInt64ValueList `protobuf:"bytes,13,rep,name=Uint64SlSl,proto3" json:"Uint64SlSl,omitempty"` - Uint64FixedSlSl []*TESTS_Fixed64UInt64ValueList `protobuf:"bytes,14,rep,name=Uint64FixedSlSl,proto3" json:"Uint64FixedSlSl,omitempty"` - UintSlSl []*TESTS_UInt64ValueList `protobuf:"bytes,15,rep,name=UintSlSl,proto3" json:"UintSlSl,omitempty"` - StrSlSl []*TESTS_StringValueList `protobuf:"bytes,16,rep,name=StrSlSl,proto3" json:"StrSlSl,omitempty"` - BytesSlSl []*TESTS_BytesList `protobuf:"bytes,17,rep,name=BytesSlSl,proto3" json:"BytesSlSl,omitempty"` - TimeSlSl []*TESTS_TimestampList `protobuf:"bytes,18,rep,name=TimeSlSl,proto3" json:"TimeSlSl,omitempty"` - DurationSlSl []*TESTS_DurationList `protobuf:"bytes,19,rep,name=DurationSlSl,proto3" json:"DurationSlSl,omitempty"` - EmptySlSl []*TESTS_EmptyStructList `protobuf:"bytes,20,rep,name=EmptySlSl,proto3" json:"EmptySlSl,omitempty"` + Int8SlSl []*TESTS_Int8List `protobuf:"bytes,1,rep,name=int8_sl_sl,json=Int8SlSl,proto3" json:"int8_sl_sl,omitempty"` + Int16SlSl []*TESTS_Int16List `protobuf:"bytes,2,rep,name=int16_sl_sl,json=Int16SlSl,proto3" json:"int16_sl_sl,omitempty"` + Int32SlSl []*TESTS_Int32ValueList `protobuf:"bytes,3,rep,name=int32_sl_sl,json=Int32SlSl,proto3" json:"int32_sl_sl,omitempty"` + Int32FixedSlSl []*TESTS_Fixed32Int32ValueList `protobuf:"bytes,4,rep,name=int32_fixed_sl_sl,json=Int32FixedSlSl,proto3" json:"int32_fixed_sl_sl,omitempty"` + Int64SlSl []*TESTS_Int64ValueList `protobuf:"bytes,5,rep,name=int64_sl_sl,json=Int64SlSl,proto3" json:"int64_sl_sl,omitempty"` + Int64FixedSlSl []*TESTS_Fixed64Int64ValueList `protobuf:"bytes,6,rep,name=int64_fixed_sl_sl,json=Int64FixedSlSl,proto3" json:"int64_fixed_sl_sl,omitempty"` + IntSlSl []*TESTS_Int64ValueList `protobuf:"bytes,7,rep,name=int_sl_sl,json=IntSlSl,proto3" json:"int_sl_sl,omitempty"` + ByteSlSl [][]byte `protobuf:"bytes,8,rep,name=byte_sl_sl,json=ByteSlSl,proto3" json:"byte_sl_sl,omitempty"` + Uint8SlSl [][]byte `protobuf:"bytes,9,rep,name=uint8_sl_sl,json=Uint8SlSl,proto3" json:"uint8_sl_sl,omitempty"` + Uint16SlSl []*TESTS_UInt16List `protobuf:"bytes,10,rep,name=uint16_sl_sl,json=Uint16SlSl,proto3" json:"uint16_sl_sl,omitempty"` + Uint32SlSl []*TESTS_UInt32ValueList `protobuf:"bytes,11,rep,name=uint32_sl_sl,json=Uint32SlSl,proto3" json:"uint32_sl_sl,omitempty"` + Uint32FixedSlSl []*TESTS_Fixed32UInt32ValueList `protobuf:"bytes,12,rep,name=uint32_fixed_sl_sl,json=Uint32FixedSlSl,proto3" json:"uint32_fixed_sl_sl,omitempty"` + Uint64SlSl []*TESTS_UInt64ValueList `protobuf:"bytes,13,rep,name=uint64_sl_sl,json=Uint64SlSl,proto3" json:"uint64_sl_sl,omitempty"` + Uint64FixedSlSl []*TESTS_Fixed64UInt64ValueList `protobuf:"bytes,14,rep,name=uint64_fixed_sl_sl,json=Uint64FixedSlSl,proto3" json:"uint64_fixed_sl_sl,omitempty"` + UintSlSl []*TESTS_UInt64ValueList `protobuf:"bytes,15,rep,name=uint_sl_sl,json=UintSlSl,proto3" json:"uint_sl_sl,omitempty"` + StrSlSl []*TESTS_StringValueList `protobuf:"bytes,16,rep,name=str_sl_sl,json=StrSlSl,proto3" json:"str_sl_sl,omitempty"` + BytesSlSl []*TESTS_BytesList `protobuf:"bytes,17,rep,name=bytes_sl_sl,json=BytesSlSl,proto3" json:"bytes_sl_sl,omitempty"` + TimeSlSl []*TESTS_TimestampList `protobuf:"bytes,18,rep,name=time_sl_sl,json=TimeSlSl,proto3" json:"time_sl_sl,omitempty"` + DurationSlSl []*TESTS_DurationList `protobuf:"bytes,19,rep,name=duration_sl_sl,json=DurationSlSl,proto3" json:"duration_sl_sl,omitempty"` + EmptySlSl []*TESTS_EmptyStructList `protobuf:"bytes,20,rep,name=empty_sl_sl,json=EmptySlSl,proto3" json:"empty_sl_sl,omitempty"` } func (x *SlicesSlicesStruct) Reset() { @@ -1117,26 +1118,26 @@ type PointersStruct struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Int8Pt int32 `protobuf:"zigzag32,1,opt,name=Int8Pt,proto3" json:"Int8Pt,omitempty"` - Int16Pt int32 `protobuf:"zigzag32,2,opt,name=Int16Pt,proto3" json:"Int16Pt,omitempty"` - Int32Pt int32 `protobuf:"zigzag32,3,opt,name=Int32Pt,proto3" json:"Int32Pt,omitempty"` - Int32FixedPt int32 `protobuf:"fixed32,4,opt,name=Int32FixedPt,proto3" json:"Int32FixedPt,omitempty"` - Int64Pt int64 `protobuf:"zigzag64,5,opt,name=Int64Pt,proto3" json:"Int64Pt,omitempty"` - Int64FixedPt int64 `protobuf:"fixed64,6,opt,name=Int64FixedPt,proto3" json:"Int64FixedPt,omitempty"` - IntPt int64 `protobuf:"zigzag64,7,opt,name=IntPt,proto3" json:"IntPt,omitempty"` - BytePt uint32 `protobuf:"varint,8,opt,name=BytePt,proto3" json:"BytePt,omitempty"` - Uint8Pt uint32 `protobuf:"varint,9,opt,name=Uint8Pt,proto3" json:"Uint8Pt,omitempty"` - Uint16Pt uint32 `protobuf:"varint,10,opt,name=Uint16Pt,proto3" json:"Uint16Pt,omitempty"` - Uint32Pt uint32 `protobuf:"varint,11,opt,name=Uint32Pt,proto3" json:"Uint32Pt,omitempty"` - Uint32FixedPt uint32 `protobuf:"fixed32,12,opt,name=Uint32FixedPt,proto3" json:"Uint32FixedPt,omitempty"` - Uint64Pt uint64 `protobuf:"varint,13,opt,name=Uint64Pt,proto3" json:"Uint64Pt,omitempty"` - Uint64FixedPt uint64 `protobuf:"fixed64,14,opt,name=Uint64FixedPt,proto3" json:"Uint64FixedPt,omitempty"` - UintPt uint64 `protobuf:"varint,15,opt,name=UintPt,proto3" json:"UintPt,omitempty"` - StrPt string `protobuf:"bytes,16,opt,name=StrPt,proto3" json:"StrPt,omitempty"` - BytesPt []byte `protobuf:"bytes,17,opt,name=BytesPt,proto3" json:"BytesPt,omitempty"` - TimePt *timestamppb.Timestamp `protobuf:"bytes,18,opt,name=TimePt,proto3" json:"TimePt,omitempty"` - DurationPt *durationpb.Duration `protobuf:"bytes,19,opt,name=DurationPt,proto3" json:"DurationPt,omitempty"` - EmptyPt *EmptyStruct `protobuf:"bytes,20,opt,name=EmptyPt,proto3" json:"EmptyPt,omitempty"` + Int8Pt int32 `protobuf:"zigzag32,1,opt,name=int8_pt,json=Int8Pt,proto3" json:"int8_pt,omitempty"` + Int16Pt int32 `protobuf:"zigzag32,2,opt,name=int16_pt,json=Int16Pt,proto3" json:"int16_pt,omitempty"` + Int32Pt int32 `protobuf:"zigzag32,3,opt,name=int32_pt,json=Int32Pt,proto3" json:"int32_pt,omitempty"` + Int32FixedPt int32 `protobuf:"fixed32,4,opt,name=int32_fixed_pt,json=Int32FixedPt,proto3" json:"int32_fixed_pt,omitempty"` + Int64Pt int64 `protobuf:"zigzag64,5,opt,name=int64_pt,json=Int64Pt,proto3" json:"int64_pt,omitempty"` + Int64FixedPt int64 `protobuf:"fixed64,6,opt,name=int64_fixed_pt,json=Int64FixedPt,proto3" json:"int64_fixed_pt,omitempty"` + IntPt int64 `protobuf:"zigzag64,7,opt,name=int_pt,json=IntPt,proto3" json:"int_pt,omitempty"` + BytePt uint32 `protobuf:"varint,8,opt,name=byte_pt,json=BytePt,proto3" json:"byte_pt,omitempty"` + Uint8Pt uint32 `protobuf:"varint,9,opt,name=uint8_pt,json=Uint8Pt,proto3" json:"uint8_pt,omitempty"` + Uint16Pt uint32 `protobuf:"varint,10,opt,name=uint16_pt,json=Uint16Pt,proto3" json:"uint16_pt,omitempty"` + Uint32Pt uint32 `protobuf:"varint,11,opt,name=uint32_pt,json=Uint32Pt,proto3" json:"uint32_pt,omitempty"` + Uint32FixedPt uint32 `protobuf:"fixed32,12,opt,name=uint32_fixed_pt,json=Uint32FixedPt,proto3" json:"uint32_fixed_pt,omitempty"` + Uint64Pt uint64 `protobuf:"varint,13,opt,name=uint64_pt,json=Uint64Pt,proto3" json:"uint64_pt,omitempty"` + Uint64FixedPt uint64 `protobuf:"fixed64,14,opt,name=uint64_fixed_pt,json=Uint64FixedPt,proto3" json:"uint64_fixed_pt,omitempty"` + UintPt uint64 `protobuf:"varint,15,opt,name=uint_pt,json=UintPt,proto3" json:"uint_pt,omitempty"` + StrPt string `protobuf:"bytes,16,opt,name=str_pt,json=StrPt,proto3" json:"str_pt,omitempty"` + BytesPt []byte `protobuf:"bytes,17,opt,name=bytes_pt,json=BytesPt,proto3" json:"bytes_pt,omitempty"` + TimePt *timestamppb.Timestamp `protobuf:"bytes,18,opt,name=time_pt,json=TimePt,proto3" json:"time_pt,omitempty"` + DurationPt *durationpb.Duration `protobuf:"bytes,19,opt,name=duration_pt,json=DurationPt,proto3" json:"duration_pt,omitempty"` + EmptyPt *EmptyStruct `protobuf:"bytes,20,opt,name=empty_pt,json=EmptyPt,proto3" json:"empty_pt,omitempty"` } func (x *PointersStruct) Reset() { @@ -1316,26 +1317,26 @@ type PointerSlicesStruct struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Int8PtSl []int32 `protobuf:"zigzag32,1,rep,packed,name=Int8PtSl,proto3" json:"Int8PtSl,omitempty"` - Int16PtSl []int32 `protobuf:"zigzag32,2,rep,packed,name=Int16PtSl,proto3" json:"Int16PtSl,omitempty"` - Int32PtSl []int32 `protobuf:"zigzag32,3,rep,packed,name=Int32PtSl,proto3" json:"Int32PtSl,omitempty"` - Int32FixedPtSl []int32 `protobuf:"fixed32,4,rep,packed,name=Int32FixedPtSl,proto3" json:"Int32FixedPtSl,omitempty"` - Int64PtSl []int64 `protobuf:"zigzag64,5,rep,packed,name=Int64PtSl,proto3" json:"Int64PtSl,omitempty"` - Int64FixedPtSl []int64 `protobuf:"fixed64,6,rep,packed,name=Int64FixedPtSl,proto3" json:"Int64FixedPtSl,omitempty"` - IntPtSl []int64 `protobuf:"zigzag64,7,rep,packed,name=IntPtSl,proto3" json:"IntPtSl,omitempty"` - BytePtSl []byte `protobuf:"bytes,8,opt,name=BytePtSl,proto3" json:"BytePtSl,omitempty"` - Uint8PtSl []byte `protobuf:"bytes,9,opt,name=Uint8PtSl,proto3" json:"Uint8PtSl,omitempty"` - Uint16PtSl []uint32 `protobuf:"varint,10,rep,packed,name=Uint16PtSl,proto3" json:"Uint16PtSl,omitempty"` - Uint32PtSl []uint32 `protobuf:"varint,11,rep,packed,name=Uint32PtSl,proto3" json:"Uint32PtSl,omitempty"` - Uint32FixedPtSl []uint32 `protobuf:"fixed32,12,rep,packed,name=Uint32FixedPtSl,proto3" json:"Uint32FixedPtSl,omitempty"` - Uint64PtSl []uint64 `protobuf:"varint,13,rep,packed,name=Uint64PtSl,proto3" json:"Uint64PtSl,omitempty"` - Uint64FixedPtSl []uint64 `protobuf:"fixed64,14,rep,packed,name=Uint64FixedPtSl,proto3" json:"Uint64FixedPtSl,omitempty"` - UintPtSl []uint64 `protobuf:"varint,15,rep,packed,name=UintPtSl,proto3" json:"UintPtSl,omitempty"` - StrPtSl []string `protobuf:"bytes,16,rep,name=StrPtSl,proto3" json:"StrPtSl,omitempty"` - BytesPtSl [][]byte `protobuf:"bytes,17,rep,name=BytesPtSl,proto3" json:"BytesPtSl,omitempty"` - TimePtSl []*timestamppb.Timestamp `protobuf:"bytes,18,rep,name=TimePtSl,proto3" json:"TimePtSl,omitempty"` - DurationPtSl []*durationpb.Duration `protobuf:"bytes,19,rep,name=DurationPtSl,proto3" json:"DurationPtSl,omitempty"` - EmptyPtSl []*EmptyStruct `protobuf:"bytes,20,rep,name=EmptyPtSl,proto3" json:"EmptyPtSl,omitempty"` + Int8PtSl []int32 `protobuf:"zigzag32,1,rep,packed,name=int8_pt_sl,json=Int8PtSl,proto3" json:"int8_pt_sl,omitempty"` + Int16PtSl []int32 `protobuf:"zigzag32,2,rep,packed,name=int16_pt_sl,json=Int16PtSl,proto3" json:"int16_pt_sl,omitempty"` + Int32PtSl []int32 `protobuf:"zigzag32,3,rep,packed,name=int32_pt_sl,json=Int32PtSl,proto3" json:"int32_pt_sl,omitempty"` + Int32FixedPtSl []int32 `protobuf:"fixed32,4,rep,packed,name=int32_fixed_pt_sl,json=Int32FixedPtSl,proto3" json:"int32_fixed_pt_sl,omitempty"` + Int64PtSl []int64 `protobuf:"zigzag64,5,rep,packed,name=int64_pt_sl,json=Int64PtSl,proto3" json:"int64_pt_sl,omitempty"` + Int64FixedPtSl []int64 `protobuf:"fixed64,6,rep,packed,name=int64_fixed_pt_sl,json=Int64FixedPtSl,proto3" json:"int64_fixed_pt_sl,omitempty"` + IntPtSl []int64 `protobuf:"zigzag64,7,rep,packed,name=int_pt_sl,json=IntPtSl,proto3" json:"int_pt_sl,omitempty"` + BytePtSl []byte `protobuf:"bytes,8,opt,name=byte_pt_sl,json=BytePtSl,proto3" json:"byte_pt_sl,omitempty"` + Uint8PtSl []byte `protobuf:"bytes,9,opt,name=uint8_pt_sl,json=Uint8PtSl,proto3" json:"uint8_pt_sl,omitempty"` + Uint16PtSl []uint32 `protobuf:"varint,10,rep,packed,name=uint16_pt_sl,json=Uint16PtSl,proto3" json:"uint16_pt_sl,omitempty"` + Uint32PtSl []uint32 `protobuf:"varint,11,rep,packed,name=uint32_pt_sl,json=Uint32PtSl,proto3" json:"uint32_pt_sl,omitempty"` + Uint32FixedPtSl []uint32 `protobuf:"fixed32,12,rep,packed,name=uint32_fixed_pt_sl,json=Uint32FixedPtSl,proto3" json:"uint32_fixed_pt_sl,omitempty"` + Uint64PtSl []uint64 `protobuf:"varint,13,rep,packed,name=uint64_pt_sl,json=Uint64PtSl,proto3" json:"uint64_pt_sl,omitempty"` + Uint64FixedPtSl []uint64 `protobuf:"fixed64,14,rep,packed,name=uint64_fixed_pt_sl,json=Uint64FixedPtSl,proto3" json:"uint64_fixed_pt_sl,omitempty"` + UintPtSl []uint64 `protobuf:"varint,15,rep,packed,name=uint_pt_sl,json=UintPtSl,proto3" json:"uint_pt_sl,omitempty"` + StrPtSl []string `protobuf:"bytes,16,rep,name=str_pt_sl,json=StrPtSl,proto3" json:"str_pt_sl,omitempty"` + BytesPtSl [][]byte `protobuf:"bytes,17,rep,name=bytes_pt_sl,json=BytesPtSl,proto3" json:"bytes_pt_sl,omitempty"` + TimePtSl []*timestamppb.Timestamp `protobuf:"bytes,18,rep,name=time_pt_sl,json=TimePtSl,proto3" json:"time_pt_sl,omitempty"` + DurationPtSl []*durationpb.Duration `protobuf:"bytes,19,rep,name=duration_pt_sl,json=DurationPtSl,proto3" json:"duration_pt_sl,omitempty"` + EmptyPtSl []*EmptyStruct `protobuf:"bytes,20,rep,name=empty_pt_sl,json=EmptyPtSl,proto3" json:"empty_pt_sl,omitempty"` } func (x *PointerSlicesStruct) Reset() { @@ -1515,10 +1516,10 @@ type ComplexSt struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - PrField *PrimitivesStruct `protobuf:"bytes,1,opt,name=PrField,proto3" json:"PrField,omitempty"` - ArField *ArraysStruct `protobuf:"bytes,2,opt,name=ArField,proto3" json:"ArField,omitempty"` - SlField *SlicesStruct `protobuf:"bytes,3,opt,name=SlField,proto3" json:"SlField,omitempty"` - PtField *PointersStruct `protobuf:"bytes,4,opt,name=PtField,proto3" json:"PtField,omitempty"` + PrField *PrimitivesStruct `protobuf:"bytes,1,opt,name=pr_field,json=PrField,proto3" json:"pr_field,omitempty"` + ArField *ArraysStruct `protobuf:"bytes,2,opt,name=ar_field,json=ArField,proto3" json:"ar_field,omitempty"` + SlField *SlicesStruct `protobuf:"bytes,3,opt,name=sl_field,json=SlField,proto3" json:"sl_field,omitempty"` + PtField *PointersStruct `protobuf:"bytes,4,opt,name=pt_field,json=PtField,proto3" json:"pt_field,omitempty"` } func (x *ComplexSt) Reset() { @@ -1586,7 +1587,7 @@ type EmbeddedSt1 struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - PrimitivesStruct *PrimitivesStruct `protobuf:"bytes,1,opt,name=PrimitivesStruct,proto3" json:"PrimitivesStruct,omitempty"` + PrimitivesStruct *PrimitivesStruct `protobuf:"bytes,1,opt,name=primitives_struct,json=PrimitivesStruct,proto3" json:"primitives_struct,omitempty"` } func (x *EmbeddedSt1) Reset() { @@ -1633,10 +1634,10 @@ type EmbeddedSt2 struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - PrimitivesStruct *PrimitivesStruct `protobuf:"bytes,1,opt,name=PrimitivesStruct,proto3" json:"PrimitivesStruct,omitempty"` - ArraysStruct *ArraysStruct `protobuf:"bytes,2,opt,name=ArraysStruct,proto3" json:"ArraysStruct,omitempty"` - SlicesStruct *SlicesStruct `protobuf:"bytes,3,opt,name=SlicesStruct,proto3" json:"SlicesStruct,omitempty"` - PointersStruct *PointersStruct `protobuf:"bytes,4,opt,name=PointersStruct,proto3" json:"PointersStruct,omitempty"` + PrimitivesStruct *PrimitivesStruct `protobuf:"bytes,1,opt,name=primitives_struct,json=PrimitivesStruct,proto3" json:"primitives_struct,omitempty"` + ArraysStruct *ArraysStruct `protobuf:"bytes,2,opt,name=arrays_struct,json=ArraysStruct,proto3" json:"arrays_struct,omitempty"` + SlicesStruct *SlicesStruct `protobuf:"bytes,3,opt,name=slices_struct,json=SlicesStruct,proto3" json:"slices_struct,omitempty"` + PointersStruct *PointersStruct `protobuf:"bytes,4,opt,name=pointers_struct,json=PointersStruct,proto3" json:"pointers_struct,omitempty"` } func (x *EmbeddedSt2) Reset() { @@ -1704,11 +1705,11 @@ type EmbeddedSt3 struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - PrimitivesStruct *PrimitivesStruct `protobuf:"bytes,1,opt,name=PrimitivesStruct,proto3" json:"PrimitivesStruct,omitempty"` - ArraysStruct *ArraysStruct `protobuf:"bytes,2,opt,name=ArraysStruct,proto3" json:"ArraysStruct,omitempty"` - SlicesStruct *SlicesStruct `protobuf:"bytes,3,opt,name=SlicesStruct,proto3" json:"SlicesStruct,omitempty"` - PointersStruct *PointersStruct `protobuf:"bytes,4,opt,name=PointersStruct,proto3" json:"PointersStruct,omitempty"` - EmptyStruct *EmptyStruct `protobuf:"bytes,5,opt,name=EmptyStruct,proto3" json:"EmptyStruct,omitempty"` + PrimitivesStruct *PrimitivesStruct `protobuf:"bytes,1,opt,name=primitives_struct,json=PrimitivesStruct,proto3" json:"primitives_struct,omitempty"` + ArraysStruct *ArraysStruct `protobuf:"bytes,2,opt,name=arrays_struct,json=ArraysStruct,proto3" json:"arrays_struct,omitempty"` + SlicesStruct *SlicesStruct `protobuf:"bytes,3,opt,name=slices_struct,json=SlicesStruct,proto3" json:"slices_struct,omitempty"` + PointersStruct *PointersStruct `protobuf:"bytes,4,opt,name=pointers_struct,json=PointersStruct,proto3" json:"pointers_struct,omitempty"` + EmptyStruct *EmptyStruct `protobuf:"bytes,5,opt,name=empty_struct,json=EmptyStruct,proto3" json:"empty_struct,omitempty"` } func (x *EmbeddedSt3) Reset() { @@ -1783,15 +1784,15 @@ type EmbeddedSt4 struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Foo1 int64 `protobuf:"zigzag64,1,opt,name=Foo1,proto3" json:"Foo1,omitempty"` - PrimitivesStruct *PrimitivesStruct `protobuf:"bytes,2,opt,name=PrimitivesStruct,proto3" json:"PrimitivesStruct,omitempty"` - Foo2 string `protobuf:"bytes,3,opt,name=Foo2,proto3" json:"Foo2,omitempty"` - ArraysStructField *ArraysStruct `protobuf:"bytes,4,opt,name=ArraysStructField,proto3" json:"ArraysStructField,omitempty"` - Foo3 []byte `protobuf:"bytes,5,opt,name=Foo3,proto3" json:"Foo3,omitempty"` - SlicesStruct *SlicesStruct `protobuf:"bytes,6,opt,name=SlicesStruct,proto3" json:"SlicesStruct,omitempty"` - Foo4 bool `protobuf:"varint,7,opt,name=Foo4,proto3" json:"Foo4,omitempty"` - PointersStructField *PointersStruct `protobuf:"bytes,8,opt,name=PointersStructField,proto3" json:"PointersStructField,omitempty"` - Foo5 uint64 `protobuf:"varint,9,opt,name=Foo5,proto3" json:"Foo5,omitempty"` + Foo1 int64 `protobuf:"zigzag64,1,opt,name=foo1,json=Foo1,proto3" json:"foo1,omitempty"` + PrimitivesStruct *PrimitivesStruct `protobuf:"bytes,2,opt,name=primitives_struct,json=PrimitivesStruct,proto3" json:"primitives_struct,omitempty"` + Foo2 string `protobuf:"bytes,3,opt,name=foo2,json=Foo2,proto3" json:"foo2,omitempty"` + ArraysStructField *ArraysStruct `protobuf:"bytes,4,opt,name=arrays_struct_field,json=ArraysStructField,proto3" json:"arrays_struct_field,omitempty"` + Foo3 []byte `protobuf:"bytes,5,opt,name=foo3,json=Foo3,proto3" json:"foo3,omitempty"` + SlicesStruct *SlicesStruct `protobuf:"bytes,6,opt,name=slices_struct,json=SlicesStruct,proto3" json:"slices_struct,omitempty"` + Foo4 bool `protobuf:"varint,7,opt,name=foo4,json=Foo4,proto3" json:"foo4,omitempty"` + PointersStructField *PointersStruct `protobuf:"bytes,8,opt,name=pointers_struct_field,json=PointersStructField,proto3" json:"pointers_struct_field,omitempty"` + Foo5 uint64 `protobuf:"varint,9,opt,name=foo5,json=Foo5,proto3" json:"foo5,omitempty"` } func (x *EmbeddedSt4) Reset() { @@ -1894,15 +1895,15 @@ type EmbeddedSt5NameOverride struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Foo1 int64 `protobuf:"zigzag64,1,opt,name=Foo1,proto3" json:"Foo1,omitempty"` - PrimitivesStruct *PrimitivesStruct `protobuf:"bytes,2,opt,name=PrimitivesStruct,proto3" json:"PrimitivesStruct,omitempty"` - Foo2 string `protobuf:"bytes,3,opt,name=Foo2,proto3" json:"Foo2,omitempty"` - ArraysStructField *ArraysStruct `protobuf:"bytes,4,opt,name=ArraysStructField,proto3" json:"ArraysStructField,omitempty"` - Foo3 []byte `protobuf:"bytes,5,opt,name=Foo3,proto3" json:"Foo3,omitempty"` - SlicesStruct *SlicesStruct `protobuf:"bytes,6,opt,name=SlicesStruct,proto3" json:"SlicesStruct,omitempty"` - Foo4 bool `protobuf:"varint,7,opt,name=Foo4,proto3" json:"Foo4,omitempty"` - PointersStructField *PointersStruct `protobuf:"bytes,8,opt,name=PointersStructField,proto3" json:"PointersStructField,omitempty"` - Foo5 uint64 `protobuf:"varint,9,opt,name=Foo5,proto3" json:"Foo5,omitempty"` + Foo1 int64 `protobuf:"zigzag64,1,opt,name=foo1,json=Foo1,proto3" json:"foo1,omitempty"` + PrimitivesStruct *PrimitivesStruct `protobuf:"bytes,2,opt,name=primitives_struct,json=PrimitivesStruct,proto3" json:"primitives_struct,omitempty"` + Foo2 string `protobuf:"bytes,3,opt,name=foo2,json=Foo2,proto3" json:"foo2,omitempty"` + ArraysStructField *ArraysStruct `protobuf:"bytes,4,opt,name=arrays_struct_field,json=ArraysStructField,proto3" json:"arrays_struct_field,omitempty"` + Foo3 []byte `protobuf:"bytes,5,opt,name=foo3,json=Foo3,proto3" json:"foo3,omitempty"` + SlicesStruct *SlicesStruct `protobuf:"bytes,6,opt,name=slices_struct,json=SlicesStruct,proto3" json:"slices_struct,omitempty"` + Foo4 bool `protobuf:"varint,7,opt,name=foo4,json=Foo4,proto3" json:"foo4,omitempty"` + PointersStructField *PointersStruct `protobuf:"bytes,8,opt,name=pointers_struct_field,json=PointersStructField,proto3" json:"pointers_struct_field,omitempty"` + Foo5 uint64 `protobuf:"varint,9,opt,name=foo5,json=Foo5,proto3" json:"foo5,omitempty"` } func (x *EmbeddedSt5NameOverride) Reset() { @@ -2005,8 +2006,8 @@ type AminoMarshalerStruct1 struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - C int64 `protobuf:"zigzag64,1,opt,name=C,proto3" json:"C,omitempty"` - D int64 `protobuf:"zigzag64,2,opt,name=D,proto3" json:"D,omitempty"` + C int64 `protobuf:"zigzag64,1,opt,name=c,json=C,proto3" json:"c,omitempty"` + D int64 `protobuf:"zigzag64,2,opt,name=d,json=D,proto3" json:"d,omitempty"` } func (x *AminoMarshalerStruct1) Reset() { @@ -2060,8 +2061,8 @@ type ReprStruct1 struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - C int64 `protobuf:"zigzag64,1,opt,name=C,proto3" json:"C,omitempty"` - D int64 `protobuf:"zigzag64,2,opt,name=D,proto3" json:"D,omitempty"` + C int64 `protobuf:"zigzag64,1,opt,name=c,json=C,proto3" json:"c,omitempty"` + D int64 `protobuf:"zigzag64,2,opt,name=d,json=D,proto3" json:"d,omitempty"` } func (x *ReprStruct1) Reset() { @@ -2115,7 +2116,7 @@ type AminoMarshalerStruct2 struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Value []*ReprElem2 `protobuf:"bytes,1,rep,name=Value,proto3" json:"Value,omitempty"` + Value []*ReprElem2 `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"` } func (x *AminoMarshalerStruct2) Reset() { @@ -2162,8 +2163,8 @@ type ReprElem2 struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Key string `protobuf:"bytes,1,opt,name=Key,proto3" json:"Key,omitempty"` - Value *anypb.Any `protobuf:"bytes,2,opt,name=Value,proto3" json:"Value,omitempty"` + Key string `protobuf:"bytes,1,opt,name=key,json=Key,proto3" json:"key,omitempty"` + Value *anypb.Any `protobuf:"bytes,2,opt,name=value,json=Value,proto3" json:"value,omitempty"` } func (x *ReprElem2) Reset() { @@ -2217,7 +2218,7 @@ type AminoMarshalerStruct3 struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Value int32 `protobuf:"zigzag32,1,opt,name=Value,proto3" json:"Value,omitempty"` + Value int32 `protobuf:"zigzag32,1,opt,name=value,proto3" json:"value,omitempty"` } func (x *AminoMarshalerStruct3) Reset() { @@ -2264,7 +2265,7 @@ type AminoMarshalerInt4 struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - A int32 `protobuf:"zigzag32,1,opt,name=A,proto3" json:"A,omitempty"` + A int32 `protobuf:"zigzag32,1,opt,name=a,json=A,proto3" json:"a,omitempty"` } func (x *AminoMarshalerInt4) Reset() { @@ -2311,7 +2312,7 @@ type AminoMarshalerInt5 struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Value string `protobuf:"bytes,1,opt,name=Value,proto3" json:"Value,omitempty"` + Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` } func (x *AminoMarshalerInt5) Reset() { @@ -2358,7 +2359,7 @@ type AminoMarshalerStruct6 struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Value []*AminoMarshalerStruct1 `protobuf:"bytes,1,rep,name=Value,proto3" json:"Value,omitempty"` + Value []*AminoMarshalerStruct1 `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"` } func (x *AminoMarshalerStruct6) Reset() { @@ -2405,7 +2406,7 @@ type AminoMarshalerStruct7 struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Value []byte `protobuf:"bytes,1,opt,name=Value,proto3" json:"Value,omitempty"` + Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` } func (x *AminoMarshalerStruct7) Reset() { @@ -2452,7 +2453,7 @@ type ReprElem7 struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Value uint32 `protobuf:"varint,1,opt,name=Value,proto3" json:"Value,omitempty"` + Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` } func (x *ReprElem7) Reset() { @@ -2499,7 +2500,7 @@ type IntDef struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Value int64 `protobuf:"zigzag64,1,opt,name=Value,proto3" json:"Value,omitempty"` + Value int64 `protobuf:"zigzag64,1,opt,name=value,proto3" json:"value,omitempty"` } func (x *IntDef) Reset() { @@ -2546,7 +2547,7 @@ type IntAr struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Value []int64 `protobuf:"zigzag64,1,rep,packed,name=Value,proto3" json:"Value,omitempty"` + Value []int64 `protobuf:"zigzag64,1,rep,packed,name=value,proto3" json:"value,omitempty"` } func (x *IntAr) Reset() { @@ -2593,7 +2594,7 @@ type IntSl struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Value []int64 `protobuf:"zigzag64,1,rep,packed,name=Value,proto3" json:"Value,omitempty"` + Value []int64 `protobuf:"zigzag64,1,rep,packed,name=value,proto3" json:"value,omitempty"` } func (x *IntSl) Reset() { @@ -2640,7 +2641,7 @@ type ByteAr struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Value []byte `protobuf:"bytes,1,opt,name=Value,proto3" json:"Value,omitempty"` + Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` } func (x *ByteAr) Reset() { @@ -2687,7 +2688,7 @@ type ByteSl struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Value []byte `protobuf:"bytes,1,opt,name=Value,proto3" json:"Value,omitempty"` + Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` } func (x *ByteSl) Reset() { @@ -2734,26 +2735,26 @@ type PrimitivesStructDef struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Int8 int32 `protobuf:"zigzag32,1,opt,name=Int8,proto3" json:"Int8,omitempty"` - Int16 int32 `protobuf:"zigzag32,2,opt,name=Int16,proto3" json:"Int16,omitempty"` - Int32 int32 `protobuf:"zigzag32,3,opt,name=Int32,proto3" json:"Int32,omitempty"` - Int32Fixed int32 `protobuf:"fixed32,4,opt,name=Int32Fixed,proto3" json:"Int32Fixed,omitempty"` - Int64 int64 `protobuf:"zigzag64,5,opt,name=Int64,proto3" json:"Int64,omitempty"` - Int64Fixed int64 `protobuf:"fixed64,6,opt,name=Int64Fixed,proto3" json:"Int64Fixed,omitempty"` - Int int64 `protobuf:"zigzag64,7,opt,name=Int,proto3" json:"Int,omitempty"` - Byte uint32 `protobuf:"varint,8,opt,name=Byte,proto3" json:"Byte,omitempty"` - Uint8 uint32 `protobuf:"varint,9,opt,name=Uint8,proto3" json:"Uint8,omitempty"` - Uint16 uint32 `protobuf:"varint,10,opt,name=Uint16,proto3" json:"Uint16,omitempty"` - Uint32 uint32 `protobuf:"varint,11,opt,name=Uint32,proto3" json:"Uint32,omitempty"` - Uint32Fixed uint32 `protobuf:"fixed32,12,opt,name=Uint32Fixed,proto3" json:"Uint32Fixed,omitempty"` - Uint64 uint64 `protobuf:"varint,13,opt,name=Uint64,proto3" json:"Uint64,omitempty"` - Uint64Fixed uint64 `protobuf:"fixed64,14,opt,name=Uint64Fixed,proto3" json:"Uint64Fixed,omitempty"` - Uint uint64 `protobuf:"varint,15,opt,name=Uint,proto3" json:"Uint,omitempty"` - Str string `protobuf:"bytes,16,opt,name=Str,proto3" json:"Str,omitempty"` - Bytes []byte `protobuf:"bytes,17,opt,name=Bytes,proto3" json:"Bytes,omitempty"` - Time *timestamppb.Timestamp `protobuf:"bytes,18,opt,name=Time,proto3" json:"Time,omitempty"` - Duration *durationpb.Duration `protobuf:"bytes,19,opt,name=Duration,proto3" json:"Duration,omitempty"` - Empty *EmptyStruct `protobuf:"bytes,20,opt,name=Empty,proto3" json:"Empty,omitempty"` + Int8 int32 `protobuf:"zigzag32,1,opt,name=int8,json=Int8,proto3" json:"int8,omitempty"` + Int16 int32 `protobuf:"zigzag32,2,opt,name=int16,json=Int16,proto3" json:"int16,omitempty"` + Int32 int32 `protobuf:"zigzag32,3,opt,name=int32,json=Int32,proto3" json:"int32,omitempty"` + Int32Fixed int32 `protobuf:"fixed32,4,opt,name=int32_fixed,json=Int32Fixed,proto3" json:"int32_fixed,omitempty"` + Int64 int64 `protobuf:"zigzag64,5,opt,name=int64,json=Int64,proto3" json:"int64,omitempty"` + Int64Fixed int64 `protobuf:"fixed64,6,opt,name=int64_fixed,json=Int64Fixed,proto3" json:"int64_fixed,omitempty"` + Int int64 `protobuf:"zigzag64,7,opt,name=int,json=Int,proto3" json:"int,omitempty"` + Byte uint32 `protobuf:"varint,8,opt,name=byte,json=Byte,proto3" json:"byte,omitempty"` + Uint8 uint32 `protobuf:"varint,9,opt,name=uint8,json=Uint8,proto3" json:"uint8,omitempty"` + Uint16 uint32 `protobuf:"varint,10,opt,name=uint16,json=Uint16,proto3" json:"uint16,omitempty"` + Uint32 uint32 `protobuf:"varint,11,opt,name=uint32,json=Uint32,proto3" json:"uint32,omitempty"` + Uint32Fixed uint32 `protobuf:"fixed32,12,opt,name=uint32_fixed,json=Uint32Fixed,proto3" json:"uint32_fixed,omitempty"` + Uint64 uint64 `protobuf:"varint,13,opt,name=uint64,json=Uint64,proto3" json:"uint64,omitempty"` + Uint64Fixed uint64 `protobuf:"fixed64,14,opt,name=uint64_fixed,json=Uint64Fixed,proto3" json:"uint64_fixed,omitempty"` + Uint uint64 `protobuf:"varint,15,opt,name=uint,json=Uint,proto3" json:"uint,omitempty"` + Str string `protobuf:"bytes,16,opt,name=str,json=Str,proto3" json:"str,omitempty"` + Bytes []byte `protobuf:"bytes,17,opt,name=bytes,json=Bytes,proto3" json:"bytes,omitempty"` + Time *timestamppb.Timestamp `protobuf:"bytes,18,opt,name=time,json=Time,proto3" json:"time,omitempty"` + Duration *durationpb.Duration `protobuf:"bytes,19,opt,name=duration,json=Duration,proto3" json:"duration,omitempty"` + Empty *EmptyStruct `protobuf:"bytes,20,opt,name=empty,json=Empty,proto3" json:"empty,omitempty"` } func (x *PrimitivesStructDef) Reset() { @@ -2933,7 +2934,7 @@ type PrimitivesStructSl struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Value []*PrimitivesStruct `protobuf:"bytes,1,rep,name=Value,proto3" json:"Value,omitempty"` + Value []*PrimitivesStruct `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"` } func (x *PrimitivesStructSl) Reset() { @@ -2980,7 +2981,7 @@ type PrimitivesStructAr struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Value []*PrimitivesStruct `protobuf:"bytes,1,rep,name=Value,proto3" json:"Value,omitempty"` + Value []*PrimitivesStruct `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"` } func (x *PrimitivesStructAr) Reset() { @@ -3103,7 +3104,7 @@ type ConcreteTypeDef struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Value []byte `protobuf:"bytes,1,opt,name=Value,proto3" json:"Value,omitempty"` + Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` } func (x *ConcreteTypeDef) Reset() { @@ -3150,7 +3151,7 @@ type ConcreteWrappedBytes struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Value []byte `protobuf:"bytes,1,opt,name=Value,proto3" json:"Value,omitempty"` + Value []byte `protobuf:"bytes,1,opt,name=value,json=Value,proto3" json:"value,omitempty"` } func (x *ConcreteWrappedBytes) Reset() { @@ -3197,10 +3198,10 @@ type InterfaceFieldsStruct struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - F1 *anypb.Any `protobuf:"bytes,1,opt,name=F1,proto3" json:"F1,omitempty"` - F2 *anypb.Any `protobuf:"bytes,2,opt,name=F2,proto3" json:"F2,omitempty"` - F3 *anypb.Any `protobuf:"bytes,3,opt,name=F3,proto3" json:"F3,omitempty"` - F4 *anypb.Any `protobuf:"bytes,4,opt,name=F4,proto3" json:"F4,omitempty"` + F1 *anypb.Any `protobuf:"bytes,1,opt,name=f1,json=F1,proto3" json:"f1,omitempty"` + F2 *anypb.Any `protobuf:"bytes,2,opt,name=f2,json=F2,proto3" json:"f2,omitempty"` + F3 *anypb.Any `protobuf:"bytes,3,opt,name=f3,json=F3,proto3" json:"f3,omitempty"` + F4 *anypb.Any `protobuf:"bytes,4,opt,name=f4,json=F4,proto3" json:"f4,omitempty"` } func (x *InterfaceFieldsStruct) Reset() { @@ -4073,575 +4074,590 @@ var file_tests_proto_rawDesc = []byte{ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x0d, 0x0a, 0x0b, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x22, - 0xbd, 0x04, 0x0a, 0x10, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x49, 0x6e, 0x74, 0x38, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x11, 0x52, 0x04, 0x49, 0x6e, 0x74, 0x38, 0x12, 0x14, 0x0a, 0x05, 0x49, 0x6e, 0x74, 0x31, + 0xc1, 0x04, 0x0a, 0x10, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x74, 0x38, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x11, 0x52, 0x04, 0x49, 0x6e, 0x74, 0x38, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x18, 0x02, 0x20, 0x01, 0x28, 0x11, 0x52, 0x05, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x12, 0x14, - 0x0a, 0x05, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x03, 0x20, 0x01, 0x28, 0x11, 0x52, 0x05, 0x49, - 0x6e, 0x74, 0x33, 0x32, 0x12, 0x1e, 0x0a, 0x0a, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, - 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0f, 0x52, 0x0a, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, - 0x69, 0x78, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x12, 0x52, 0x05, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x1e, 0x0a, 0x0a, 0x49, 0x6e, - 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x10, 0x52, 0x0a, - 0x49, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x49, 0x6e, - 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x12, 0x52, 0x03, 0x49, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, - 0x42, 0x79, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x42, 0x79, 0x74, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x05, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x31, 0x36, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x12, 0x16, - 0x0a, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, - 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x12, 0x20, 0x0a, 0x0b, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, - 0x46, 0x69, 0x78, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x07, 0x52, 0x0b, 0x55, 0x69, 0x6e, - 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x69, 0x6e, 0x74, - 0x36, 0x34, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, - 0x12, 0x20, 0x0a, 0x0b, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x18, - 0x0e, 0x20, 0x01, 0x28, 0x06, 0x52, 0x0b, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, - 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x55, 0x69, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x04, 0x55, 0x69, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x53, 0x74, 0x72, 0x18, 0x10, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x53, 0x74, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x42, 0x79, 0x74, 0x65, - 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2e, - 0x0a, 0x04, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x35, - 0x0a, 0x08, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x44, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x14, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, - 0x82, 0x01, 0x0a, 0x11, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x32, 0x0a, 0x06, 0x54, 0x69, 0x6d, 0x65, 0x41, 0x72, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x06, 0x54, 0x69, 0x6d, 0x65, 0x41, 0x72, 0x12, 0x39, 0x0a, 0x0a, 0x44, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x41, 0x72, 0x22, 0x89, 0x05, 0x0a, 0x0c, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x49, 0x6e, 0x74, 0x38, 0x41, 0x72, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x11, 0x52, 0x06, 0x49, 0x6e, 0x74, 0x38, 0x41, 0x72, 0x12, 0x18, 0x0a, - 0x07, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x41, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x11, 0x52, 0x07, - 0x49, 0x6e, 0x74, 0x31, 0x36, 0x41, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x49, 0x6e, 0x74, 0x33, 0x32, - 0x41, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x11, 0x52, 0x07, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x41, - 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x41, - 0x72, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0f, 0x52, 0x0c, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, - 0x78, 0x65, 0x64, 0x41, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x41, 0x72, - 0x18, 0x05, 0x20, 0x03, 0x28, 0x12, 0x52, 0x07, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x41, 0x72, 0x12, - 0x22, 0x0a, 0x0c, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x41, 0x72, 0x18, - 0x06, 0x20, 0x03, 0x28, 0x10, 0x52, 0x0c, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, - 0x64, 0x41, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x49, 0x6e, 0x74, 0x41, 0x72, 0x18, 0x07, 0x20, 0x03, - 0x28, 0x12, 0x52, 0x05, 0x49, 0x6e, 0x74, 0x41, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x42, 0x79, 0x74, - 0x65, 0x41, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x42, 0x79, 0x74, 0x65, 0x41, - 0x72, 0x12, 0x18, 0x0a, 0x07, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x41, 0x72, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x07, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x41, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x55, - 0x69, 0x6e, 0x74, 0x31, 0x36, 0x41, 0x72, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x55, - 0x69, 0x6e, 0x74, 0x31, 0x36, 0x41, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x33, - 0x32, 0x41, 0x72, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x33, - 0x32, 0x41, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, - 0x65, 0x64, 0x41, 0x72, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x07, 0x52, 0x0d, 0x55, 0x69, 0x6e, 0x74, - 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x41, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x55, 0x69, 0x6e, - 0x74, 0x36, 0x34, 0x41, 0x72, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x04, 0x52, 0x08, 0x55, 0x69, 0x6e, - 0x74, 0x36, 0x34, 0x41, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x46, - 0x69, 0x78, 0x65, 0x64, 0x41, 0x72, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x06, 0x52, 0x0d, 0x55, 0x69, - 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x41, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x55, - 0x69, 0x6e, 0x74, 0x41, 0x72, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x04, 0x52, 0x06, 0x55, 0x69, 0x6e, - 0x74, 0x41, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x74, 0x72, 0x41, 0x72, 0x18, 0x10, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x05, 0x53, 0x74, 0x72, 0x41, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x42, 0x79, 0x74, - 0x65, 0x73, 0x41, 0x72, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, 0x42, 0x79, 0x74, 0x65, - 0x73, 0x41, 0x72, 0x12, 0x32, 0x0a, 0x06, 0x54, 0x69, 0x6d, 0x65, 0x41, 0x72, 0x18, 0x12, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x06, 0x54, 0x69, 0x6d, 0x65, 0x41, 0x72, 0x12, 0x39, 0x0a, 0x0a, 0x44, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x41, 0x72, 0x12, 0x2c, 0x0a, 0x07, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x41, 0x72, 0x18, 0x14, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x07, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x41, 0x72, - 0x22, 0xaa, 0x09, 0x0a, 0x12, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, - 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x38, 0x41, - 0x72, 0x41, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, + 0x0a, 0x05, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x03, 0x20, 0x01, 0x28, 0x11, 0x52, 0x05, 0x49, + 0x6e, 0x74, 0x33, 0x32, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x66, 0x69, + 0x78, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0f, 0x52, 0x0a, 0x49, 0x6e, 0x74, 0x33, 0x32, + 0x46, 0x69, 0x78, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x12, 0x52, 0x05, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x1f, 0x0a, 0x0b, 0x69, + 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x10, + 0x52, 0x0a, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, + 0x69, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x12, 0x52, 0x03, 0x49, 0x6e, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x62, 0x79, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x42, 0x79, + 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x05, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x69, 0x6e, 0x74, + 0x31, 0x36, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x31, 0x36, + 0x12, 0x16, 0x0a, 0x06, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x07, 0x52, 0x0b, + 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x75, + 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x55, 0x69, 0x6e, + 0x74, 0x36, 0x34, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x66, 0x69, + 0x78, 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x06, 0x52, 0x0b, 0x55, 0x69, 0x6e, 0x74, 0x36, + 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x69, 0x6e, 0x74, 0x18, 0x0f, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x55, 0x69, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x74, + 0x72, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x53, 0x74, 0x72, 0x12, 0x14, 0x0a, 0x05, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x42, 0x79, 0x74, + 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x54, 0x69, + 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x13, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x08, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x05, 0x65, 0x6d, 0x70, + 0x74, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x05, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x22, 0x84, 0x01, 0x0a, 0x11, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x41, 0x72, 0x72, + 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x33, 0x0a, 0x07, 0x74, 0x69, 0x6d, + 0x65, 0x5f, 0x61, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x54, 0x69, 0x6d, 0x65, 0x41, 0x72, 0x12, 0x3a, + 0x0a, 0x0b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x72, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, + 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x22, 0xa1, 0x05, 0x0a, 0x0c, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x69, + 0x6e, 0x74, 0x38, 0x5f, 0x61, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x11, 0x52, 0x06, 0x49, 0x6e, + 0x74, 0x38, 0x41, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x61, 0x72, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x11, 0x52, 0x07, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x41, 0x72, 0x12, + 0x19, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x61, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x11, 0x52, 0x07, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x41, 0x72, 0x12, 0x24, 0x0a, 0x0e, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x61, 0x72, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0f, 0x52, 0x0c, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x41, 0x72, + 0x12, 0x19, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x61, 0x72, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x12, 0x52, 0x07, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x41, 0x72, 0x12, 0x24, 0x0a, 0x0e, 0x69, + 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x61, 0x72, 0x18, 0x06, 0x20, + 0x03, 0x28, 0x10, 0x52, 0x0c, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x41, + 0x72, 0x12, 0x15, 0x0a, 0x06, 0x69, 0x6e, 0x74, 0x5f, 0x61, 0x72, 0x18, 0x07, 0x20, 0x03, 0x28, + 0x12, 0x52, 0x05, 0x49, 0x6e, 0x74, 0x41, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x62, 0x79, 0x74, 0x65, + 0x5f, 0x61, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x42, 0x79, 0x74, 0x65, 0x41, + 0x72, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x61, 0x72, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x41, 0x72, 0x12, 0x1b, 0x0a, 0x09, + 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x61, 0x72, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0d, 0x52, + 0x08, 0x55, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x41, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x5f, 0x61, 0x72, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x55, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x41, 0x72, 0x12, 0x26, 0x0a, 0x0f, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x61, 0x72, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x07, 0x52, + 0x0d, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x41, 0x72, 0x12, 0x1b, + 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x61, 0x72, 0x18, 0x0d, 0x20, 0x03, 0x28, + 0x04, 0x52, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x41, 0x72, 0x12, 0x26, 0x0a, 0x0f, 0x75, + 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x61, 0x72, 0x18, 0x0e, + 0x20, 0x03, 0x28, 0x06, 0x52, 0x0d, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, + 0x64, 0x41, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x69, 0x6e, 0x74, 0x5f, 0x61, 0x72, 0x18, 0x0f, + 0x20, 0x03, 0x28, 0x04, 0x52, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x41, 0x72, 0x12, 0x15, 0x0a, 0x06, + 0x73, 0x74, 0x72, 0x5f, 0x61, 0x72, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x53, 0x74, + 0x72, 0x41, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x61, 0x72, 0x18, + 0x11, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, 0x42, 0x79, 0x74, 0x65, 0x73, 0x41, 0x72, 0x12, 0x33, + 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x61, 0x72, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x54, 0x69, 0x6d, + 0x65, 0x41, 0x72, 0x12, 0x3a, 0x0a, 0x0b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x61, 0x72, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x12, + 0x2d, 0x0a, 0x08, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f, 0x61, 0x72, 0x18, 0x14, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x07, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x41, 0x72, 0x22, 0xd6, + 0x09, 0x0a, 0x12, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x33, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x61, 0x72, + 0x5f, 0x61, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x49, 0x6e, 0x74, 0x38, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x08, 0x49, 0x6e, 0x74, 0x38, 0x41, 0x72, 0x41, 0x72, 0x12, 0x34, 0x0a, 0x09, 0x49, 0x6e, - 0x74, 0x31, 0x36, 0x41, 0x72, 0x41, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, - 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x49, 0x6e, 0x74, 0x31, - 0x36, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x09, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x41, 0x72, 0x41, 0x72, - 0x12, 0x39, 0x0a, 0x09, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x41, 0x72, 0x41, 0x72, 0x18, 0x03, 0x20, + 0x52, 0x08, 0x49, 0x6e, 0x74, 0x38, 0x41, 0x72, 0x41, 0x72, 0x12, 0x36, 0x0a, 0x0b, 0x69, 0x6e, + 0x74, 0x31, 0x36, 0x5f, 0x61, 0x72, 0x5f, 0x61, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x49, 0x6e, + 0x74, 0x31, 0x36, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x09, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x41, 0x72, + 0x41, 0x72, 0x12, 0x3b, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x61, 0x72, 0x5f, 0x61, + 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, + 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x09, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x41, 0x72, 0x41, 0x72, 0x12, + 0x4d, 0x0a, 0x11, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x61, + 0x72, 0x5f, 0x61, 0x72, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x73, + 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, + 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0e, + 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x41, 0x72, 0x41, 0x72, 0x12, 0x3b, + 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x61, 0x72, 0x5f, 0x61, 0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, - 0x53, 0x5f, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x09, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x41, 0x72, 0x41, 0x72, 0x12, 0x4a, 0x0a, 0x0e, 0x49, - 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x41, 0x72, 0x41, 0x72, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, - 0x53, 0x5f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, - 0x78, 0x65, 0x64, 0x41, 0x72, 0x41, 0x72, 0x12, 0x39, 0x0a, 0x09, 0x49, 0x6e, 0x74, 0x36, 0x34, - 0x41, 0x72, 0x41, 0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x73, - 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x09, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x41, 0x72, - 0x41, 0x72, 0x12, 0x4a, 0x0a, 0x0e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, - 0x41, 0x72, 0x41, 0x72, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x73, - 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, - 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0e, - 0x49, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x41, 0x72, 0x41, 0x72, 0x12, 0x35, - 0x0a, 0x07, 0x49, 0x6e, 0x74, 0x41, 0x72, 0x41, 0x72, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x49, 0x6e, - 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x07, 0x49, 0x6e, - 0x74, 0x41, 0x72, 0x41, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x42, 0x79, 0x74, 0x65, 0x41, 0x72, 0x41, + 0x53, 0x5f, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x09, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x41, 0x72, 0x41, 0x72, 0x12, 0x4d, 0x0a, 0x11, 0x69, + 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x61, 0x72, 0x5f, 0x61, 0x72, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, + 0x45, 0x53, 0x54, 0x53, 0x5f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x49, 0x6e, 0x74, 0x36, + 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0e, 0x49, 0x6e, 0x74, 0x36, + 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x41, 0x72, 0x41, 0x72, 0x12, 0x37, 0x0a, 0x09, 0x69, 0x6e, + 0x74, 0x5f, 0x61, 0x72, 0x5f, 0x61, 0x72, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x49, 0x6e, 0x74, 0x36, + 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x07, 0x49, 0x6e, 0x74, 0x41, + 0x72, 0x41, 0x72, 0x12, 0x1c, 0x0a, 0x0a, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x61, 0x72, 0x5f, 0x61, 0x72, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x42, 0x79, 0x74, 0x65, 0x41, 0x72, 0x41, - 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x41, 0x72, 0x41, 0x72, 0x18, 0x09, - 0x20, 0x03, 0x28, 0x0c, 0x52, 0x09, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x41, 0x72, 0x41, 0x72, 0x12, - 0x37, 0x0a, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x41, 0x72, 0x41, 0x72, 0x18, 0x0a, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, - 0x53, 0x5f, 0x55, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0a, 0x55, 0x69, - 0x6e, 0x74, 0x31, 0x36, 0x41, 0x72, 0x41, 0x72, 0x12, 0x3c, 0x0a, 0x0a, 0x55, 0x69, 0x6e, 0x74, - 0x33, 0x32, 0x41, 0x72, 0x41, 0x72, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x55, 0x49, 0x6e, 0x74, 0x33, - 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0a, 0x55, 0x69, 0x6e, 0x74, - 0x33, 0x32, 0x41, 0x72, 0x41, 0x72, 0x12, 0x4d, 0x0a, 0x0f, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, - 0x46, 0x69, 0x78, 0x65, 0x64, 0x41, 0x72, 0x41, 0x72, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x23, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x46, 0x69, - 0x78, 0x65, 0x64, 0x33, 0x32, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0f, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, - 0x64, 0x41, 0x72, 0x41, 0x72, 0x12, 0x3c, 0x0a, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x41, - 0x72, 0x41, 0x72, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x73, 0x74, - 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x41, - 0x72, 0x41, 0x72, 0x12, 0x4d, 0x0a, 0x0f, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, - 0x65, 0x64, 0x41, 0x72, 0x41, 0x72, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x46, 0x69, 0x78, 0x65, 0x64, - 0x36, 0x34, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x0f, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x41, 0x72, - 0x41, 0x72, 0x12, 0x38, 0x0a, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x41, 0x72, 0x41, 0x72, 0x18, 0x0f, + 0x72, 0x12, 0x1e, 0x0a, 0x0b, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x61, 0x72, 0x5f, 0x61, 0x72, + 0x18, 0x09, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x09, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x41, 0x72, 0x41, + 0x72, 0x12, 0x39, 0x0a, 0x0c, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x61, 0x72, 0x5f, 0x61, + 0x72, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, + 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x55, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x41, 0x72, 0x41, 0x72, 0x12, 0x3e, 0x0a, 0x0c, + 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x61, 0x72, 0x5f, 0x61, 0x72, 0x18, 0x0b, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, + 0x5f, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x41, 0x72, 0x41, 0x72, 0x12, 0x50, 0x0a, 0x12, + 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x61, 0x72, 0x5f, + 0x61, 0x72, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, + 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x55, 0x49, + 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0f, 0x55, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x41, 0x72, 0x41, 0x72, 0x12, 0x3e, + 0x0a, 0x0c, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x61, 0x72, 0x5f, 0x61, 0x72, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, - 0x73, 0x74, 0x52, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x41, 0x72, 0x41, 0x72, 0x12, 0x36, 0x0a, 0x07, - 0x53, 0x74, 0x72, 0x41, 0x72, 0x41, 0x72, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x53, 0x74, 0x72, 0x69, - 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x07, 0x53, 0x74, 0x72, - 0x41, 0x72, 0x41, 0x72, 0x12, 0x34, 0x0a, 0x09, 0x42, 0x79, 0x74, 0x65, 0x73, 0x41, 0x72, 0x41, - 0x72, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, - 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x42, 0x79, 0x74, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x09, 0x42, 0x79, 0x74, 0x65, 0x73, 0x41, 0x72, 0x41, 0x72, 0x12, 0x36, 0x0a, 0x08, 0x54, 0x69, - 0x6d, 0x65, 0x41, 0x72, 0x41, 0x72, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x08, 0x54, 0x69, 0x6d, 0x65, 0x41, 0x72, - 0x41, 0x72, 0x12, 0x3d, 0x0a, 0x0c, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, - 0x41, 0x72, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, - 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x0c, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x41, - 0x72, 0x12, 0x3a, 0x0a, 0x09, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x41, 0x72, 0x41, 0x72, 0x18, 0x14, + 0x73, 0x74, 0x52, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x41, 0x72, 0x41, 0x72, 0x12, 0x50, + 0x0a, 0x12, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x61, + 0x72, 0x5f, 0x61, 0x72, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x65, 0x73, + 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, + 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x0f, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x41, 0x72, 0x41, 0x72, + 0x12, 0x3a, 0x0a, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x5f, 0x61, 0x72, 0x5f, 0x61, 0x72, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, - 0x54, 0x53, 0x5f, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4c, 0x69, - 0x73, 0x74, 0x52, 0x09, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x41, 0x72, 0x41, 0x72, 0x22, 0x89, 0x05, - 0x0a, 0x0c, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x16, - 0x0a, 0x06, 0x49, 0x6e, 0x74, 0x38, 0x53, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x11, 0x52, 0x06, - 0x49, 0x6e, 0x74, 0x38, 0x53, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x53, - 0x6c, 0x18, 0x02, 0x20, 0x03, 0x28, 0x11, 0x52, 0x07, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x53, 0x6c, - 0x12, 0x18, 0x0a, 0x07, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x53, 0x6c, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x11, 0x52, 0x07, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x53, 0x6c, 0x12, 0x22, 0x0a, 0x0c, 0x49, 0x6e, - 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0f, - 0x52, 0x0c, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x12, 0x18, - 0x0a, 0x07, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x53, 0x6c, 0x18, 0x05, 0x20, 0x03, 0x28, 0x12, 0x52, - 0x07, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x53, 0x6c, 0x12, 0x22, 0x0a, 0x0c, 0x49, 0x6e, 0x74, 0x36, - 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x18, 0x06, 0x20, 0x03, 0x28, 0x10, 0x52, 0x0c, - 0x49, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x12, 0x14, 0x0a, 0x05, - 0x49, 0x6e, 0x74, 0x53, 0x6c, 0x18, 0x07, 0x20, 0x03, 0x28, 0x12, 0x52, 0x05, 0x49, 0x6e, 0x74, - 0x53, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x42, 0x79, 0x74, 0x65, 0x53, 0x6c, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x06, 0x42, 0x79, 0x74, 0x65, 0x53, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x55, 0x69, - 0x6e, 0x74, 0x38, 0x53, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x55, 0x69, 0x6e, - 0x74, 0x38, 0x53, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x53, 0x6c, - 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x53, 0x6c, - 0x12, 0x1a, 0x0a, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x53, 0x6c, 0x18, 0x0b, 0x20, 0x03, - 0x28, 0x0d, 0x52, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x53, 0x6c, 0x12, 0x24, 0x0a, 0x0d, - 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x18, 0x0c, 0x20, - 0x03, 0x28, 0x07, 0x52, 0x0d, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, - 0x53, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x53, 0x6c, 0x18, 0x0d, - 0x20, 0x03, 0x28, 0x04, 0x52, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x53, 0x6c, 0x12, 0x24, - 0x0a, 0x0d, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x18, - 0x0e, 0x20, 0x03, 0x28, 0x06, 0x52, 0x0d, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, - 0x65, 0x64, 0x53, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x53, 0x6c, 0x18, 0x0f, - 0x20, 0x03, 0x28, 0x04, 0x52, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x53, 0x6c, 0x12, 0x14, 0x0a, 0x05, - 0x53, 0x74, 0x72, 0x53, 0x6c, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x53, 0x74, 0x72, - 0x53, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x42, 0x79, 0x74, 0x65, 0x73, 0x53, 0x6c, 0x18, 0x11, 0x20, - 0x03, 0x28, 0x0c, 0x52, 0x07, 0x42, 0x79, 0x74, 0x65, 0x73, 0x53, 0x6c, 0x12, 0x32, 0x0a, 0x06, - 0x54, 0x69, 0x6d, 0x65, 0x53, 0x6c, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x54, 0x53, 0x5f, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x41, 0x72, 0x41, 0x72, 0x12, 0x38, 0x0a, 0x09, + 0x73, 0x74, 0x72, 0x5f, 0x61, 0x72, 0x5f, 0x61, 0x72, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x07, 0x53, + 0x74, 0x72, 0x41, 0x72, 0x41, 0x72, 0x12, 0x36, 0x0a, 0x0b, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, + 0x61, 0x72, 0x5f, 0x61, 0x72, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x65, + 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x42, 0x79, 0x74, 0x65, 0x73, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x09, 0x42, 0x79, 0x74, 0x65, 0x73, 0x41, 0x72, 0x41, 0x72, 0x12, 0x38, + 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x61, 0x72, 0x5f, 0x61, 0x72, 0x18, 0x12, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, + 0x5f, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x08, + 0x54, 0x69, 0x6d, 0x65, 0x41, 0x72, 0x41, 0x72, 0x12, 0x3f, 0x0a, 0x0e, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x72, 0x5f, 0x61, 0x72, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x44, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0c, 0x44, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x41, 0x72, 0x12, 0x3c, 0x0a, 0x0b, 0x65, 0x6d, 0x70, + 0x74, 0x79, 0x5f, 0x61, 0x72, 0x5f, 0x61, 0x72, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x09, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x41, 0x72, 0x41, 0x72, 0x22, 0xa1, 0x05, 0x0a, 0x0c, 0x53, 0x6c, 0x69, 0x63, + 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x6e, 0x74, 0x38, + 0x5f, 0x73, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x11, 0x52, 0x06, 0x49, 0x6e, 0x74, 0x38, 0x53, + 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x73, 0x6c, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x11, 0x52, 0x07, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x53, 0x6c, 0x12, 0x19, 0x0a, 0x08, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x73, 0x6c, 0x18, 0x03, 0x20, 0x03, 0x28, 0x11, 0x52, 0x07, + 0x49, 0x6e, 0x74, 0x33, 0x32, 0x53, 0x6c, 0x12, 0x24, 0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0f, 0x52, + 0x0c, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x12, 0x19, 0x0a, + 0x08, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x73, 0x6c, 0x18, 0x05, 0x20, 0x03, 0x28, 0x12, 0x52, + 0x07, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x53, 0x6c, 0x12, 0x24, 0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x36, + 0x34, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x18, 0x06, 0x20, 0x03, 0x28, 0x10, + 0x52, 0x0c, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x12, 0x15, + 0x0a, 0x06, 0x69, 0x6e, 0x74, 0x5f, 0x73, 0x6c, 0x18, 0x07, 0x20, 0x03, 0x28, 0x12, 0x52, 0x05, + 0x49, 0x6e, 0x74, 0x53, 0x6c, 0x12, 0x17, 0x0a, 0x07, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x73, 0x6c, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x42, 0x79, 0x74, 0x65, 0x53, 0x6c, 0x12, 0x19, + 0x0a, 0x08, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x73, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x07, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x53, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x69, 0x6e, + 0x74, 0x31, 0x36, 0x5f, 0x73, 0x6c, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x55, 0x69, + 0x6e, 0x74, 0x31, 0x36, 0x53, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x5f, 0x73, 0x6c, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x33, + 0x32, 0x53, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x66, 0x69, + 0x78, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x07, 0x52, 0x0d, 0x55, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x75, + 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x73, 0x6c, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x04, 0x52, 0x08, + 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x53, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x75, 0x69, 0x6e, 0x74, + 0x36, 0x34, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x18, 0x0e, 0x20, 0x03, 0x28, + 0x06, 0x52, 0x0d, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, + 0x12, 0x17, 0x0a, 0x07, 0x75, 0x69, 0x6e, 0x74, 0x5f, 0x73, 0x6c, 0x18, 0x0f, 0x20, 0x03, 0x28, + 0x04, 0x52, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x53, 0x6c, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x74, 0x72, + 0x5f, 0x73, 0x6c, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x53, 0x74, 0x72, 0x53, 0x6c, + 0x12, 0x19, 0x0a, 0x08, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x73, 0x6c, 0x18, 0x11, 0x20, 0x03, + 0x28, 0x0c, 0x52, 0x07, 0x42, 0x79, 0x74, 0x65, 0x73, 0x53, 0x6c, 0x12, 0x33, 0x0a, 0x07, 0x74, + 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x6c, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x6c, - 0x12, 0x39, 0x0a, 0x0a, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6c, 0x18, 0x13, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x0a, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6c, 0x12, 0x2c, 0x0a, 0x07, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x53, 0x6c, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x52, 0x07, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x6c, 0x22, 0xaa, 0x09, 0x0a, 0x12, 0x53, 0x6c, - 0x69, 0x63, 0x65, 0x73, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x12, 0x31, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x38, 0x53, 0x6c, 0x53, 0x6c, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, - 0x5f, 0x49, 0x6e, 0x74, 0x38, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x08, 0x49, 0x6e, 0x74, 0x38, 0x53, - 0x6c, 0x53, 0x6c, 0x12, 0x34, 0x0a, 0x09, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x53, 0x6c, 0x53, 0x6c, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, - 0x45, 0x53, 0x54, 0x53, 0x5f, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x09, - 0x49, 0x6e, 0x74, 0x31, 0x36, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x39, 0x0a, 0x09, 0x49, 0x6e, 0x74, - 0x33, 0x32, 0x53, 0x6c, 0x53, 0x6c, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x49, 0x6e, 0x74, 0x33, 0x32, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x09, 0x49, 0x6e, 0x74, 0x33, 0x32, - 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x4a, 0x0a, 0x0e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, - 0x65, 0x64, 0x53, 0x6c, 0x53, 0x6c, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x46, 0x69, 0x78, 0x65, 0x64, - 0x33, 0x32, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x0e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x53, 0x6c, - 0x12, 0x39, 0x0a, 0x09, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x53, 0x6c, 0x53, 0x6c, 0x18, 0x05, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, - 0x53, 0x5f, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x09, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x4a, 0x0a, 0x0e, 0x49, - 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x53, 0x6c, 0x18, 0x06, 0x20, + 0x12, 0x3a, 0x0a, 0x0b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x6c, 0x18, + 0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x0a, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6c, 0x12, 0x2d, 0x0a, 0x08, + 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f, 0x73, 0x6c, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x52, 0x07, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x6c, 0x22, 0xd6, 0x09, 0x0a, 0x12, + 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x12, 0x33, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x73, 0x6c, 0x5f, 0x73, 0x6c, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, + 0x45, 0x53, 0x54, 0x53, 0x5f, 0x49, 0x6e, 0x74, 0x38, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x08, 0x49, + 0x6e, 0x74, 0x38, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x36, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x31, 0x36, + 0x5f, 0x73, 0x6c, 0x5f, 0x73, 0x6c, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, + 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x49, 0x6e, 0x74, 0x31, 0x36, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x09, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x53, 0x6c, 0x53, 0x6c, 0x12, + 0x3b, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x73, 0x6c, 0x5f, 0x73, 0x6c, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, + 0x54, 0x53, 0x5f, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x09, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x4d, 0x0a, 0x11, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x5f, 0x73, + 0x6c, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, + 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x49, 0x6e, 0x74, + 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0e, 0x49, 0x6e, 0x74, + 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x3b, 0x0a, 0x0b, 0x69, + 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x73, 0x6c, 0x5f, 0x73, 0x6c, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x49, + 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x09, 0x49, + 0x6e, 0x74, 0x36, 0x34, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x4d, 0x0a, 0x11, 0x69, 0x6e, 0x74, 0x36, + 0x34, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x5f, 0x73, 0x6c, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, - 0x78, 0x65, 0x64, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x35, 0x0a, 0x07, 0x49, 0x6e, 0x74, 0x53, 0x6c, - 0x53, 0x6c, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, - 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x07, 0x49, 0x6e, 0x74, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x1a, - 0x0a, 0x08, 0x42, 0x79, 0x74, 0x65, 0x53, 0x6c, 0x53, 0x6c, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0c, - 0x52, 0x08, 0x42, 0x79, 0x74, 0x65, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x55, 0x69, - 0x6e, 0x74, 0x38, 0x53, 0x6c, 0x53, 0x6c, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x09, 0x55, - 0x69, 0x6e, 0x74, 0x38, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x37, 0x0a, 0x0a, 0x55, 0x69, 0x6e, 0x74, - 0x31, 0x36, 0x53, 0x6c, 0x53, 0x6c, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x55, 0x49, 0x6e, 0x74, 0x31, - 0x36, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x53, 0x6c, 0x53, - 0x6c, 0x12, 0x3c, 0x0a, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x53, 0x6c, 0x53, 0x6c, 0x18, - 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, - 0x53, 0x54, 0x53, 0x5f, 0x55, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x53, 0x6c, 0x53, 0x6c, 0x12, - 0x4d, 0x0a, 0x0f, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, - 0x53, 0x6c, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, - 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x55, 0x49, - 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0f, 0x55, - 0x69, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x3c, - 0x0a, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x53, 0x6c, 0x53, 0x6c, 0x18, 0x0d, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, - 0x5f, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x4d, 0x0a, 0x0f, - 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x53, 0x6c, 0x18, - 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, - 0x53, 0x54, 0x53, 0x5f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x55, 0x49, 0x6e, 0x74, 0x36, - 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0f, 0x55, 0x69, 0x6e, 0x74, - 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x38, 0x0a, 0x08, 0x55, - 0x69, 0x6e, 0x74, 0x53, 0x6c, 0x53, 0x6c, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x55, 0x49, 0x6e, 0x74, - 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x08, 0x55, 0x69, 0x6e, - 0x74, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x36, 0x0a, 0x07, 0x53, 0x74, 0x72, 0x53, 0x6c, 0x53, 0x6c, - 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, - 0x45, 0x53, 0x54, 0x53, 0x5f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x07, 0x53, 0x74, 0x72, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x34, 0x0a, - 0x09, 0x42, 0x79, 0x74, 0x65, 0x73, 0x53, 0x6c, 0x53, 0x6c, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x42, - 0x79, 0x74, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x09, 0x42, 0x79, 0x74, 0x65, 0x73, 0x53, - 0x6c, 0x53, 0x6c, 0x12, 0x36, 0x0a, 0x08, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x6c, 0x53, 0x6c, 0x18, - 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, - 0x53, 0x54, 0x53, 0x5f, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x08, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x3d, 0x0a, 0x0c, 0x44, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6c, 0x53, 0x6c, 0x18, 0x13, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, - 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0c, 0x44, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x3a, 0x0a, 0x09, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x53, 0x6c, 0x53, 0x6c, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x09, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x53, 0x6c, 0x53, 0x6c, 0x22, 0x8b, 0x05, 0x0a, 0x0e, 0x50, 0x6f, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x49, 0x6e, 0x74, - 0x38, 0x50, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x11, 0x52, 0x06, 0x49, 0x6e, 0x74, 0x38, 0x50, - 0x74, 0x12, 0x18, 0x0a, 0x07, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x50, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x11, 0x52, 0x07, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x50, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x49, - 0x6e, 0x74, 0x33, 0x32, 0x50, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x11, 0x52, 0x07, 0x49, 0x6e, - 0x74, 0x33, 0x32, 0x50, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, - 0x78, 0x65, 0x64, 0x50, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0f, 0x52, 0x0c, 0x49, 0x6e, 0x74, - 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x49, 0x6e, 0x74, - 0x36, 0x34, 0x50, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x12, 0x52, 0x07, 0x49, 0x6e, 0x74, 0x36, - 0x34, 0x50, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, - 0x64, 0x50, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x10, 0x52, 0x0c, 0x49, 0x6e, 0x74, 0x36, 0x34, - 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x49, 0x6e, 0x74, 0x50, 0x74, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x12, 0x52, 0x05, 0x49, 0x6e, 0x74, 0x50, 0x74, 0x12, 0x16, 0x0a, - 0x06, 0x42, 0x79, 0x74, 0x65, 0x50, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x42, - 0x79, 0x74, 0x65, 0x50, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x50, 0x74, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x50, 0x74, 0x12, - 0x1a, 0x0a, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x50, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x50, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x55, - 0x69, 0x6e, 0x74, 0x33, 0x32, 0x50, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x55, - 0x69, 0x6e, 0x74, 0x33, 0x32, 0x50, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x55, 0x69, 0x6e, 0x74, 0x33, - 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x07, 0x52, 0x0d, - 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x50, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x08, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x50, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x55, 0x69, 0x6e, - 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x06, + 0x78, 0x65, 0x64, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x37, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x5f, 0x73, + 0x6c, 0x5f, 0x73, 0x6c, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x73, + 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x07, 0x49, 0x6e, 0x74, 0x53, 0x6c, 0x53, 0x6c, + 0x12, 0x1c, 0x0a, 0x0a, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x73, 0x6c, 0x5f, 0x73, 0x6c, 0x18, 0x08, + 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x42, 0x79, 0x74, 0x65, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x1e, + 0x0a, 0x0b, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x73, 0x6c, 0x5f, 0x73, 0x6c, 0x18, 0x09, 0x20, + 0x03, 0x28, 0x0c, 0x52, 0x09, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x39, + 0x0a, 0x0c, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x73, 0x6c, 0x5f, 0x73, 0x6c, 0x18, 0x0a, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, + 0x54, 0x53, 0x5f, 0x55, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0a, 0x55, + 0x69, 0x6e, 0x74, 0x31, 0x36, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x3e, 0x0a, 0x0c, 0x75, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x5f, 0x73, 0x6c, 0x5f, 0x73, 0x6c, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x55, 0x49, + 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0a, 0x55, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x50, 0x0a, 0x12, 0x75, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x5f, 0x73, 0x6c, 0x18, + 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, + 0x53, 0x54, 0x53, 0x5f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x33, 0x32, 0x55, 0x49, 0x6e, 0x74, 0x33, + 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0f, 0x55, 0x69, 0x6e, 0x74, + 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x3e, 0x0a, 0x0c, 0x75, + 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x73, 0x6c, 0x5f, 0x73, 0x6c, 0x18, 0x0d, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, + 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x50, 0x0a, 0x12, 0x75, + 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x5f, 0x73, + 0x6c, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, + 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x46, 0x69, 0x78, 0x65, 0x64, 0x36, 0x34, 0x55, 0x49, 0x6e, + 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0f, 0x55, 0x69, + 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x3a, 0x0a, + 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x5f, 0x73, 0x6c, 0x5f, 0x73, 0x6c, 0x18, 0x0f, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, + 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x08, 0x55, 0x69, 0x6e, 0x74, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x38, 0x0a, 0x09, 0x73, 0x74, 0x72, + 0x5f, 0x73, 0x6c, 0x5f, 0x73, 0x6c, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, + 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x53, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x07, 0x53, 0x74, 0x72, 0x53, + 0x6c, 0x53, 0x6c, 0x12, 0x36, 0x0a, 0x0b, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x73, 0x6c, 0x5f, + 0x73, 0x6c, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, + 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x42, 0x79, 0x74, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x09, 0x42, 0x79, 0x74, 0x65, 0x73, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x38, 0x0a, 0x0a, 0x74, + 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x6c, 0x5f, 0x73, 0x6c, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x08, 0x54, 0x69, 0x6d, + 0x65, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x3f, 0x0a, 0x0e, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x73, 0x6c, 0x5f, 0x73, 0x6c, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x44, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x0c, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x53, 0x6c, 0x53, 0x6c, 0x12, 0x3c, 0x0a, 0x0b, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f, + 0x73, 0x6c, 0x5f, 0x73, 0x6c, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, + 0x73, 0x74, 0x73, 0x2e, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x09, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x53, 0x6c, 0x53, 0x6c, 0x22, 0xa3, 0x05, 0x0a, 0x0e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x6e, 0x74, 0x38, 0x5f, + 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x11, 0x52, 0x06, 0x49, 0x6e, 0x74, 0x38, 0x50, 0x74, + 0x12, 0x19, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x11, 0x52, 0x07, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x50, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x70, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x11, 0x52, 0x07, 0x49, + 0x6e, 0x74, 0x33, 0x32, 0x50, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, + 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x70, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0f, 0x52, 0x0c, + 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x12, 0x19, 0x0a, 0x08, + 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x70, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x12, 0x52, 0x07, + 0x49, 0x6e, 0x74, 0x36, 0x34, 0x50, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x69, 0x6e, 0x74, 0x36, 0x34, + 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x70, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x10, 0x52, + 0x0c, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x12, 0x15, 0x0a, + 0x06, 0x69, 0x6e, 0x74, 0x5f, 0x70, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x12, 0x52, 0x05, 0x49, + 0x6e, 0x74, 0x50, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x70, 0x74, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x42, 0x79, 0x74, 0x65, 0x50, 0x74, 0x12, 0x19, 0x0a, + 0x08, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x70, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x50, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, + 0x31, 0x36, 0x5f, 0x70, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x55, 0x69, 0x6e, + 0x74, 0x31, 0x36, 0x50, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, + 0x70, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x50, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x66, 0x69, 0x78, + 0x65, 0x64, 0x5f, 0x70, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x07, 0x52, 0x0d, 0x55, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x69, + 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x70, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x55, + 0x69, 0x6e, 0x74, 0x36, 0x34, 0x50, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x75, 0x69, 0x6e, 0x74, 0x36, + 0x34, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x70, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x06, 0x52, 0x0d, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x12, - 0x16, 0x0a, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x50, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x06, 0x55, 0x69, 0x6e, 0x74, 0x50, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x74, 0x72, 0x50, 0x74, - 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x53, 0x74, 0x72, 0x50, 0x74, 0x12, 0x18, 0x0a, - 0x07, 0x42, 0x79, 0x74, 0x65, 0x73, 0x50, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, - 0x42, 0x79, 0x74, 0x65, 0x73, 0x50, 0x74, 0x12, 0x32, 0x0a, 0x06, 0x54, 0x69, 0x6d, 0x65, 0x50, - 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x06, 0x54, 0x69, 0x6d, 0x65, 0x50, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x44, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x74, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x44, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x74, 0x12, 0x2c, 0x0a, 0x07, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x50, - 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x07, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x50, 0x74, 0x22, 0xe0, 0x05, 0x0a, 0x13, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, - 0x49, 0x6e, 0x74, 0x38, 0x50, 0x74, 0x53, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x11, 0x52, 0x08, - 0x49, 0x6e, 0x74, 0x38, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x49, 0x6e, 0x74, 0x31, - 0x36, 0x50, 0x74, 0x53, 0x6c, 0x18, 0x02, 0x20, 0x03, 0x28, 0x11, 0x52, 0x09, 0x49, 0x6e, 0x74, - 0x31, 0x36, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x50, - 0x74, 0x53, 0x6c, 0x18, 0x03, 0x20, 0x03, 0x28, 0x11, 0x52, 0x09, 0x49, 0x6e, 0x74, 0x33, 0x32, - 0x50, 0x74, 0x53, 0x6c, 0x12, 0x26, 0x0a, 0x0e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, - 0x65, 0x64, 0x50, 0x74, 0x53, 0x6c, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0f, 0x52, 0x0e, 0x49, 0x6e, - 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x1c, 0x0a, 0x09, - 0x49, 0x6e, 0x74, 0x36, 0x34, 0x50, 0x74, 0x53, 0x6c, 0x18, 0x05, 0x20, 0x03, 0x28, 0x12, 0x52, - 0x09, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x26, 0x0a, 0x0e, 0x49, 0x6e, - 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x53, 0x6c, 0x18, 0x06, 0x20, 0x03, - 0x28, 0x10, 0x52, 0x0e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, - 0x53, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x49, 0x6e, 0x74, 0x50, 0x74, 0x53, 0x6c, 0x18, 0x07, 0x20, - 0x03, 0x28, 0x12, 0x52, 0x07, 0x49, 0x6e, 0x74, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x1a, 0x0a, 0x08, - 0x42, 0x79, 0x74, 0x65, 0x50, 0x74, 0x53, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, - 0x42, 0x79, 0x74, 0x65, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x55, 0x69, 0x6e, 0x74, - 0x38, 0x50, 0x74, 0x53, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x55, 0x69, 0x6e, - 0x74, 0x38, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x31, 0x36, - 0x50, 0x74, 0x53, 0x6c, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0a, 0x55, 0x69, 0x6e, 0x74, - 0x31, 0x36, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, - 0x50, 0x74, 0x53, 0x6c, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0a, 0x55, 0x69, 0x6e, 0x74, - 0x33, 0x32, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x28, 0x0a, 0x0f, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, - 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x53, 0x6c, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x07, 0x52, - 0x0f, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x53, 0x6c, - 0x12, 0x1e, 0x0a, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x50, 0x74, 0x53, 0x6c, 0x18, 0x0d, - 0x20, 0x03, 0x28, 0x04, 0x52, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x50, 0x74, 0x53, 0x6c, - 0x12, 0x28, 0x0a, 0x0f, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, - 0x74, 0x53, 0x6c, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x06, 0x52, 0x0f, 0x55, 0x69, 0x6e, 0x74, 0x36, - 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x55, 0x69, - 0x6e, 0x74, 0x50, 0x74, 0x53, 0x6c, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x04, 0x52, 0x08, 0x55, 0x69, - 0x6e, 0x74, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x74, 0x72, 0x50, 0x74, 0x53, - 0x6c, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x53, 0x74, 0x72, 0x50, 0x74, 0x53, 0x6c, - 0x12, 0x1c, 0x0a, 0x09, 0x42, 0x79, 0x74, 0x65, 0x73, 0x50, 0x74, 0x53, 0x6c, 0x18, 0x11, 0x20, - 0x03, 0x28, 0x0c, 0x52, 0x09, 0x42, 0x79, 0x74, 0x65, 0x73, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x36, - 0x0a, 0x08, 0x54, 0x69, 0x6d, 0x65, 0x50, 0x74, 0x53, 0x6c, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x54, 0x69, - 0x6d, 0x65, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x3d, 0x0a, 0x0c, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x50, 0x74, 0x53, 0x6c, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, + 0x17, 0x0a, 0x07, 0x75, 0x69, 0x6e, 0x74, 0x5f, 0x70, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x50, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x5f, + 0x70, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x53, 0x74, 0x72, 0x50, 0x74, 0x12, + 0x19, 0x0a, 0x08, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x70, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x42, 0x79, 0x74, 0x65, 0x73, 0x50, 0x74, 0x12, 0x33, 0x0a, 0x07, 0x74, 0x69, + 0x6d, 0x65, 0x5f, 0x70, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x54, 0x69, 0x6d, 0x65, 0x50, 0x74, 0x12, + 0x3a, 0x0a, 0x0b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x74, 0x18, 0x13, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x0a, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x74, 0x12, 0x2d, 0x0a, 0x08, 0x65, + 0x6d, 0x70, 0x74, 0x79, 0x5f, 0x70, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x52, 0x07, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x50, 0x74, 0x22, 0x8c, 0x06, 0x0a, 0x13, 0x50, + 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x12, 0x1c, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x70, 0x74, 0x5f, 0x73, 0x6c, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x11, 0x52, 0x08, 0x49, 0x6e, 0x74, 0x38, 0x50, 0x74, 0x53, 0x6c, + 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x70, 0x74, 0x5f, 0x73, 0x6c, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x11, 0x52, 0x09, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x50, 0x74, 0x53, 0x6c, + 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x70, 0x74, 0x5f, 0x73, 0x6c, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x11, 0x52, 0x09, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x50, 0x74, 0x53, 0x6c, + 0x12, 0x29, 0x0a, 0x11, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, + 0x70, 0x74, 0x5f, 0x73, 0x6c, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0f, 0x52, 0x0e, 0x49, 0x6e, 0x74, + 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x1e, 0x0a, 0x0b, 0x69, + 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x70, 0x74, 0x5f, 0x73, 0x6c, 0x18, 0x05, 0x20, 0x03, 0x28, 0x12, + 0x52, 0x09, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x29, 0x0a, 0x11, 0x69, + 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x70, 0x74, 0x5f, 0x73, 0x6c, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x10, 0x52, 0x0e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, + 0x65, 0x64, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x1a, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x5f, 0x70, 0x74, + 0x5f, 0x73, 0x6c, 0x18, 0x07, 0x20, 0x03, 0x28, 0x12, 0x52, 0x07, 0x49, 0x6e, 0x74, 0x50, 0x74, + 0x53, 0x6c, 0x12, 0x1c, 0x0a, 0x0a, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x70, 0x74, 0x5f, 0x73, 0x6c, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x42, 0x79, 0x74, 0x65, 0x50, 0x74, 0x53, 0x6c, + 0x12, 0x1e, 0x0a, 0x0b, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x70, 0x74, 0x5f, 0x73, 0x6c, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x50, 0x74, 0x53, 0x6c, + 0x12, 0x20, 0x0a, 0x0c, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x5f, 0x70, 0x74, 0x5f, 0x73, 0x6c, + 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x50, 0x74, + 0x53, 0x6c, 0x12, 0x20, 0x0a, 0x0c, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x70, 0x74, 0x5f, + 0x73, 0x6c, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, + 0x50, 0x74, 0x53, 0x6c, 0x12, 0x2b, 0x0a, 0x12, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x66, + 0x69, 0x78, 0x65, 0x64, 0x5f, 0x70, 0x74, 0x5f, 0x73, 0x6c, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x07, + 0x52, 0x0f, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x53, + 0x6c, 0x12, 0x20, 0x0a, 0x0c, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x70, 0x74, 0x5f, 0x73, + 0x6c, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0a, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x50, + 0x74, 0x53, 0x6c, 0x12, 0x2b, 0x0a, 0x12, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x66, 0x69, + 0x78, 0x65, 0x64, 0x5f, 0x70, 0x74, 0x5f, 0x73, 0x6c, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x06, 0x52, + 0x0f, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x74, 0x53, 0x6c, + 0x12, 0x1c, 0x0a, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x5f, 0x70, 0x74, 0x5f, 0x73, 0x6c, 0x18, 0x0f, + 0x20, 0x03, 0x28, 0x04, 0x52, 0x08, 0x55, 0x69, 0x6e, 0x74, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x1a, + 0x0a, 0x09, 0x73, 0x74, 0x72, 0x5f, 0x70, 0x74, 0x5f, 0x73, 0x6c, 0x18, 0x10, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x07, 0x53, 0x74, 0x72, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x1e, 0x0a, 0x0b, 0x62, 0x79, + 0x74, 0x65, 0x73, 0x5f, 0x70, 0x74, 0x5f, 0x73, 0x6c, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0c, 0x52, + 0x09, 0x42, 0x79, 0x74, 0x65, 0x73, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x38, 0x0a, 0x0a, 0x74, 0x69, + 0x6d, 0x65, 0x5f, 0x70, 0x74, 0x5f, 0x73, 0x6c, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x54, 0x69, 0x6d, 0x65, + 0x50, 0x74, 0x53, 0x6c, 0x12, 0x3f, 0x0a, 0x0e, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x70, 0x74, 0x5f, 0x73, 0x6c, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x30, 0x0a, 0x09, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x50, 0x74, - 0x53, 0x6c, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x09, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x50, 0x74, 0x53, 0x6c, 0x22, 0xcd, 0x01, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x70, - 0x6c, 0x65, 0x78, 0x53, 0x74, 0x12, 0x31, 0x0a, 0x07, 0x50, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, - 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, - 0x07, 0x50, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x2d, 0x0a, 0x07, 0x41, 0x72, 0x46, 0x69, - 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, - 0x73, 0x2e, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x07, - 0x41, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x2d, 0x0a, 0x07, 0x53, 0x6c, 0x46, 0x69, 0x65, - 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, - 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x07, 0x53, - 0x6c, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x2f, 0x0a, 0x07, 0x50, 0x74, 0x46, 0x69, 0x65, 0x6c, - 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x07, - 0x50, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x22, 0x52, 0x0a, 0x0b, 0x45, 0x6d, 0x62, 0x65, 0x64, - 0x64, 0x65, 0x64, 0x53, 0x74, 0x31, 0x12, 0x43, 0x0a, 0x10, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, - 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, - 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x10, 0x50, 0x72, 0x69, 0x6d, 0x69, - 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x22, 0x83, 0x02, 0x0a, 0x0b, - 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x53, 0x74, 0x32, 0x12, 0x43, 0x0a, 0x10, 0x50, - 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, 0x72, - 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x10, + 0x6e, 0x50, 0x74, 0x53, 0x6c, 0x12, 0x32, 0x0a, 0x0b, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f, 0x70, + 0x74, 0x5f, 0x73, 0x6c, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x65, 0x73, + 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x09, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x50, 0x74, 0x53, 0x6c, 0x22, 0xd1, 0x01, 0x0a, 0x09, 0x43, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x53, 0x74, 0x12, 0x32, 0x0a, 0x08, 0x70, 0x72, 0x5f, 0x66, 0x69, + 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x73, 0x74, + 0x73, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x52, 0x07, 0x50, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x2e, 0x0a, 0x08, 0x61, + 0x72, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x52, 0x07, 0x41, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x2e, 0x0a, 0x08, 0x73, + 0x6c, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x52, 0x07, 0x53, 0x6c, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x30, 0x0a, 0x08, 0x70, + 0x74, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x53, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x52, 0x07, 0x50, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x22, 0x53, 0x0a, + 0x0b, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x53, 0x74, 0x31, 0x12, 0x44, 0x0a, 0x11, + 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, + 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x52, 0x10, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x22, 0x87, 0x02, 0x0a, 0x0b, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x53, + 0x74, 0x32, 0x12, 0x44, 0x0a, 0x11, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, + 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, + 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x10, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, + 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x61, 0x72, 0x72, 0x61, + 0x79, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x52, 0x0c, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x5f, 0x73, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, + 0x73, 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0c, + 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x3e, 0x0a, 0x0f, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0e, 0x50, 0x6f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x22, 0xbe, 0x02, 0x0a, + 0x0b, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x53, 0x74, 0x33, 0x12, 0x44, 0x0a, 0x11, + 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x12, 0x37, 0x0a, 0x0c, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x41, - 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0c, 0x41, 0x72, 0x72, - 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x37, 0x0a, 0x0c, 0x53, 0x6c, 0x69, - 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x52, 0x0c, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x12, 0x3d, 0x0a, 0x0e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x53, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, - 0x74, 0x73, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, - 0x74, 0x52, 0x0e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, - 0x74, 0x22, 0xb9, 0x02, 0x0a, 0x0b, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x53, 0x74, - 0x33, 0x12, 0x43, 0x0a, 0x10, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, - 0x73, 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x52, 0x10, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x37, 0x0a, 0x0c, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x73, 0x2e, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, - 0x74, 0x52, 0x0c, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, - 0x37, 0x0a, 0x0c, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x53, 0x6c, - 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0c, 0x53, 0x6c, 0x69, 0x63, - 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x3d, 0x0a, 0x0e, 0x50, 0x6f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x34, 0x0a, 0x0b, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, + 0x52, 0x10, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x12, 0x38, 0x0a, 0x0d, 0x61, 0x72, 0x72, 0x61, 0x79, 0x73, 0x5f, 0x73, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, + 0x73, 0x2e, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0c, + 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x38, 0x0a, 0x0d, + 0x73, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x53, 0x6c, 0x69, 0x63, + 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0c, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, + 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x3e, 0x0a, 0x0f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, + 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, + 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x35, 0x0a, 0x0c, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x52, 0x0b, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x22, 0xfb, 0x02, + 0x52, 0x0b, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x22, 0x81, 0x03, 0x0a, 0x0b, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x53, 0x74, 0x34, 0x12, 0x12, 0x0a, - 0x04, 0x46, 0x6f, 0x6f, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x12, 0x52, 0x04, 0x46, 0x6f, 0x6f, - 0x31, 0x12, 0x43, 0x0a, 0x10, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, - 0x73, 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x52, 0x10, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x6f, 0x6f, 0x32, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x46, 0x6f, 0x6f, 0x32, 0x12, 0x41, 0x0a, 0x11, 0x41, 0x72, - 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x41, 0x72, - 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x11, 0x41, 0x72, 0x72, 0x61, - 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, - 0x04, 0x46, 0x6f, 0x6f, 0x33, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x46, 0x6f, 0x6f, - 0x33, 0x12, 0x37, 0x0a, 0x0c, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, - 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, - 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0c, 0x53, 0x6c, - 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x6f, - 0x6f, 0x34, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x46, 0x6f, 0x6f, 0x34, 0x12, 0x47, - 0x0a, 0x13, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x46, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, - 0x73, 0x74, 0x73, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x52, 0x13, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x6f, 0x6f, 0x35, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x46, 0x6f, 0x6f, 0x35, 0x22, 0x87, 0x03, 0x0a, 0x17, - 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x53, 0x74, 0x35, 0x4e, 0x61, 0x6d, 0x65, 0x4f, - 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x6f, 0x6f, 0x31, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x12, 0x52, 0x04, 0x46, 0x6f, 0x6f, 0x31, 0x12, 0x43, 0x0a, 0x10, 0x50, - 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, 0x72, - 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x10, - 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x46, 0x6f, 0x6f, 0x32, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x46, 0x6f, 0x6f, 0x32, 0x12, 0x41, 0x0a, 0x11, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x52, 0x11, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x6f, 0x6f, 0x33, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x46, 0x6f, 0x6f, 0x33, 0x12, 0x37, 0x0a, 0x0c, 0x53, - 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0c, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x6f, 0x6f, 0x34, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x04, 0x46, 0x6f, 0x6f, 0x34, 0x12, 0x47, 0x0a, 0x13, 0x50, 0x6f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, 0x6f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x13, 0x50, 0x6f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x46, 0x69, 0x65, 0x6c, - 0x64, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x6f, 0x6f, 0x35, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x04, 0x46, 0x6f, 0x6f, 0x35, 0x22, 0x33, 0x0a, 0x15, 0x41, 0x6d, 0x69, 0x6e, 0x6f, 0x4d, 0x61, - 0x72, 0x73, 0x68, 0x61, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x31, 0x12, 0x0c, - 0x0a, 0x01, 0x43, 0x18, 0x01, 0x20, 0x01, 0x28, 0x12, 0x52, 0x01, 0x43, 0x12, 0x0c, 0x0a, 0x01, - 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x12, 0x52, 0x01, 0x44, 0x22, 0x29, 0x0a, 0x0b, 0x52, 0x65, - 0x70, 0x72, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x31, 0x12, 0x0c, 0x0a, 0x01, 0x43, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x12, 0x52, 0x01, 0x43, 0x12, 0x0c, 0x0a, 0x01, 0x44, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x12, 0x52, 0x01, 0x44, 0x22, 0x3f, 0x0a, 0x15, 0x41, 0x6d, 0x69, 0x6e, 0x6f, 0x4d, 0x61, - 0x72, 0x73, 0x68, 0x61, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x32, 0x12, 0x26, - 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x52, 0x65, 0x70, 0x72, 0x45, 0x6c, 0x65, 0x6d, 0x32, 0x52, - 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x49, 0x0a, 0x09, 0x52, 0x65, 0x70, 0x72, 0x45, 0x6c, - 0x65, 0x6d, 0x32, 0x12, 0x10, 0x0a, 0x03, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x22, 0x2d, 0x0a, 0x15, 0x41, 0x6d, 0x69, 0x6e, 0x6f, 0x4d, 0x61, 0x72, 0x73, 0x68, 0x61, - 0x6c, 0x65, 0x72, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x33, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x11, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x22, 0x22, 0x0a, 0x12, 0x41, 0x6d, 0x69, 0x6e, 0x6f, 0x4d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, - 0x65, 0x72, 0x49, 0x6e, 0x74, 0x34, 0x12, 0x0c, 0x0a, 0x01, 0x41, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x11, 0x52, 0x01, 0x41, 0x22, 0x2a, 0x0a, 0x12, 0x41, 0x6d, 0x69, 0x6e, 0x6f, 0x4d, 0x61, 0x72, - 0x73, 0x68, 0x61, 0x6c, 0x65, 0x72, 0x49, 0x6e, 0x74, 0x35, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x22, 0x4b, 0x0a, 0x15, 0x41, 0x6d, 0x69, 0x6e, 0x6f, 0x4d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, - 0x65, 0x72, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x36, 0x12, 0x32, 0x0a, 0x05, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, - 0x2e, 0x41, 0x6d, 0x69, 0x6e, 0x6f, 0x4d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x65, 0x72, 0x53, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x31, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2d, 0x0a, + 0x04, 0x66, 0x6f, 0x6f, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x12, 0x52, 0x04, 0x46, 0x6f, 0x6f, + 0x31, 0x12, 0x44, 0x0a, 0x11, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x5f, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, + 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x10, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, + 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6f, 0x6f, 0x32, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x46, 0x6f, 0x6f, 0x32, 0x12, 0x43, 0x0a, 0x13, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, + 0x2e, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x11, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6f, 0x6f, 0x33, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x46, 0x6f, 0x6f, 0x33, 0x12, 0x38, 0x0a, 0x0d, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x5f, 0x73, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, + 0x73, 0x74, 0x73, 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x52, 0x0c, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x66, 0x6f, 0x6f, 0x34, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x46, 0x6f, + 0x6f, 0x34, 0x12, 0x49, 0x0a, 0x15, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x73, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x13, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x66, 0x6f, 0x6f, 0x35, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x46, 0x6f, 0x6f, + 0x35, 0x22, 0x8d, 0x03, 0x0a, 0x17, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x53, 0x74, + 0x35, 0x4e, 0x61, 0x6d, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x66, 0x6f, 0x6f, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x12, 0x52, 0x04, 0x46, 0x6f, 0x6f, + 0x31, 0x12, 0x44, 0x0a, 0x11, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x5f, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, + 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x10, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, + 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6f, 0x6f, 0x32, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x46, 0x6f, 0x6f, 0x32, 0x12, 0x43, 0x0a, 0x13, 0x61, + 0x72, 0x72, 0x61, 0x79, 0x73, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, + 0x2e, 0x41, 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x11, 0x41, + 0x72, 0x72, 0x61, 0x79, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6f, 0x6f, 0x33, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x46, 0x6f, 0x6f, 0x33, 0x12, 0x38, 0x0a, 0x0d, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x5f, 0x73, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, + 0x73, 0x74, 0x73, 0x2e, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, + 0x52, 0x0c, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x66, 0x6f, 0x6f, 0x34, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x46, 0x6f, + 0x6f, 0x34, 0x12, 0x49, 0x0a, 0x15, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x73, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x13, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x66, 0x6f, 0x6f, 0x35, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x46, 0x6f, 0x6f, + 0x35, 0x22, 0x33, 0x0a, 0x15, 0x41, 0x6d, 0x69, 0x6e, 0x6f, 0x4d, 0x61, 0x72, 0x73, 0x68, 0x61, + 0x6c, 0x65, 0x72, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x31, 0x12, 0x0c, 0x0a, 0x01, 0x63, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x12, 0x52, 0x01, 0x43, 0x12, 0x0c, 0x0a, 0x01, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x12, 0x52, 0x01, 0x44, 0x22, 0x29, 0x0a, 0x0b, 0x52, 0x65, 0x70, 0x72, 0x53, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x31, 0x12, 0x0c, 0x0a, 0x01, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x12, + 0x52, 0x01, 0x43, 0x12, 0x0c, 0x0a, 0x01, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x12, 0x52, 0x01, + 0x44, 0x22, 0x3f, 0x0a, 0x15, 0x41, 0x6d, 0x69, 0x6e, 0x6f, 0x4d, 0x61, 0x72, 0x73, 0x68, 0x61, + 0x6c, 0x65, 0x72, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x32, 0x12, 0x26, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, + 0x73, 0x2e, 0x52, 0x65, 0x70, 0x72, 0x45, 0x6c, 0x65, 0x6d, 0x32, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x22, 0x49, 0x0a, 0x09, 0x52, 0x65, 0x70, 0x72, 0x45, 0x6c, 0x65, 0x6d, 0x32, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x4b, 0x65, + 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2d, 0x0a, 0x15, 0x41, 0x6d, 0x69, 0x6e, 0x6f, 0x4d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x65, 0x72, 0x53, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x37, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x21, 0x0a, 0x09, - 0x52, 0x65, 0x70, 0x72, 0x45, 0x6c, 0x65, 0x6d, 0x37, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, - 0x1e, 0x0a, 0x06, 0x49, 0x6e, 0x74, 0x44, 0x65, 0x66, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x12, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, - 0x1d, 0x0a, 0x05, 0x49, 0x6e, 0x74, 0x41, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x12, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x1d, - 0x0a, 0x05, 0x49, 0x6e, 0x74, 0x53, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x12, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x1e, 0x0a, - 0x06, 0x42, 0x79, 0x74, 0x65, 0x41, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x1e, 0x0a, - 0x06, 0x42, 0x79, 0x74, 0x65, 0x53, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xc0, 0x04, - 0x0a, 0x13, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x44, 0x65, 0x66, 0x12, 0x12, 0x0a, 0x04, 0x49, 0x6e, 0x74, 0x38, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x11, 0x52, 0x04, 0x49, 0x6e, 0x74, 0x38, 0x12, 0x14, 0x0a, 0x05, 0x49, 0x6e, 0x74, - 0x31, 0x36, 0x18, 0x02, 0x20, 0x01, 0x28, 0x11, 0x52, 0x05, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x12, - 0x14, 0x0a, 0x05, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x03, 0x20, 0x01, 0x28, 0x11, 0x52, 0x05, - 0x49, 0x6e, 0x74, 0x33, 0x32, 0x12, 0x1e, 0x0a, 0x0a, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, - 0x78, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0f, 0x52, 0x0a, 0x49, 0x6e, 0x74, 0x33, 0x32, - 0x46, 0x69, 0x78, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x12, 0x52, 0x05, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x1e, 0x0a, 0x0a, 0x49, - 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x10, 0x52, - 0x0a, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x49, - 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x12, 0x52, 0x03, 0x49, 0x6e, 0x74, 0x12, 0x12, 0x0a, - 0x04, 0x42, 0x79, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x42, 0x79, 0x74, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x05, 0x55, 0x69, 0x6e, 0x74, 0x38, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x31, - 0x36, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x12, - 0x16, 0x0a, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x06, 0x55, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x12, 0x20, 0x0a, 0x0b, 0x55, 0x69, 0x6e, 0x74, 0x33, - 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x07, 0x52, 0x0b, 0x55, 0x69, - 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x69, 0x6e, - 0x74, 0x36, 0x34, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x36, - 0x34, 0x12, 0x20, 0x0a, 0x0b, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x33, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x11, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x22, 0x0a, 0x12, + 0x41, 0x6d, 0x69, 0x6e, 0x6f, 0x4d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x65, 0x72, 0x49, 0x6e, + 0x74, 0x34, 0x12, 0x0c, 0x0a, 0x01, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x11, 0x52, 0x01, 0x41, + 0x22, 0x2a, 0x0a, 0x12, 0x41, 0x6d, 0x69, 0x6e, 0x6f, 0x4d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, + 0x65, 0x72, 0x49, 0x6e, 0x74, 0x35, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4b, 0x0a, 0x15, + 0x41, 0x6d, 0x69, 0x6e, 0x6f, 0x4d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x65, 0x72, 0x53, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x36, 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x41, 0x6d, 0x69, + 0x6e, 0x6f, 0x4d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x31, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2d, 0x0a, 0x15, 0x41, 0x6d, 0x69, + 0x6e, 0x6f, 0x4d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x37, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x21, 0x0a, 0x09, 0x52, 0x65, 0x70, 0x72, + 0x45, 0x6c, 0x65, 0x6d, 0x37, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x1e, 0x0a, 0x06, 0x49, + 0x6e, 0x74, 0x44, 0x65, 0x66, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x12, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x1d, 0x0a, 0x05, 0x49, + 0x6e, 0x74, 0x41, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x12, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x1d, 0x0a, 0x05, 0x49, 0x6e, + 0x74, 0x53, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x12, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x1e, 0x0a, 0x06, 0x42, 0x79, 0x74, + 0x65, 0x41, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x1e, 0x0a, 0x06, 0x42, 0x79, 0x74, + 0x65, 0x53, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xc4, 0x04, 0x0a, 0x13, 0x50, 0x72, + 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x44, 0x65, + 0x66, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x74, 0x38, 0x18, 0x01, 0x20, 0x01, 0x28, 0x11, 0x52, + 0x04, 0x49, 0x6e, 0x74, 0x38, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x11, 0x52, 0x05, 0x49, 0x6e, 0x74, 0x31, 0x36, 0x12, 0x14, 0x0a, 0x05, 0x69, + 0x6e, 0x74, 0x33, 0x32, 0x18, 0x03, 0x20, 0x01, 0x28, 0x11, 0x52, 0x05, 0x49, 0x6e, 0x74, 0x33, + 0x32, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0f, 0x52, 0x0a, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, + 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x12, 0x52, 0x05, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x74, 0x36, + 0x34, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x10, 0x52, 0x0a, 0x49, + 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, 0x78, 0x65, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x6e, 0x74, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x12, 0x52, 0x03, 0x49, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x62, + 0x79, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x42, 0x79, 0x74, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x75, 0x69, 0x6e, 0x74, 0x38, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, + 0x55, 0x69, 0x6e, 0x74, 0x38, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x12, 0x16, 0x0a, + 0x06, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x55, + 0x69, 0x6e, 0x74, 0x33, 0x32, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, + 0x66, 0x69, 0x78, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x07, 0x52, 0x0b, 0x55, 0x69, 0x6e, + 0x74, 0x33, 0x32, 0x46, 0x69, 0x78, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x69, 0x6e, 0x74, + 0x36, 0x34, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, + 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x66, 0x69, 0x78, 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x06, 0x52, 0x0b, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x46, 0x69, - 0x78, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x55, 0x69, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x04, 0x55, 0x69, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x53, 0x74, 0x72, 0x18, 0x10, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x53, 0x74, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x42, 0x79, 0x74, + 0x78, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x69, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x04, 0x55, 0x69, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x74, 0x72, 0x18, 0x10, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x53, 0x74, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, - 0x2e, 0x0a, 0x04, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x54, 0x69, 0x6d, 0x65, 0x12, - 0x35, 0x0a, 0x08, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x13, 0x20, 0x01, 0x28, + 0x35, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x44, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x05, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x43, 0x0a, 0x12, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x53, 0x6c, 0x12, 0x2d, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x72, 0x75, 0x63, 0x74, 0x53, 0x6c, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x05, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x43, 0x0a, 0x12, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, - 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x41, 0x72, 0x12, 0x2d, 0x0a, 0x05, 0x56, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x43, 0x0a, 0x12, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, + 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x41, 0x72, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x53, 0x74, 0x72, - 0x75, 0x63, 0x74, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x0b, 0x0a, 0x09, 0x43, 0x6f, + 0x75, 0x63, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x0b, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x63, 0x72, 0x65, 0x74, 0x65, 0x31, 0x22, 0x0b, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x63, 0x72, 0x65, 0x74, 0x65, 0x32, 0x22, 0x27, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x63, 0x72, 0x65, 0x74, 0x65, - 0x54, 0x79, 0x70, 0x65, 0x44, 0x65, 0x66, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2c, 0x0a, + 0x54, 0x79, 0x70, 0x65, 0x44, 0x65, 0x66, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x2c, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x63, 0x72, 0x65, 0x74, 0x65, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, - 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, + 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xaf, 0x01, 0x0a, 0x15, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x53, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x24, 0x0a, 0x02, 0x46, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x24, 0x0a, 0x02, 0x66, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x02, 0x46, 0x31, 0x12, 0x24, 0x0a, 0x02, 0x46, + 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x02, 0x46, 0x31, 0x12, 0x24, 0x0a, 0x02, 0x66, 0x32, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x02, 0x46, - 0x32, 0x12, 0x24, 0x0a, 0x02, 0x46, 0x33, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x32, 0x12, 0x24, 0x0a, 0x02, 0x66, 0x33, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x41, 0x6e, 0x79, 0x52, 0x02, 0x46, 0x33, 0x12, 0x24, 0x0a, 0x02, 0x46, 0x34, 0x18, 0x04, 0x20, + 0x41, 0x6e, 0x79, 0x52, 0x02, 0x46, 0x33, 0x12, 0x24, 0x0a, 0x02, 0x66, 0x34, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x02, 0x46, 0x34, 0x22, 0x27, 0x0a, 0x0f, 0x54, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x42, 0x79, 0x74, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, @@ -4780,93 +4796,93 @@ var file_tests_proto_goTypes = []interface{}{ (*anypb.Any)(nil), // 57: google.protobuf.Any } var file_tests_proto_depIdxs = []int32{ - 55, // 0: tests.PrimitivesStruct.Time:type_name -> google.protobuf.Timestamp - 56, // 1: tests.PrimitivesStruct.Duration:type_name -> google.protobuf.Duration - 0, // 2: tests.PrimitivesStruct.Empty:type_name -> tests.EmptyStruct - 55, // 3: tests.ShortArraysStruct.TimeAr:type_name -> google.protobuf.Timestamp - 56, // 4: tests.ShortArraysStruct.DurationAr:type_name -> google.protobuf.Duration - 55, // 5: tests.ArraysStruct.TimeAr:type_name -> google.protobuf.Timestamp - 56, // 6: tests.ArraysStruct.DurationAr:type_name -> google.protobuf.Duration - 0, // 7: tests.ArraysStruct.EmptyAr:type_name -> tests.EmptyStruct - 49, // 8: tests.ArraysArraysStruct.Int8ArAr:type_name -> tests.TESTS_Int8List - 46, // 9: tests.ArraysArraysStruct.Int16ArAr:type_name -> tests.TESTS_Int16List - 47, // 10: tests.ArraysArraysStruct.Int32ArAr:type_name -> tests.TESTS_Int32ValueList - 42, // 11: tests.ArraysArraysStruct.Int32FixedArAr:type_name -> tests.TESTS_Fixed32Int32ValueList - 48, // 12: tests.ArraysArraysStruct.Int64ArAr:type_name -> tests.TESTS_Int64ValueList - 44, // 13: tests.ArraysArraysStruct.Int64FixedArAr:type_name -> tests.TESTS_Fixed64Int64ValueList - 48, // 14: tests.ArraysArraysStruct.IntArAr:type_name -> tests.TESTS_Int64ValueList - 52, // 15: tests.ArraysArraysStruct.Uint16ArAr:type_name -> tests.TESTS_UInt16List - 53, // 16: tests.ArraysArraysStruct.Uint32ArAr:type_name -> tests.TESTS_UInt32ValueList - 43, // 17: tests.ArraysArraysStruct.Uint32FixedArAr:type_name -> tests.TESTS_Fixed32UInt32ValueList - 54, // 18: tests.ArraysArraysStruct.Uint64ArAr:type_name -> tests.TESTS_UInt64ValueList - 45, // 19: tests.ArraysArraysStruct.Uint64FixedArAr:type_name -> tests.TESTS_Fixed64UInt64ValueList - 54, // 20: tests.ArraysArraysStruct.UintArAr:type_name -> tests.TESTS_UInt64ValueList - 50, // 21: tests.ArraysArraysStruct.StrArAr:type_name -> tests.TESTS_StringValueList - 38, // 22: tests.ArraysArraysStruct.BytesArAr:type_name -> tests.TESTS_BytesList - 51, // 23: tests.ArraysArraysStruct.TimeArAr:type_name -> tests.TESTS_TimestampList - 40, // 24: tests.ArraysArraysStruct.DurationArAr:type_name -> tests.TESTS_DurationList - 41, // 25: tests.ArraysArraysStruct.EmptyArAr:type_name -> tests.TESTS_EmptyStructList - 55, // 26: tests.SlicesStruct.TimeSl:type_name -> google.protobuf.Timestamp - 56, // 27: tests.SlicesStruct.DurationSl:type_name -> google.protobuf.Duration - 0, // 28: tests.SlicesStruct.EmptySl:type_name -> tests.EmptyStruct - 49, // 29: tests.SlicesSlicesStruct.Int8SlSl:type_name -> tests.TESTS_Int8List - 46, // 30: tests.SlicesSlicesStruct.Int16SlSl:type_name -> tests.TESTS_Int16List - 47, // 31: tests.SlicesSlicesStruct.Int32SlSl:type_name -> tests.TESTS_Int32ValueList - 42, // 32: tests.SlicesSlicesStruct.Int32FixedSlSl:type_name -> tests.TESTS_Fixed32Int32ValueList - 48, // 33: tests.SlicesSlicesStruct.Int64SlSl:type_name -> tests.TESTS_Int64ValueList - 44, // 34: tests.SlicesSlicesStruct.Int64FixedSlSl:type_name -> tests.TESTS_Fixed64Int64ValueList - 48, // 35: tests.SlicesSlicesStruct.IntSlSl:type_name -> tests.TESTS_Int64ValueList - 52, // 36: tests.SlicesSlicesStruct.Uint16SlSl:type_name -> tests.TESTS_UInt16List - 53, // 37: tests.SlicesSlicesStruct.Uint32SlSl:type_name -> tests.TESTS_UInt32ValueList - 43, // 38: tests.SlicesSlicesStruct.Uint32FixedSlSl:type_name -> tests.TESTS_Fixed32UInt32ValueList - 54, // 39: tests.SlicesSlicesStruct.Uint64SlSl:type_name -> tests.TESTS_UInt64ValueList - 45, // 40: tests.SlicesSlicesStruct.Uint64FixedSlSl:type_name -> tests.TESTS_Fixed64UInt64ValueList - 54, // 41: tests.SlicesSlicesStruct.UintSlSl:type_name -> tests.TESTS_UInt64ValueList - 50, // 42: tests.SlicesSlicesStruct.StrSlSl:type_name -> tests.TESTS_StringValueList - 38, // 43: tests.SlicesSlicesStruct.BytesSlSl:type_name -> tests.TESTS_BytesList - 51, // 44: tests.SlicesSlicesStruct.TimeSlSl:type_name -> tests.TESTS_TimestampList - 40, // 45: tests.SlicesSlicesStruct.DurationSlSl:type_name -> tests.TESTS_DurationList - 41, // 46: tests.SlicesSlicesStruct.EmptySlSl:type_name -> tests.TESTS_EmptyStructList - 55, // 47: tests.PointersStruct.TimePt:type_name -> google.protobuf.Timestamp - 56, // 48: tests.PointersStruct.DurationPt:type_name -> google.protobuf.Duration - 0, // 49: tests.PointersStruct.EmptyPt:type_name -> tests.EmptyStruct - 55, // 50: tests.PointerSlicesStruct.TimePtSl:type_name -> google.protobuf.Timestamp - 56, // 51: tests.PointerSlicesStruct.DurationPtSl:type_name -> google.protobuf.Duration - 0, // 52: tests.PointerSlicesStruct.EmptyPtSl:type_name -> tests.EmptyStruct - 1, // 53: tests.ComplexSt.PrField:type_name -> tests.PrimitivesStruct - 3, // 54: tests.ComplexSt.ArField:type_name -> tests.ArraysStruct - 5, // 55: tests.ComplexSt.SlField:type_name -> tests.SlicesStruct - 7, // 56: tests.ComplexSt.PtField:type_name -> tests.PointersStruct - 1, // 57: tests.EmbeddedSt1.PrimitivesStruct:type_name -> tests.PrimitivesStruct - 1, // 58: tests.EmbeddedSt2.PrimitivesStruct:type_name -> tests.PrimitivesStruct - 3, // 59: tests.EmbeddedSt2.ArraysStruct:type_name -> tests.ArraysStruct - 5, // 60: tests.EmbeddedSt2.SlicesStruct:type_name -> tests.SlicesStruct - 7, // 61: tests.EmbeddedSt2.PointersStruct:type_name -> tests.PointersStruct - 1, // 62: tests.EmbeddedSt3.PrimitivesStruct:type_name -> tests.PrimitivesStruct - 3, // 63: tests.EmbeddedSt3.ArraysStruct:type_name -> tests.ArraysStruct - 5, // 64: tests.EmbeddedSt3.SlicesStruct:type_name -> tests.SlicesStruct - 7, // 65: tests.EmbeddedSt3.PointersStruct:type_name -> tests.PointersStruct - 0, // 66: tests.EmbeddedSt3.EmptyStruct:type_name -> tests.EmptyStruct - 1, // 67: tests.EmbeddedSt4.PrimitivesStruct:type_name -> tests.PrimitivesStruct - 3, // 68: tests.EmbeddedSt4.ArraysStructField:type_name -> tests.ArraysStruct - 5, // 69: tests.EmbeddedSt4.SlicesStruct:type_name -> tests.SlicesStruct - 7, // 70: tests.EmbeddedSt4.PointersStructField:type_name -> tests.PointersStruct - 1, // 71: tests.EmbeddedSt5NameOverride.PrimitivesStruct:type_name -> tests.PrimitivesStruct - 3, // 72: tests.EmbeddedSt5NameOverride.ArraysStructField:type_name -> tests.ArraysStruct - 5, // 73: tests.EmbeddedSt5NameOverride.SlicesStruct:type_name -> tests.SlicesStruct - 7, // 74: tests.EmbeddedSt5NameOverride.PointersStructField:type_name -> tests.PointersStruct - 18, // 75: tests.AminoMarshalerStruct2.Value:type_name -> tests.ReprElem2 - 57, // 76: tests.ReprElem2.Value:type_name -> google.protobuf.Any - 15, // 77: tests.AminoMarshalerStruct6.Value:type_name -> tests.AminoMarshalerStruct1 - 55, // 78: tests.PrimitivesStructDef.Time:type_name -> google.protobuf.Timestamp - 56, // 79: tests.PrimitivesStructDef.Duration:type_name -> google.protobuf.Duration - 0, // 80: tests.PrimitivesStructDef.Empty:type_name -> tests.EmptyStruct - 1, // 81: tests.PrimitivesStructSl.Value:type_name -> tests.PrimitivesStruct - 1, // 82: tests.PrimitivesStructAr.Value:type_name -> tests.PrimitivesStruct - 57, // 83: tests.InterfaceFieldsStruct.F1:type_name -> google.protobuf.Any - 57, // 84: tests.InterfaceFieldsStruct.F2:type_name -> google.protobuf.Any - 57, // 85: tests.InterfaceFieldsStruct.F3:type_name -> google.protobuf.Any - 57, // 86: tests.InterfaceFieldsStruct.F4:type_name -> google.protobuf.Any + 55, // 0: tests.PrimitivesStruct.time:type_name -> google.protobuf.Timestamp + 56, // 1: tests.PrimitivesStruct.duration:type_name -> google.protobuf.Duration + 0, // 2: tests.PrimitivesStruct.empty:type_name -> tests.EmptyStruct + 55, // 3: tests.ShortArraysStruct.time_ar:type_name -> google.protobuf.Timestamp + 56, // 4: tests.ShortArraysStruct.duration_ar:type_name -> google.protobuf.Duration + 55, // 5: tests.ArraysStruct.time_ar:type_name -> google.protobuf.Timestamp + 56, // 6: tests.ArraysStruct.duration_ar:type_name -> google.protobuf.Duration + 0, // 7: tests.ArraysStruct.empty_ar:type_name -> tests.EmptyStruct + 49, // 8: tests.ArraysArraysStruct.int8_ar_ar:type_name -> tests.TESTS_Int8List + 46, // 9: tests.ArraysArraysStruct.int16_ar_ar:type_name -> tests.TESTS_Int16List + 47, // 10: tests.ArraysArraysStruct.int32_ar_ar:type_name -> tests.TESTS_Int32ValueList + 42, // 11: tests.ArraysArraysStruct.int32_fixed_ar_ar:type_name -> tests.TESTS_Fixed32Int32ValueList + 48, // 12: tests.ArraysArraysStruct.int64_ar_ar:type_name -> tests.TESTS_Int64ValueList + 44, // 13: tests.ArraysArraysStruct.int64_fixed_ar_ar:type_name -> tests.TESTS_Fixed64Int64ValueList + 48, // 14: tests.ArraysArraysStruct.int_ar_ar:type_name -> tests.TESTS_Int64ValueList + 52, // 15: tests.ArraysArraysStruct.uint16_ar_ar:type_name -> tests.TESTS_UInt16List + 53, // 16: tests.ArraysArraysStruct.uint32_ar_ar:type_name -> tests.TESTS_UInt32ValueList + 43, // 17: tests.ArraysArraysStruct.uint32_fixed_ar_ar:type_name -> tests.TESTS_Fixed32UInt32ValueList + 54, // 18: tests.ArraysArraysStruct.uint64_ar_ar:type_name -> tests.TESTS_UInt64ValueList + 45, // 19: tests.ArraysArraysStruct.uint64_fixed_ar_ar:type_name -> tests.TESTS_Fixed64UInt64ValueList + 54, // 20: tests.ArraysArraysStruct.uint_ar_ar:type_name -> tests.TESTS_UInt64ValueList + 50, // 21: tests.ArraysArraysStruct.str_ar_ar:type_name -> tests.TESTS_StringValueList + 38, // 22: tests.ArraysArraysStruct.bytes_ar_ar:type_name -> tests.TESTS_BytesList + 51, // 23: tests.ArraysArraysStruct.time_ar_ar:type_name -> tests.TESTS_TimestampList + 40, // 24: tests.ArraysArraysStruct.duration_ar_ar:type_name -> tests.TESTS_DurationList + 41, // 25: tests.ArraysArraysStruct.empty_ar_ar:type_name -> tests.TESTS_EmptyStructList + 55, // 26: tests.SlicesStruct.time_sl:type_name -> google.protobuf.Timestamp + 56, // 27: tests.SlicesStruct.duration_sl:type_name -> google.protobuf.Duration + 0, // 28: tests.SlicesStruct.empty_sl:type_name -> tests.EmptyStruct + 49, // 29: tests.SlicesSlicesStruct.int8_sl_sl:type_name -> tests.TESTS_Int8List + 46, // 30: tests.SlicesSlicesStruct.int16_sl_sl:type_name -> tests.TESTS_Int16List + 47, // 31: tests.SlicesSlicesStruct.int32_sl_sl:type_name -> tests.TESTS_Int32ValueList + 42, // 32: tests.SlicesSlicesStruct.int32_fixed_sl_sl:type_name -> tests.TESTS_Fixed32Int32ValueList + 48, // 33: tests.SlicesSlicesStruct.int64_sl_sl:type_name -> tests.TESTS_Int64ValueList + 44, // 34: tests.SlicesSlicesStruct.int64_fixed_sl_sl:type_name -> tests.TESTS_Fixed64Int64ValueList + 48, // 35: tests.SlicesSlicesStruct.int_sl_sl:type_name -> tests.TESTS_Int64ValueList + 52, // 36: tests.SlicesSlicesStruct.uint16_sl_sl:type_name -> tests.TESTS_UInt16List + 53, // 37: tests.SlicesSlicesStruct.uint32_sl_sl:type_name -> tests.TESTS_UInt32ValueList + 43, // 38: tests.SlicesSlicesStruct.uint32_fixed_sl_sl:type_name -> tests.TESTS_Fixed32UInt32ValueList + 54, // 39: tests.SlicesSlicesStruct.uint64_sl_sl:type_name -> tests.TESTS_UInt64ValueList + 45, // 40: tests.SlicesSlicesStruct.uint64_fixed_sl_sl:type_name -> tests.TESTS_Fixed64UInt64ValueList + 54, // 41: tests.SlicesSlicesStruct.uint_sl_sl:type_name -> tests.TESTS_UInt64ValueList + 50, // 42: tests.SlicesSlicesStruct.str_sl_sl:type_name -> tests.TESTS_StringValueList + 38, // 43: tests.SlicesSlicesStruct.bytes_sl_sl:type_name -> tests.TESTS_BytesList + 51, // 44: tests.SlicesSlicesStruct.time_sl_sl:type_name -> tests.TESTS_TimestampList + 40, // 45: tests.SlicesSlicesStruct.duration_sl_sl:type_name -> tests.TESTS_DurationList + 41, // 46: tests.SlicesSlicesStruct.empty_sl_sl:type_name -> tests.TESTS_EmptyStructList + 55, // 47: tests.PointersStruct.time_pt:type_name -> google.protobuf.Timestamp + 56, // 48: tests.PointersStruct.duration_pt:type_name -> google.protobuf.Duration + 0, // 49: tests.PointersStruct.empty_pt:type_name -> tests.EmptyStruct + 55, // 50: tests.PointerSlicesStruct.time_pt_sl:type_name -> google.protobuf.Timestamp + 56, // 51: tests.PointerSlicesStruct.duration_pt_sl:type_name -> google.protobuf.Duration + 0, // 52: tests.PointerSlicesStruct.empty_pt_sl:type_name -> tests.EmptyStruct + 1, // 53: tests.ComplexSt.pr_field:type_name -> tests.PrimitivesStruct + 3, // 54: tests.ComplexSt.ar_field:type_name -> tests.ArraysStruct + 5, // 55: tests.ComplexSt.sl_field:type_name -> tests.SlicesStruct + 7, // 56: tests.ComplexSt.pt_field:type_name -> tests.PointersStruct + 1, // 57: tests.EmbeddedSt1.primitives_struct:type_name -> tests.PrimitivesStruct + 1, // 58: tests.EmbeddedSt2.primitives_struct:type_name -> tests.PrimitivesStruct + 3, // 59: tests.EmbeddedSt2.arrays_struct:type_name -> tests.ArraysStruct + 5, // 60: tests.EmbeddedSt2.slices_struct:type_name -> tests.SlicesStruct + 7, // 61: tests.EmbeddedSt2.pointers_struct:type_name -> tests.PointersStruct + 1, // 62: tests.EmbeddedSt3.primitives_struct:type_name -> tests.PrimitivesStruct + 3, // 63: tests.EmbeddedSt3.arrays_struct:type_name -> tests.ArraysStruct + 5, // 64: tests.EmbeddedSt3.slices_struct:type_name -> tests.SlicesStruct + 7, // 65: tests.EmbeddedSt3.pointers_struct:type_name -> tests.PointersStruct + 0, // 66: tests.EmbeddedSt3.empty_struct:type_name -> tests.EmptyStruct + 1, // 67: tests.EmbeddedSt4.primitives_struct:type_name -> tests.PrimitivesStruct + 3, // 68: tests.EmbeddedSt4.arrays_struct_field:type_name -> tests.ArraysStruct + 5, // 69: tests.EmbeddedSt4.slices_struct:type_name -> tests.SlicesStruct + 7, // 70: tests.EmbeddedSt4.pointers_struct_field:type_name -> tests.PointersStruct + 1, // 71: tests.EmbeddedSt5NameOverride.primitives_struct:type_name -> tests.PrimitivesStruct + 3, // 72: tests.EmbeddedSt5NameOverride.arrays_struct_field:type_name -> tests.ArraysStruct + 5, // 73: tests.EmbeddedSt5NameOverride.slices_struct:type_name -> tests.SlicesStruct + 7, // 74: tests.EmbeddedSt5NameOverride.pointers_struct_field:type_name -> tests.PointersStruct + 18, // 75: tests.AminoMarshalerStruct2.value:type_name -> tests.ReprElem2 + 57, // 76: tests.ReprElem2.value:type_name -> google.protobuf.Any + 15, // 77: tests.AminoMarshalerStruct6.value:type_name -> tests.AminoMarshalerStruct1 + 55, // 78: tests.PrimitivesStructDef.time:type_name -> google.protobuf.Timestamp + 56, // 79: tests.PrimitivesStructDef.duration:type_name -> google.protobuf.Duration + 0, // 80: tests.PrimitivesStructDef.empty:type_name -> tests.EmptyStruct + 1, // 81: tests.PrimitivesStructSl.value:type_name -> tests.PrimitivesStruct + 1, // 82: tests.PrimitivesStructAr.value:type_name -> tests.PrimitivesStruct + 57, // 83: tests.InterfaceFieldsStruct.f1:type_name -> google.protobuf.Any + 57, // 84: tests.InterfaceFieldsStruct.f2:type_name -> google.protobuf.Any + 57, // 85: tests.InterfaceFieldsStruct.f3:type_name -> google.protobuf.Any + 57, // 86: tests.InterfaceFieldsStruct.f4:type_name -> google.protobuf.Any 38, // 87: tests.TESTS_BytesListList.Value:type_name -> tests.TESTS_BytesList 56, // 88: tests.TESTS_DurationList.Value:type_name -> google.protobuf.Duration 0, // 89: tests.TESTS_EmptyStructList.Value:type_name -> tests.EmptyStruct diff --git a/tm2/pkg/amino/tests/proto3/proto/compat.pb.go b/tm2/pkg/amino/tests/proto3/proto/compat.pb.go index 753f17a9d83..b03a9273125 100644 --- a/tm2/pkg/amino/tests/proto3/proto/compat.pb.go +++ b/tm2/pkg/amino/tests/proto3/proto/compat.pb.go @@ -1,17 +1,18 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 -// protoc v3.21.12 +// protoc-gen-go v1.31.0 +// protoc v4.24.3 // source: proto/compat.proto package proto3 import ( + reflect "reflect" + sync "sync" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" - reflect "reflect" - sync "sync" ) const ( diff --git a/tm2/pkg/amino/tests/proto3/proto3_compat_test.go b/tm2/pkg/amino/tests/proto3/proto3_compat_test.go index bd2a51825da..8f9e04fc35c 100644 --- a/tm2/pkg/amino/tests/proto3/proto3_compat_test.go +++ b/tm2/pkg/amino/tests/proto3/proto3_compat_test.go @@ -36,6 +36,8 @@ func init() { } func TestFixed32Roundtrip(t *testing.T) { + t.Parallel() + // amino fixed32 (int32) <-> protbuf fixed32 (uint32) type testi32 struct { Int32 int32 `binary:"fixed32"` @@ -61,6 +63,8 @@ func TestFixed32Roundtrip(t *testing.T) { } func TestVarintZigzagRoundtrip(t *testing.T) { + t.Parallel() + t.Skip("zigzag encoding isn't default anymore for (unsigned) ints") // amino varint (int) <-> protobuf zigzag32 (int32 in go sint32 in proto file) type testInt32Varint struct { @@ -85,6 +89,8 @@ func TestVarintZigzagRoundtrip(t *testing.T) { } func TestFixedU64Roundtrip(t *testing.T) { + t.Parallel() + type testFixed64Uint struct { Int64 uint64 `binary:"fixed64"` } @@ -111,6 +117,8 @@ func TestFixedU64Roundtrip(t *testing.T) { } func TestMultidimensionalSlices(t *testing.T) { + t.Parallel() + s := [][]int8{ {1, 2}, {3, 4, 5}, @@ -121,6 +129,8 @@ func TestMultidimensionalSlices(t *testing.T) { } func TestMultidimensionalArrays(t *testing.T) { + t.Parallel() + arr := [2][2]int8{ {1, 2}, {3, 4}, @@ -131,6 +141,8 @@ func TestMultidimensionalArrays(t *testing.T) { } func TestMultidimensionalByteArraysAndSlices(t *testing.T) { + t.Parallel() + arr := [2][2]byte{ {1, 2}, {3, 4}, @@ -157,6 +169,8 @@ func TestMultidimensionalByteArraysAndSlices(t *testing.T) { } func TestProto3CompatPtrsRoundtrip(t *testing.T) { + t.Parallel() + s := p3.SomeStruct{} ab, err := cdc.Marshal(s) @@ -214,6 +228,8 @@ type goAminoGotTime struct { } func TestProto3CompatEmptyTimestamp(t *testing.T) { + t.Parallel() + empty := p3.ProtoGotTime{} // protobuf also marshals to empty bytes here: pb, err := proto.Marshal(&empty) @@ -235,6 +251,8 @@ func TestProto3CompatEmptyTimestamp(t *testing.T) { } func TestProto3CompatTimestampNow(t *testing.T) { + t.Parallel() + // test with current time: now := time.Now() ptts, err := ptypes.TimestampProto(now) @@ -267,6 +285,8 @@ func TestProto3CompatTimestampNow(t *testing.T) { } func TestProto3EpochTime(t *testing.T) { + t.Parallel() + pbRes := p3.ProtoGotTime{} // amino encode epoch (1970) and decode using proto; expect the resulting time to be epoch again: ab, err := cdc.Marshal(goAminoGotTime{T: &epoch}) @@ -279,6 +299,8 @@ func TestProto3EpochTime(t *testing.T) { } func TestProtoNegativeSeconds(t *testing.T) { + t.Parallel() + pbRes := p3.ProtoGotTime{} // test with negative seconds (0001-01-01 -> seconds = -62135596800, nanos = 0): ntm, err := time.Parse("2006-01-02 15:04:05 +0000 UTC", "0001-01-01 00:00:00 +0000 UTC") @@ -296,6 +318,8 @@ func TestProtoNegativeSeconds(t *testing.T) { } func TestIntVarintCompat(t *testing.T) { + t.Parallel() + tcs := []struct { val32 int32 val64 int64 @@ -382,6 +406,8 @@ func TestIntVarintCompat(t *testing.T) { // See if encoding of type def types matches the proto3 encoding func TestTypeDefCompatibility(t *testing.T) { + t.Parallel() + pNow := ptypes.TimestampNow() now, err := ptypes.Timestamp(pNow) require.NoError(t, err) diff --git a/tm2/pkg/amino/tests/tests.proto b/tm2/pkg/amino/tests/tests.proto index 8a9598dded9..c355789eb63 100644 --- a/tm2/pkg/amino/tests/tests.proto +++ b/tm2/pkg/amino/tests/tests.proto @@ -13,313 +13,313 @@ message EmptyStruct { } message PrimitivesStruct { - sint32 Int8 = 1; - sint32 Int16 = 2; - sint32 Int32 = 3; - sfixed32 Int32Fixed = 4; - sint64 Int64 = 5; - sfixed64 Int64Fixed = 6; - sint64 Int = 7; - uint32 Byte = 8; - uint32 Uint8 = 9; - uint32 Uint16 = 10; - uint32 Uint32 = 11; - fixed32 Uint32Fixed = 12; - uint64 Uint64 = 13; - fixed64 Uint64Fixed = 14; - uint64 Uint = 15; - string Str = 16; - bytes Bytes = 17; - google.protobuf.Timestamp Time = 18; - google.protobuf.Duration Duration = 19; - EmptyStruct Empty = 20; + sint32 int8 = 1 [json_name = "Int8"]; + sint32 int16 = 2 [json_name = "Int16"]; + sint32 int32 = 3 [json_name = "Int32"]; + sfixed32 int32_fixed = 4 [json_name = "Int32Fixed"]; + sint64 int64 = 5 [json_name = "Int64"]; + sfixed64 int64_fixed = 6 [json_name = "Int64Fixed"]; + sint64 int = 7 [json_name = "Int"]; + uint32 byte = 8 [json_name = "Byte"]; + uint32 uint8 = 9 [json_name = "Uint8"]; + uint32 uint16 = 10 [json_name = "Uint16"]; + uint32 uint32 = 11 [json_name = "Uint32"]; + fixed32 uint32_fixed = 12 [json_name = "Uint32Fixed"]; + uint64 uint64 = 13 [json_name = "Uint64"]; + fixed64 uint64_fixed = 14 [json_name = "Uint64Fixed"]; + uint64 uint = 15 [json_name = "Uint"]; + string str = 16 [json_name = "Str"]; + bytes bytes = 17 [json_name = "Bytes"]; + google.protobuf.Timestamp time = 18 [json_name = "Time"]; + google.protobuf.Duration duration = 19 [json_name = "Duration"]; + EmptyStruct empty = 20 [json_name = "Empty"]; } message ShortArraysStruct { - repeated google.protobuf.Timestamp TimeAr = 1; - repeated google.protobuf.Duration DurationAr = 2; + repeated google.protobuf.Timestamp time_ar = 1 [json_name = "TimeAr"]; + repeated google.protobuf.Duration duration_ar = 2 [json_name = "DurationAr"]; } message ArraysStruct { - repeated sint32 Int8Ar = 1; - repeated sint32 Int16Ar = 2; - repeated sint32 Int32Ar = 3; - repeated sfixed32 Int32FixedAr = 4; - repeated sint64 Int64Ar = 5; - repeated sfixed64 Int64FixedAr = 6; - repeated sint64 IntAr = 7; - bytes ByteAr = 8; - bytes Uint8Ar = 9; - repeated uint32 Uint16Ar = 10; - repeated uint32 Uint32Ar = 11; - repeated fixed32 Uint32FixedAr = 12; - repeated uint64 Uint64Ar = 13; - repeated fixed64 Uint64FixedAr = 14; - repeated uint64 UintAr = 15; - repeated string StrAr = 16; - repeated bytes BytesAr = 17; - repeated google.protobuf.Timestamp TimeAr = 18; - repeated google.protobuf.Duration DurationAr = 19; - repeated EmptyStruct EmptyAr = 20; + repeated sint32 int8_ar = 1 [json_name = "Int8Ar"]; + repeated sint32 int16_ar = 2 [json_name = "Int16Ar"]; + repeated sint32 int32_ar = 3 [json_name = "Int32Ar"]; + repeated sfixed32 int32_fixed_ar = 4 [json_name = "Int32FixedAr"]; + repeated sint64 int64_ar = 5 [json_name = "Int64Ar"]; + repeated sfixed64 int64_fixed_ar = 6 [json_name = "Int64FixedAr"]; + repeated sint64 int_ar = 7 [json_name = "IntAr"]; + bytes byte_ar = 8 [json_name = "ByteAr"]; + bytes uint8_ar = 9 [json_name = "Uint8Ar"]; + repeated uint32 uint16_ar = 10 [json_name = "Uint16Ar"]; + repeated uint32 uint32_ar = 11 [json_name = "Uint32Ar"]; + repeated fixed32 uint32_fixed_ar = 12 [json_name = "Uint32FixedAr"]; + repeated uint64 uint64_ar = 13 [json_name = "Uint64Ar"]; + repeated fixed64 uint64_fixed_ar = 14 [json_name = "Uint64FixedAr"]; + repeated uint64 uint_ar = 15 [json_name = "UintAr"]; + repeated string str_ar = 16 [json_name = "StrAr"]; + repeated bytes bytes_ar = 17 [json_name = "BytesAr"]; + repeated google.protobuf.Timestamp time_ar = 18 [json_name = "TimeAr"]; + repeated google.protobuf.Duration duration_ar = 19 [json_name = "DurationAr"]; + repeated EmptyStruct empty_ar = 20 [json_name = "EmptyAr"]; } message ArraysArraysStruct { - repeated TESTS_Int8List Int8ArAr = 1; - repeated TESTS_Int16List Int16ArAr = 2; - repeated TESTS_Int32ValueList Int32ArAr = 3; - repeated TESTS_Fixed32Int32ValueList Int32FixedArAr = 4; - repeated TESTS_Int64ValueList Int64ArAr = 5; - repeated TESTS_Fixed64Int64ValueList Int64FixedArAr = 6; - repeated TESTS_Int64ValueList IntArAr = 7; - repeated bytes ByteArAr = 8; - repeated bytes Uint8ArAr = 9; - repeated TESTS_UInt16List Uint16ArAr = 10; - repeated TESTS_UInt32ValueList Uint32ArAr = 11; - repeated TESTS_Fixed32UInt32ValueList Uint32FixedArAr = 12; - repeated TESTS_UInt64ValueList Uint64ArAr = 13; - repeated TESTS_Fixed64UInt64ValueList Uint64FixedArAr = 14; - repeated TESTS_UInt64ValueList UintArAr = 15; - repeated TESTS_StringValueList StrArAr = 16; - repeated TESTS_BytesList BytesArAr = 17; - repeated TESTS_TimestampList TimeArAr = 18; - repeated TESTS_DurationList DurationArAr = 19; - repeated TESTS_EmptyStructList EmptyArAr = 20; + repeated TESTS_Int8List int8_ar_ar = 1 [json_name = "Int8ArAr"]; + repeated TESTS_Int16List int16_ar_ar = 2 [json_name = "Int16ArAr"]; + repeated TESTS_Int32ValueList int32_ar_ar = 3 [json_name = "Int32ArAr"]; + repeated TESTS_Fixed32Int32ValueList int32_fixed_ar_ar = 4 [json_name = "Int32FixedArAr"]; + repeated TESTS_Int64ValueList int64_ar_ar = 5 [json_name = "Int64ArAr"]; + repeated TESTS_Fixed64Int64ValueList int64_fixed_ar_ar = 6 [json_name = "Int64FixedArAr"]; + repeated TESTS_Int64ValueList int_ar_ar = 7 [json_name = "IntArAr"]; + repeated bytes byte_ar_ar = 8 [json_name = "ByteArAr"]; + repeated bytes uint8_ar_ar = 9 [json_name = "Uint8ArAr"]; + repeated TESTS_UInt16List uint16_ar_ar = 10 [json_name = "Uint16ArAr"]; + repeated TESTS_UInt32ValueList uint32_ar_ar = 11 [json_name = "Uint32ArAr"]; + repeated TESTS_Fixed32UInt32ValueList uint32_fixed_ar_ar = 12 [json_name = "Uint32FixedArAr"]; + repeated TESTS_UInt64ValueList uint64_ar_ar = 13 [json_name = "Uint64ArAr"]; + repeated TESTS_Fixed64UInt64ValueList uint64_fixed_ar_ar = 14 [json_name = "Uint64FixedArAr"]; + repeated TESTS_UInt64ValueList uint_ar_ar = 15 [json_name = "UintArAr"]; + repeated TESTS_StringValueList str_ar_ar = 16 [json_name = "StrArAr"]; + repeated TESTS_BytesList bytes_ar_ar = 17 [json_name = "BytesArAr"]; + repeated TESTS_TimestampList time_ar_ar = 18 [json_name = "TimeArAr"]; + repeated TESTS_DurationList duration_ar_ar = 19 [json_name = "DurationArAr"]; + repeated TESTS_EmptyStructList empty_ar_ar = 20 [json_name = "EmptyArAr"]; } message SlicesStruct { - repeated sint32 Int8Sl = 1; - repeated sint32 Int16Sl = 2; - repeated sint32 Int32Sl = 3; - repeated sfixed32 Int32FixedSl = 4; - repeated sint64 Int64Sl = 5; - repeated sfixed64 Int64FixedSl = 6; - repeated sint64 IntSl = 7; - bytes ByteSl = 8; - bytes Uint8Sl = 9; - repeated uint32 Uint16Sl = 10; - repeated uint32 Uint32Sl = 11; - repeated fixed32 Uint32FixedSl = 12; - repeated uint64 Uint64Sl = 13; - repeated fixed64 Uint64FixedSl = 14; - repeated uint64 UintSl = 15; - repeated string StrSl = 16; - repeated bytes BytesSl = 17; - repeated google.protobuf.Timestamp TimeSl = 18; - repeated google.protobuf.Duration DurationSl = 19; - repeated EmptyStruct EmptySl = 20; + repeated sint32 int8_sl = 1 [json_name = "Int8Sl"]; + repeated sint32 int16_sl = 2 [json_name = "Int16Sl"]; + repeated sint32 int32_sl = 3 [json_name = "Int32Sl"]; + repeated sfixed32 int32_fixed_sl = 4 [json_name = "Int32FixedSl"]; + repeated sint64 int64_sl = 5 [json_name = "Int64Sl"]; + repeated sfixed64 int64_fixed_sl = 6 [json_name = "Int64FixedSl"]; + repeated sint64 int_sl = 7 [json_name = "IntSl"]; + bytes byte_sl = 8 [json_name = "ByteSl"]; + bytes uint8_sl = 9 [json_name = "Uint8Sl"]; + repeated uint32 uint16_sl = 10 [json_name = "Uint16Sl"]; + repeated uint32 uint32_sl = 11 [json_name = "Uint32Sl"]; + repeated fixed32 uint32_fixed_sl = 12 [json_name = "Uint32FixedSl"]; + repeated uint64 uint64_sl = 13 [json_name = "Uint64Sl"]; + repeated fixed64 uint64_fixed_sl = 14 [json_name = "Uint64FixedSl"]; + repeated uint64 uint_sl = 15 [json_name = "UintSl"]; + repeated string str_sl = 16 [json_name = "StrSl"]; + repeated bytes bytes_sl = 17 [json_name = "BytesSl"]; + repeated google.protobuf.Timestamp time_sl = 18 [json_name = "TimeSl"]; + repeated google.protobuf.Duration duration_sl = 19 [json_name = "DurationSl"]; + repeated EmptyStruct empty_sl = 20 [json_name = "EmptySl"]; } message SlicesSlicesStruct { - repeated TESTS_Int8List Int8SlSl = 1; - repeated TESTS_Int16List Int16SlSl = 2; - repeated TESTS_Int32ValueList Int32SlSl = 3; - repeated TESTS_Fixed32Int32ValueList Int32FixedSlSl = 4; - repeated TESTS_Int64ValueList Int64SlSl = 5; - repeated TESTS_Fixed64Int64ValueList Int64FixedSlSl = 6; - repeated TESTS_Int64ValueList IntSlSl = 7; - repeated bytes ByteSlSl = 8; - repeated bytes Uint8SlSl = 9; - repeated TESTS_UInt16List Uint16SlSl = 10; - repeated TESTS_UInt32ValueList Uint32SlSl = 11; - repeated TESTS_Fixed32UInt32ValueList Uint32FixedSlSl = 12; - repeated TESTS_UInt64ValueList Uint64SlSl = 13; - repeated TESTS_Fixed64UInt64ValueList Uint64FixedSlSl = 14; - repeated TESTS_UInt64ValueList UintSlSl = 15; - repeated TESTS_StringValueList StrSlSl = 16; - repeated TESTS_BytesList BytesSlSl = 17; - repeated TESTS_TimestampList TimeSlSl = 18; - repeated TESTS_DurationList DurationSlSl = 19; - repeated TESTS_EmptyStructList EmptySlSl = 20; + repeated TESTS_Int8List int8_sl_sl = 1 [json_name = "Int8SlSl"]; + repeated TESTS_Int16List int16_sl_sl = 2 [json_name = "Int16SlSl"]; + repeated TESTS_Int32ValueList int32_sl_sl = 3 [json_name = "Int32SlSl"]; + repeated TESTS_Fixed32Int32ValueList int32_fixed_sl_sl = 4 [json_name = "Int32FixedSlSl"]; + repeated TESTS_Int64ValueList int64_sl_sl = 5 [json_name = "Int64SlSl"]; + repeated TESTS_Fixed64Int64ValueList int64_fixed_sl_sl = 6 [json_name = "Int64FixedSlSl"]; + repeated TESTS_Int64ValueList int_sl_sl = 7 [json_name = "IntSlSl"]; + repeated bytes byte_sl_sl = 8 [json_name = "ByteSlSl"]; + repeated bytes uint8_sl_sl = 9 [json_name = "Uint8SlSl"]; + repeated TESTS_UInt16List uint16_sl_sl = 10 [json_name = "Uint16SlSl"]; + repeated TESTS_UInt32ValueList uint32_sl_sl = 11 [json_name = "Uint32SlSl"]; + repeated TESTS_Fixed32UInt32ValueList uint32_fixed_sl_sl = 12 [json_name = "Uint32FixedSlSl"]; + repeated TESTS_UInt64ValueList uint64_sl_sl = 13 [json_name = "Uint64SlSl"]; + repeated TESTS_Fixed64UInt64ValueList uint64_fixed_sl_sl = 14 [json_name = "Uint64FixedSlSl"]; + repeated TESTS_UInt64ValueList uint_sl_sl = 15 [json_name = "UintSlSl"]; + repeated TESTS_StringValueList str_sl_sl = 16 [json_name = "StrSlSl"]; + repeated TESTS_BytesList bytes_sl_sl = 17 [json_name = "BytesSlSl"]; + repeated TESTS_TimestampList time_sl_sl = 18 [json_name = "TimeSlSl"]; + repeated TESTS_DurationList duration_sl_sl = 19 [json_name = "DurationSlSl"]; + repeated TESTS_EmptyStructList empty_sl_sl = 20 [json_name = "EmptySlSl"]; } message PointersStruct { - sint32 Int8Pt = 1; - sint32 Int16Pt = 2; - sint32 Int32Pt = 3; - sfixed32 Int32FixedPt = 4; - sint64 Int64Pt = 5; - sfixed64 Int64FixedPt = 6; - sint64 IntPt = 7; - uint32 BytePt = 8; - uint32 Uint8Pt = 9; - uint32 Uint16Pt = 10; - uint32 Uint32Pt = 11; - fixed32 Uint32FixedPt = 12; - uint64 Uint64Pt = 13; - fixed64 Uint64FixedPt = 14; - uint64 UintPt = 15; - string StrPt = 16; - bytes BytesPt = 17; - google.protobuf.Timestamp TimePt = 18; - google.protobuf.Duration DurationPt = 19; - EmptyStruct EmptyPt = 20; + sint32 int8_pt = 1 [json_name = "Int8Pt"]; + sint32 int16_pt = 2 [json_name = "Int16Pt"]; + sint32 int32_pt = 3 [json_name = "Int32Pt"]; + sfixed32 int32_fixed_pt = 4 [json_name = "Int32FixedPt"]; + sint64 int64_pt = 5 [json_name = "Int64Pt"]; + sfixed64 int64_fixed_pt = 6 [json_name = "Int64FixedPt"]; + sint64 int_pt = 7 [json_name = "IntPt"]; + uint32 byte_pt = 8 [json_name = "BytePt"]; + uint32 uint8_pt = 9 [json_name = "Uint8Pt"]; + uint32 uint16_pt = 10 [json_name = "Uint16Pt"]; + uint32 uint32_pt = 11 [json_name = "Uint32Pt"]; + fixed32 uint32_fixed_pt = 12 [json_name = "Uint32FixedPt"]; + uint64 uint64_pt = 13 [json_name = "Uint64Pt"]; + fixed64 uint64_fixed_pt = 14 [json_name = "Uint64FixedPt"]; + uint64 uint_pt = 15 [json_name = "UintPt"]; + string str_pt = 16 [json_name = "StrPt"]; + bytes bytes_pt = 17 [json_name = "BytesPt"]; + google.protobuf.Timestamp time_pt = 18 [json_name = "TimePt"]; + google.protobuf.Duration duration_pt = 19 [json_name = "DurationPt"]; + EmptyStruct empty_pt = 20 [json_name = "EmptyPt"]; } message PointerSlicesStruct { - repeated sint32 Int8PtSl = 1; - repeated sint32 Int16PtSl = 2; - repeated sint32 Int32PtSl = 3; - repeated sfixed32 Int32FixedPtSl = 4; - repeated sint64 Int64PtSl = 5; - repeated sfixed64 Int64FixedPtSl = 6; - repeated sint64 IntPtSl = 7; - bytes BytePtSl = 8; - bytes Uint8PtSl = 9; - repeated uint32 Uint16PtSl = 10; - repeated uint32 Uint32PtSl = 11; - repeated fixed32 Uint32FixedPtSl = 12; - repeated uint64 Uint64PtSl = 13; - repeated fixed64 Uint64FixedPtSl = 14; - repeated uint64 UintPtSl = 15; - repeated string StrPtSl = 16; - repeated bytes BytesPtSl = 17; - repeated google.protobuf.Timestamp TimePtSl = 18; - repeated google.protobuf.Duration DurationPtSl = 19; - repeated EmptyStruct EmptyPtSl = 20; + repeated sint32 int8_pt_sl = 1 [json_name = "Int8PtSl"]; + repeated sint32 int16_pt_sl = 2 [json_name = "Int16PtSl"]; + repeated sint32 int32_pt_sl = 3 [json_name = "Int32PtSl"]; + repeated sfixed32 int32_fixed_pt_sl = 4 [json_name = "Int32FixedPtSl"]; + repeated sint64 int64_pt_sl = 5 [json_name = "Int64PtSl"]; + repeated sfixed64 int64_fixed_pt_sl = 6 [json_name = "Int64FixedPtSl"]; + repeated sint64 int_pt_sl = 7 [json_name = "IntPtSl"]; + bytes byte_pt_sl = 8 [json_name = "BytePtSl"]; + bytes uint8_pt_sl = 9 [json_name = "Uint8PtSl"]; + repeated uint32 uint16_pt_sl = 10 [json_name = "Uint16PtSl"]; + repeated uint32 uint32_pt_sl = 11 [json_name = "Uint32PtSl"]; + repeated fixed32 uint32_fixed_pt_sl = 12 [json_name = "Uint32FixedPtSl"]; + repeated uint64 uint64_pt_sl = 13 [json_name = "Uint64PtSl"]; + repeated fixed64 uint64_fixed_pt_sl = 14 [json_name = "Uint64FixedPtSl"]; + repeated uint64 uint_pt_sl = 15 [json_name = "UintPtSl"]; + repeated string str_pt_sl = 16 [json_name = "StrPtSl"]; + repeated bytes bytes_pt_sl = 17 [json_name = "BytesPtSl"]; + repeated google.protobuf.Timestamp time_pt_sl = 18 [json_name = "TimePtSl"]; + repeated google.protobuf.Duration duration_pt_sl = 19 [json_name = "DurationPtSl"]; + repeated EmptyStruct empty_pt_sl = 20 [json_name = "EmptyPtSl"]; } message ComplexSt { - PrimitivesStruct PrField = 1; - ArraysStruct ArField = 2; - SlicesStruct SlField = 3; - PointersStruct PtField = 4; + PrimitivesStruct pr_field = 1 [json_name = "PrField"]; + ArraysStruct ar_field = 2 [json_name = "ArField"]; + SlicesStruct sl_field = 3 [json_name = "SlField"]; + PointersStruct pt_field = 4 [json_name = "PtField"]; } message EmbeddedSt1 { - PrimitivesStruct PrimitivesStruct = 1; + PrimitivesStruct primitives_struct = 1 [json_name = "PrimitivesStruct"]; } message EmbeddedSt2 { - PrimitivesStruct PrimitivesStruct = 1; - ArraysStruct ArraysStruct = 2; - SlicesStruct SlicesStruct = 3; - PointersStruct PointersStruct = 4; + PrimitivesStruct primitives_struct = 1 [json_name = "PrimitivesStruct"]; + ArraysStruct arrays_struct = 2 [json_name = "ArraysStruct"]; + SlicesStruct slices_struct = 3 [json_name = "SlicesStruct"]; + PointersStruct pointers_struct = 4 [json_name = "PointersStruct"]; } message EmbeddedSt3 { - PrimitivesStruct PrimitivesStruct = 1; - ArraysStruct ArraysStruct = 2; - SlicesStruct SlicesStruct = 3; - PointersStruct PointersStruct = 4; - EmptyStruct EmptyStruct = 5; + PrimitivesStruct primitives_struct = 1 [json_name = "PrimitivesStruct"]; + ArraysStruct arrays_struct = 2 [json_name = "ArraysStruct"]; + SlicesStruct slices_struct = 3 [json_name = "SlicesStruct"]; + PointersStruct pointers_struct = 4 [json_name = "PointersStruct"]; + EmptyStruct empty_struct = 5 [json_name = "EmptyStruct"]; } message EmbeddedSt4 { - sint64 Foo1 = 1; - PrimitivesStruct PrimitivesStruct = 2; - string Foo2 = 3; - ArraysStruct ArraysStructField = 4; - bytes Foo3 = 5; - SlicesStruct SlicesStruct = 6; - bool Foo4 = 7; - PointersStruct PointersStructField = 8; - uint64 Foo5 = 9; + sint64 foo1 = 1 [json_name = "Foo1"]; + PrimitivesStruct primitives_struct = 2 [json_name = "PrimitivesStruct"]; + string foo2 = 3 [json_name = "Foo2"]; + ArraysStruct arrays_struct_field = 4 [json_name = "ArraysStructField"]; + bytes foo3 = 5 [json_name = "Foo3"]; + SlicesStruct slices_struct = 6 [json_name = "SlicesStruct"]; + bool foo4 = 7 [json_name = "Foo4"]; + PointersStruct pointers_struct_field = 8 [json_name = "PointersStructField"]; + uint64 foo5 = 9 [json_name = "Foo5"]; } message EmbeddedSt5NameOverride { - sint64 Foo1 = 1; - PrimitivesStruct PrimitivesStruct = 2; - string Foo2 = 3; - ArraysStruct ArraysStructField = 4; - bytes Foo3 = 5; - SlicesStruct SlicesStruct = 6; - bool Foo4 = 7; - PointersStruct PointersStructField = 8; - uint64 Foo5 = 9; + sint64 foo1 = 1 [json_name = "Foo1"]; + PrimitivesStruct primitives_struct = 2 [json_name = "PrimitivesStruct"]; + string foo2 = 3 [json_name = "Foo2"]; + ArraysStruct arrays_struct_field = 4 [json_name = "ArraysStructField"]; + bytes foo3 = 5 [json_name = "Foo3"]; + SlicesStruct slices_struct = 6 [json_name = "SlicesStruct"]; + bool foo4 = 7 [json_name = "Foo4"]; + PointersStruct pointers_struct_field = 8 [json_name = "PointersStructField"]; + uint64 foo5 = 9 [json_name = "Foo5"]; } message AminoMarshalerStruct1 { - sint64 C = 1; - sint64 D = 2; + sint64 c = 1 [json_name = "C"]; + sint64 d = 2 [json_name = "D"]; } message ReprStruct1 { - sint64 C = 1; - sint64 D = 2; + sint64 c = 1 [json_name = "C"]; + sint64 d = 2 [json_name = "D"]; } message AminoMarshalerStruct2 { - repeated ReprElem2 Value = 1; + repeated ReprElem2 value = 1; } message ReprElem2 { - string Key = 1; - google.protobuf.Any Value = 2; + string key = 1 [json_name = "Key"]; + google.protobuf.Any value = 2 [json_name = "Value"]; } message AminoMarshalerStruct3 { - sint32 Value = 1; + sint32 value = 1; } message AminoMarshalerInt4 { - sint32 A = 1; + sint32 a = 1 [json_name = "A"]; } message AminoMarshalerInt5 { - string Value = 1; + string value = 1; } message AminoMarshalerStruct6 { - repeated AminoMarshalerStruct1 Value = 1; + repeated AminoMarshalerStruct1 value = 1; } message AminoMarshalerStruct7 { - bytes Value = 1; + bytes value = 1; } message ReprElem7 { - uint32 Value = 1; + uint32 value = 1; } message IntDef { - sint64 Value = 1; + sint64 value = 1; } message IntAr { - repeated sint64 Value = 1; + repeated sint64 value = 1; } message IntSl { - repeated sint64 Value = 1; + repeated sint64 value = 1; } message ByteAr { - bytes Value = 1; + bytes value = 1; } message ByteSl { - bytes Value = 1; + bytes value = 1; } message PrimitivesStructDef { - sint32 Int8 = 1; - sint32 Int16 = 2; - sint32 Int32 = 3; - sfixed32 Int32Fixed = 4; - sint64 Int64 = 5; - sfixed64 Int64Fixed = 6; - sint64 Int = 7; - uint32 Byte = 8; - uint32 Uint8 = 9; - uint32 Uint16 = 10; - uint32 Uint32 = 11; - fixed32 Uint32Fixed = 12; - uint64 Uint64 = 13; - fixed64 Uint64Fixed = 14; - uint64 Uint = 15; - string Str = 16; - bytes Bytes = 17; - google.protobuf.Timestamp Time = 18; - google.protobuf.Duration Duration = 19; - EmptyStruct Empty = 20; + sint32 int8 = 1 [json_name = "Int8"]; + sint32 int16 = 2 [json_name = "Int16"]; + sint32 int32 = 3 [json_name = "Int32"]; + sfixed32 int32_fixed = 4 [json_name = "Int32Fixed"]; + sint64 int64 = 5 [json_name = "Int64"]; + sfixed64 int64_fixed = 6 [json_name = "Int64Fixed"]; + sint64 int = 7 [json_name = "Int"]; + uint32 byte = 8 [json_name = "Byte"]; + uint32 uint8 = 9 [json_name = "Uint8"]; + uint32 uint16 = 10 [json_name = "Uint16"]; + uint32 uint32 = 11 [json_name = "Uint32"]; + fixed32 uint32_fixed = 12 [json_name = "Uint32Fixed"]; + uint64 uint64 = 13 [json_name = "Uint64"]; + fixed64 uint64_fixed = 14 [json_name = "Uint64Fixed"]; + uint64 uint = 15 [json_name = "Uint"]; + string str = 16 [json_name = "Str"]; + bytes bytes = 17 [json_name = "Bytes"]; + google.protobuf.Timestamp time = 18 [json_name = "Time"]; + google.protobuf.Duration duration = 19 [json_name = "Duration"]; + EmptyStruct empty = 20 [json_name = "Empty"]; } message PrimitivesStructSl { - repeated PrimitivesStruct Value = 1; + repeated PrimitivesStruct value = 1; } message PrimitivesStructAr { - repeated PrimitivesStruct Value = 1; + repeated PrimitivesStruct value = 1; } message Concrete1 { @@ -329,18 +329,18 @@ message Concrete2 { } message ConcreteTypeDef { - bytes Value = 1; + bytes value = 1; } message ConcreteWrappedBytes { - bytes Value = 1; + bytes value = 1 [json_name = "Value"]; } message InterfaceFieldsStruct { - google.protobuf.Any F1 = 1; - google.protobuf.Any F2 = 2; - google.protobuf.Any F3 = 3; - google.protobuf.Any F4 = 4; + google.protobuf.Any f1 = 1 [json_name = "F1"]; + google.protobuf.Any f2 = 2 [json_name = "F2"]; + google.protobuf.Any f3 = 3 [json_name = "F3"]; + google.protobuf.Any f4 = 4 [json_name = "F4"]; } message TESTS_BytesList { diff --git a/tm2/pkg/amino/time2_test.go b/tm2/pkg/amino/time2_test.go index c40e2fc660e..b8d73f37d55 100644 --- a/tm2/pkg/amino/time2_test.go +++ b/tm2/pkg/amino/time2_test.go @@ -18,6 +18,8 @@ type testTime struct { } func TestDecodeSkippedFieldsInTime(t *testing.T) { + t.Parallel() + tm, err := time.Parse("2006-01-02 15:04:05 +0000 UTC", "1970-01-01 00:00:00 +0000 UTC") assert.NoError(t, err) @@ -62,6 +64,8 @@ func TestDecodeSkippedFieldsInTime(t *testing.T) { } func TestMinMaxTimeEncode(t *testing.T) { + t.Parallel() + tMin, err := time.Parse("2006-01-02 15:04:05 +0000 UTC", "0001-01-01 00:00:00 +0000 UTC") assert.NoError(t, err) tm := testTime{tMin} diff --git a/tm2/pkg/amino/wellknown_test.go b/tm2/pkg/amino/wellknown_test.go index 3b91ffc77a5..345466bd285 100644 --- a/tm2/pkg/amino/wellknown_test.go +++ b/tm2/pkg/amino/wellknown_test.go @@ -9,6 +9,8 @@ import ( ) func TestAnyWellKnownNative(t *testing.T) { + t.Parallel() + cdc := amino.NewCodec() s1 := tests.InterfaceFieldsStruct{ diff --git a/tm2/pkg/async/async_test.go b/tm2/pkg/async/async_test.go index f82e32312f0..255ce6b8d51 100644 --- a/tm2/pkg/async/async_test.go +++ b/tm2/pkg/async/async_test.go @@ -12,6 +12,8 @@ import ( ) func TestParallel(t *testing.T) { + t.Parallel() + // Create tasks. counter := new(int32) tasks := make([]Task, 100*1000) @@ -52,6 +54,8 @@ func TestParallel(t *testing.T) { } func TestParallelAbort(t *testing.T) { + t.Parallel() + flow1 := make(chan struct{}, 1) flow2 := make(chan struct{}, 1) flow3 := make(chan struct{}, 1) // Cap must be > 0 to prevent blocking. @@ -103,6 +107,8 @@ func TestParallelAbort(t *testing.T) { } func TestParallelRecover(t *testing.T) { + t.Parallel() + // Create tasks. tasks := []Task{ func(i int) (res interface{}, err error, abort bool) { @@ -155,7 +161,7 @@ func waitTimeout(t *testing.T, taskResultCh TaskResultCh, taskName string) { } else { assert.Fail(t, "TaskResultCh unexpectedly returned for %v", taskName) } - case <-time.After(1 * time.Second): // TODO use deterministic time? + case <-time.After(200 * time.Millisecond): // TODO use deterministic time? // Good! } } diff --git a/tm2/pkg/autofile/autofile_test.go b/tm2/pkg/autofile/autofile_test.go index d50bdca3ce0..d631e0ed265 100644 --- a/tm2/pkg/autofile/autofile_test.go +++ b/tm2/pkg/autofile/autofile_test.go @@ -1,7 +1,6 @@ package autofile import ( - "io/ioutil" "os" "syscall" "testing" @@ -14,7 +13,7 @@ import ( func TestSIGHUP(t *testing.T) { // First, create an AutoFile writing to a tempfile dir - file, err := ioutil.TempFile("", "sighup_test") + file, err := os.CreateTemp("", "sighup_test") require.NoError(t, err) err = file.Close() require.NoError(t, err) @@ -60,7 +59,7 @@ func TestSIGHUP(t *testing.T) { // // Manually modify file permissions, close, and reopen using autofile: // // We expect the file permissions to be changed back to the intended perms. // func TestOpenAutoFilePerms(t *testing.T) { -// file, err := ioutil.TempFile("", "permission_test") +// file, err := os.CreateTemp("", "permission_test") // require.NoError(t, err) // err = file.Close() // require.NoError(t, err) @@ -86,7 +85,7 @@ func TestSIGHUP(t *testing.T) { func TestAutoFileSize(t *testing.T) { // First, create an AutoFile writing to a tempfile dir - f, err := ioutil.TempFile("", "sighup_test") + f, err := os.CreateTemp("", "sighup_test") require.NoError(t, err) err = f.Close() require.NoError(t, err) diff --git a/tm2/pkg/autofile/group.go b/tm2/pkg/autofile/group.go index 3350e1e62c5..c3cc1b2fd39 100644 --- a/tm2/pkg/autofile/group.go +++ b/tm2/pkg/autofile/group.go @@ -125,7 +125,11 @@ func (g *Group) OnStart() error { // OnStop implements service.Service by stopping the goroutine described above. // NOTE: g.Head must be closed separately using Close. func (g *Group) OnStop() { - g.FlushAndSync() + if err := g.FlushAndSync(); err != nil { + g.Logger.Error( + fmt.Sprintf("unable to gracefully flush data, %s", err.Error()), + ) + } } // Wait blocks until all internal goroutines are finished. Supposed to be @@ -136,11 +140,20 @@ func (g *Group) Wait() { // Close closes the head file. The group must be stopped by this moment. func (g *Group) Close() { - g.FlushAndSync() + if err := g.FlushAndSync(); err != nil { + g.Logger.Error( + fmt.Sprintf("unable to gracefully flush data, %s", err.Error()), + ) + } g.mtx.Lock() - _ = g.Head.Close() - g.mtx.Unlock() + defer g.mtx.Unlock() + + if err := g.Head.Close(); err != nil { + g.Logger.Error( + fmt.Sprintf("unable to gracefully close group head, %s", err.Error()), + ) + } } // HeadSizeLimit returns the current head size limit. @@ -334,6 +347,8 @@ func (g *Group) ReadGroupInfo() GroupInfo { return g.readGroupInfo() } +var indexedFilePattern = regexp.MustCompile(`^.+\.([0-9]{3,})$`) + // Index includes the head. // CONTRACT: caller should have called g.mtx.Lock func (g *Group) readGroupInfo() GroupInfo { @@ -362,7 +377,6 @@ func (g *Group) readGroupInfo() GroupInfo { } else if strings.HasPrefix(fileInfo.Name(), headBase) { fileSize := fileInfo.Size() totalSize += fileSize - indexedFilePattern := regexp.MustCompile(`^.+\.([0-9]{3,})$`) submatch := indexedFilePattern.FindSubmatch([]byte(fileInfo.Name())) if len(submatch) != 0 { // Matches diff --git a/tm2/pkg/autofile/group_test.go b/tm2/pkg/autofile/group_test.go index 6face4630d1..1bf79894b93 100644 --- a/tm2/pkg/autofile/group_test.go +++ b/tm2/pkg/autofile/group_test.go @@ -47,6 +47,8 @@ func assertGroupInfo(t *testing.T, gInfo GroupInfo, minIndex, maxIndex int, tota } func TestCheckHeadSizeLimit(t *testing.T) { + t.Parallel() + g := createTestGroupWithHeadSizeLimit(t, 1000*1000) // At first, there are no files. @@ -93,6 +95,8 @@ func TestCheckHeadSizeLimit(t *testing.T) { } func TestRotateFile(t *testing.T) { + t.Parallel() + g := createTestGroupWithHeadSizeLimit(t, 0) g.WriteLine("Line 1") g.WriteLine("Line 2") @@ -123,6 +127,8 @@ func TestRotateFile(t *testing.T) { } func TestWrite(t *testing.T) { + t.Parallel() + g := createTestGroupWithHeadSizeLimit(t, 0) written := []byte("Medusa") @@ -144,6 +150,8 @@ func TestWrite(t *testing.T) { // test that Read reads the required amount of bytes from all the files in the // group and returns no error if n == size of the given slice. func TestGroupReaderRead(t *testing.T) { + t.Parallel() + g := createTestGroupWithHeadSizeLimit(t, 0) professor := []byte("Professor Monster") @@ -173,6 +181,8 @@ func TestGroupReaderRead(t *testing.T) { // test that Read returns an error if number of bytes read < size of // the given slice. Subsequent call should return 0, io.EOF. func TestGroupReaderRead2(t *testing.T) { + t.Parallel() + g := createTestGroupWithHeadSizeLimit(t, 0) professor := []byte("Professor Monster") @@ -204,6 +214,8 @@ func TestGroupReaderRead2(t *testing.T) { } func TestMinIndex(t *testing.T) { + t.Parallel() + g := createTestGroupWithHeadSizeLimit(t, 0) assert.Zero(t, g.MinIndex(), "MinIndex should be zero at the beginning") @@ -213,6 +225,8 @@ func TestMinIndex(t *testing.T) { } func TestMaxIndex(t *testing.T) { + t.Parallel() + g := createTestGroupWithHeadSizeLimit(t, 0) assert.Zero(t, g.MaxIndex(), "MaxIndex should be zero at the beginning") diff --git a/tm2/pkg/bech32/bech32_test.go b/tm2/pkg/bech32/bech32_test.go index 557507cef57..361a18116ab 100644 --- a/tm2/pkg/bech32/bech32_test.go +++ b/tm2/pkg/bech32/bech32_test.go @@ -12,6 +12,8 @@ import ( ) func TestEncodeAndDecode(t *testing.T) { + t.Parallel() + sum := sha256.Sum256([]byte("hello world\n")) bech, err := bech32.ConvertAndEncode("shasum", sum[:]) @@ -39,6 +41,8 @@ var ( ) func TestEncode(t *testing.T) { + t.Parallel() + bz, err := hex.DecodeString(pubkeyBytes) assert.NoError(t, err) @@ -49,6 +53,8 @@ func TestEncode(t *testing.T) { } func TestDecode(t *testing.T) { + t.Parallel() + hrp, b1, err := bech32.Decode(pubkeyBech32) assert.NoError(t, err) diff --git a/tm2/pkg/bft/abci/client/client.go b/tm2/pkg/bft/abci/client/client.go index b3d5a945839..f9f21f08fdb 100644 --- a/tm2/pkg/bft/abci/client/client.go +++ b/tm2/pkg/bft/abci/client/client.go @@ -7,11 +7,6 @@ import ( "github.com/gnolang/gno/tm2/pkg/service" ) -const ( - dialRetryIntervalSeconds = 3 - echoRetryIntervalSeconds = 1 -) - // Client defines an interface for an ABCI client. // All `Async` methods return a `ReqRes` object. // All `Sync` methods return the appropriate protobuf ResponseXxx struct and an error. @@ -48,11 +43,11 @@ type Client interface { EndBlockSync(abci.RequestEndBlock) (abci.ResponseEndBlock, error) } -//---------------------------------------- +// ---------------------------------------- type Callback func(abci.Request, abci.Response) -//---------------------------------------- +// ---------------------------------------- type ReqRes struct { abci.Request diff --git a/tm2/pkg/bft/abci/example/kvstore/persistent_kvstore.go b/tm2/pkg/bft/abci/example/kvstore/persistent_kvstore.go index 2e0adb5656f..5042fcf9313 100644 --- a/tm2/pkg/bft/abci/example/kvstore/persistent_kvstore.go +++ b/tm2/pkg/bft/abci/example/kvstore/persistent_kvstore.go @@ -6,6 +6,8 @@ import ( "strconv" "strings" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/amino" "github.com/gnolang/gno/tm2/pkg/bft/abci/example/errors" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" @@ -31,7 +33,7 @@ type PersistentKVStoreApplication struct { // validator set ValSetChanges []abci.ValidatorUpdate - logger log.Logger + logger *slog.Logger } func NewPersistentKVStoreApplication(dbDir string) *PersistentKVStoreApplication { @@ -45,11 +47,11 @@ func NewPersistentKVStoreApplication(dbDir string) *PersistentKVStoreApplication return &PersistentKVStoreApplication{ app: &KVStoreApplication{state: state}, - logger: log.NewNopLogger(), + logger: log.NewNoopLogger(), } } -func (app *PersistentKVStoreApplication) SetLogger(l log.Logger) { +func (app *PersistentKVStoreApplication) SetLogger(l *slog.Logger) { app.logger = l } diff --git a/tm2/pkg/bft/abci/types/abci.proto b/tm2/pkg/bft/abci/types/abci.proto index 2494c94b702..99e7a584c2e 100644 --- a/tm2/pkg/bft/abci/types/abci.proto +++ b/tm2/pkg/bft/abci/types/abci.proto @@ -13,189 +13,189 @@ message RequestBase { } message RequestEcho { - RequestBase RequestBase = 1; - string Message = 2; + RequestBase request_base = 1 [json_name = "RequestBase"]; + string message = 2 [json_name = "Message"]; } message RequestFlush { - RequestBase RequestBase = 1; + RequestBase request_base = 1 [json_name = "RequestBase"]; } message RequestInfo { - RequestBase RequestBase = 1; + RequestBase request_base = 1 [json_name = "RequestBase"]; } message RequestSetOption { - RequestBase RequestBase = 1; - string Key = 2; - string Value = 3; + RequestBase request_base = 1 [json_name = "RequestBase"]; + string key = 2 [json_name = "Key"]; + string value = 3 [json_name = "Value"]; } message RequestInitChain { - RequestBase RequestBase = 1; - google.protobuf.Timestamp Time = 2; - string ChainID = 3; - ConsensusParams ConsensusParams = 4; - repeated ValidatorUpdate Validators = 5; - google.protobuf.Any AppState = 6; + RequestBase request_base = 1 [json_name = "RequestBase"]; + google.protobuf.Timestamp time = 2 [json_name = "Time"]; + string chain_id = 3 [json_name = "ChainID"]; + ConsensusParams consensus_params = 4 [json_name = "ConsensusParams"]; + repeated ValidatorUpdate validators = 5 [json_name = "Validators"]; + google.protobuf.Any app_state = 6 [json_name = "AppState"]; } message RequestQuery { - RequestBase RequestBase = 1; - bytes Data = 2; - string Path = 3; - sint64 Height = 4; - bool Prove = 5; + RequestBase request_base = 1 [json_name = "RequestBase"]; + bytes data = 2 [json_name = "Data"]; + string path = 3 [json_name = "Path"]; + sint64 height = 4 [json_name = "Height"]; + bool prove = 5 [json_name = "Prove"]; } message RequestBeginBlock { - RequestBase RequestBase = 1; - bytes Hash = 2; - google.protobuf.Any Header = 3; - LastCommitInfo LastCommitInfo = 4; + RequestBase request_base = 1 [json_name = "RequestBase"]; + bytes hash = 2 [json_name = "Hash"]; + google.protobuf.Any header = 3 [json_name = "Header"]; + LastCommitInfo last_commit_info = 4 [json_name = "LastCommitInfo"]; } message RequestCheckTx { - RequestBase RequestBase = 1; - bytes Tx = 2; - sint64 Type = 3; + RequestBase request_base = 1 [json_name = "RequestBase"]; + bytes tx = 2 [json_name = "Tx"]; + sint64 type = 3 [json_name = "Type"]; } message RequestDeliverTx { - RequestBase RequestBase = 1; - bytes Tx = 2; + RequestBase request_base = 1 [json_name = "RequestBase"]; + bytes tx = 2 [json_name = "Tx"]; } message RequestEndBlock { - RequestBase RequestBase = 1; - sint64 Height = 2; + RequestBase request_base = 1 [json_name = "RequestBase"]; + sint64 height = 2 [json_name = "Height"]; } message RequestCommit { - RequestBase RequestBase = 1; + RequestBase request_base = 1 [json_name = "RequestBase"]; } message ResponseBase { - google.protobuf.Any Error = 1; - bytes Data = 2; - repeated google.protobuf.Any Events = 3; - string Log = 4; - string Info = 5; + google.protobuf.Any error = 1 [json_name = "Error"]; + bytes data = 2 [json_name = "Data"]; + repeated google.protobuf.Any events = 3 [json_name = "Events"]; + string log = 4 [json_name = "Log"]; + string info = 5 [json_name = "Info"]; } message ResponseException { - ResponseBase ResponseBase = 1; + ResponseBase response_base = 1 [json_name = "ResponseBase"]; } message ResponseEcho { - ResponseBase ResponseBase = 1; - string Message = 2; + ResponseBase response_base = 1 [json_name = "ResponseBase"]; + string message = 2 [json_name = "Message"]; } message ResponseFlush { - ResponseBase ResponseBase = 1; + ResponseBase response_base = 1 [json_name = "ResponseBase"]; } message ResponseInfo { - ResponseBase ResponseBase = 1; - string ABCIVersion = 2; - string AppVersion = 3; - sint64 LastBlockHeight = 4; - bytes LastBlockAppHash = 5; + ResponseBase response_base = 1 [json_name = "ResponseBase"]; + string abci_version = 2 [json_name = "ABCIVersion"]; + string app_version = 3 [json_name = "AppVersion"]; + sint64 last_block_height = 4 [json_name = "LastBlockHeight"]; + bytes last_block_app_hash = 5 [json_name = "LastBlockAppHash"]; } message ResponseSetOption { - ResponseBase ResponseBase = 1; + ResponseBase response_base = 1 [json_name = "ResponseBase"]; } message ResponseInitChain { - ResponseBase ResponseBase = 1; - ConsensusParams ConsensusParams = 2; - repeated ValidatorUpdate Validators = 3; + ResponseBase response_base = 1 [json_name = "ResponseBase"]; + ConsensusParams consensus_params = 2 [json_name = "ConsensusParams"]; + repeated ValidatorUpdate validators = 3 [json_name = "Validators"]; } message ResponseQuery { - ResponseBase ResponseBase = 1; - bytes Key = 2; - bytes Value = 3; - tm.Proof Proof = 4; - sint64 Height = 5; + ResponseBase response_base = 1 [json_name = "ResponseBase"]; + bytes key = 2 [json_name = "Key"]; + bytes value = 3 [json_name = "Value"]; + tm.Proof proof = 4 [json_name = "Proof"]; + sint64 height = 5 [json_name = "Height"]; } message ResponseBeginBlock { - ResponseBase ResponseBase = 1; + ResponseBase response_base = 1 [json_name = "ResponseBase"]; } message ResponseCheckTx { - ResponseBase ResponseBase = 1; - sint64 GasWanted = 2; - sint64 GasUsed = 3; + ResponseBase response_base = 1 [json_name = "ResponseBase"]; + sint64 gas_wanted = 2 [json_name = "GasWanted"]; + sint64 gas_used = 3 [json_name = "GasUsed"]; } message ResponseDeliverTx { - ResponseBase ResponseBase = 1; - sint64 GasWanted = 2; - sint64 GasUsed = 3; + ResponseBase response_base = 1 [json_name = "ResponseBase"]; + sint64 gas_wanted = 2 [json_name = "GasWanted"]; + sint64 gas_used = 3 [json_name = "GasUsed"]; } message ResponseEndBlock { - ResponseBase ResponseBase = 1; - repeated ValidatorUpdate ValidatorUpdates = 2; - ConsensusParams ConsensusParams = 3; - repeated google.protobuf.Any Events = 4; + ResponseBase response_base = 1 [json_name = "ResponseBase"]; + repeated ValidatorUpdate validator_updates = 2 [json_name = "ValidatorUpdates"]; + ConsensusParams consensus_params = 3 [json_name = "ConsensusParams"]; + repeated google.protobuf.Any events = 4 [json_name = "Events"]; } message ResponseCommit { - ResponseBase ResponseBase = 1; + ResponseBase response_base = 1 [json_name = "ResponseBase"]; } message StringError { - string Value = 1; + string value = 1; } message ConsensusParams { - BlockParams Block = 1; - ValidatorParams Validator = 2; + BlockParams block = 1 [json_name = "Block"]; + ValidatorParams validator = 2 [json_name = "Validator"]; } message BlockParams { - sint64 MaxTxBytes = 1; - sint64 MaxDataBytes = 2; - sint64 MaxBlockBytes = 3; - sint64 MaxGas = 4; - sint64 TimeIotaMS = 5; + sint64 max_tx_bytes = 1 [json_name = "MaxTxBytes"]; + sint64 max_data_bytes = 2 [json_name = "MaxDataBytes"]; + sint64 max_block_bytes = 3 [json_name = "MaxBlockBytes"]; + sint64 max_gas = 4 [json_name = "MaxGas"]; + sint64 time_iota_ms = 5 [json_name = "TimeIotaMS"]; } message ValidatorParams { - repeated string PubKeyTypeURLs = 1; + repeated string pub_key_type_ur_ls = 1 [json_name = "PubKeyTypeURLs"]; } message ValidatorUpdate { - string Address = 1; - google.protobuf.Any PubKey = 2; - sint64 Power = 3; + string address = 1 [json_name = "Address"]; + google.protobuf.Any pub_key = 2 [json_name = "PubKey"]; + sint64 power = 3 [json_name = "Power"]; } message LastCommitInfo { - sint32 Round = 1; - repeated VoteInfo Votes = 2; + sint32 round = 1 [json_name = "Round"]; + repeated VoteInfo votes = 2 [json_name = "Votes"]; } message VoteInfo { - string Address = 1; - sint64 Power = 2; - bool SignedLastBlock = 3; + string address = 1 [json_name = "Address"]; + sint64 power = 2 [json_name = "Power"]; + bool signed_last_block = 3 [json_name = "SignedLastBlock"]; } message EventString { - string Value = 1; + string value = 1; } message MockHeader { - string Version = 1; - string ChainID = 2; - sint64 Height = 3; - google.protobuf.Timestamp Time = 4; - sint64 NumTxs = 5; - sint64 TotalTxs = 6; + string version = 1; + string chain_id = 2; + sint64 height = 3; + google.protobuf.Timestamp time = 4; + sint64 num_txs = 5; + sint64 total_txs = 6; } \ No newline at end of file diff --git a/tm2/pkg/bft/blockchain/blockchain.proto b/tm2/pkg/bft/blockchain/blockchain.proto index a618d5abf49..d31d9e7074e 100644 --- a/tm2/pkg/bft/blockchain/blockchain.proto +++ b/tm2/pkg/bft/blockchain/blockchain.proto @@ -11,21 +11,21 @@ import "github.com/gnolang/gno/tm2/pkg/bitarray/bitarray.proto"; // messages message BlockRequest { - sint64 Height = 1; + sint64 height = 1 [json_name = "Height"]; } message BlockResponse { - Block Block = 1; + Block block = 1 [json_name = "Block"]; } message NoBlockResponse { - sint64 Height = 1; + sint64 height = 1 [json_name = "Height"]; } message StatusRequest { - sint64 Height = 1; + sint64 height = 1 [json_name = "Height"]; } message StatusResponse { - sint64 Height = 1; + sint64 height = 1 [json_name = "Height"]; } \ No newline at end of file diff --git a/tm2/pkg/bft/blockchain/pool.go b/tm2/pkg/bft/blockchain/pool.go index a44926ec7b2..a3a24d265a8 100644 --- a/tm2/pkg/bft/blockchain/pool.go +++ b/tm2/pkg/bft/blockchain/pool.go @@ -8,6 +8,8 @@ import ( "sync/atomic" "time" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/flow" "github.com/gnolang/gno/tm2/pkg/log" @@ -419,7 +421,7 @@ func (pool *BlockPool) debug() string { return str } -//------------------------------------- +// ------------------------------------- type bpPeer struct { pool *BlockPool @@ -431,7 +433,7 @@ type bpPeer struct { timeout *time.Timer didTimeout bool - logger log.Logger + logger *slog.Logger } func newBPPeer(pool *BlockPool, peerID p2p.ID, height int64) *bpPeer { @@ -440,12 +442,12 @@ func newBPPeer(pool *BlockPool, peerID p2p.ID, height int64) *bpPeer { id: peerID, height: height, numPending: 0, - logger: log.NewNopLogger(), + logger: log.NewNoopLogger(), } return peer } -func (peer *bpPeer) setLogger(l log.Logger) { +func (peer *bpPeer) setLogger(l *slog.Logger) { peer.logger = l } @@ -491,7 +493,7 @@ func (peer *bpPeer) onTimeout() { peer.didTimeout = true } -//------------------------------------- +// ------------------------------------- type bpRequester struct { service.BaseService diff --git a/tm2/pkg/bft/blockchain/pool_test.go b/tm2/pkg/bft/blockchain/pool_test.go index 79205bff5ce..a4d5636d5e3 100644 --- a/tm2/pkg/bft/blockchain/pool_test.go +++ b/tm2/pkg/bft/blockchain/pool_test.go @@ -72,12 +72,14 @@ func makePeers(numPeers int, minHeight, maxHeight int64) testPeers { } func TestBlockPoolBasic(t *testing.T) { + t.Parallel() + start := int64(42) peers := makePeers(10, start+1, 1000) errorsCh := make(chan peerError, 1000) requestsCh := make(chan BlockRequest, 1000) pool := NewBlockPool(start, requestsCh, errorsCh) - pool.SetLogger(log.TestingLogger()) + pool.SetLogger(log.NewNoopLogger()) err := pool.Start() if err != nil { @@ -128,12 +130,14 @@ func TestBlockPoolBasic(t *testing.T) { } func TestBlockPoolTimeout(t *testing.T) { + t.Parallel() + start := int64(42) peers := makePeers(10, start+1, 1000) errorsCh := make(chan peerError, 1000) requestsCh := make(chan BlockRequest, 1000) pool := NewBlockPool(start, requestsCh, errorsCh) - pool.SetLogger(log.TestingLogger()) + pool.SetLogger(log.NewTestingLogger(t)) err := pool.Start() if err != nil { t.Error(err) @@ -187,6 +191,8 @@ func TestBlockPoolTimeout(t *testing.T) { } func TestBlockPoolRemovePeer(t *testing.T) { + t.Parallel() + peers := make(testPeers, 10) for i := 0; i < 10; i++ { peerID := p2p.ID(fmt.Sprintf("%d", i+1)) @@ -197,7 +203,7 @@ func TestBlockPoolRemovePeer(t *testing.T) { errorsCh := make(chan peerError) pool := NewBlockPool(1, requestsCh, errorsCh) - pool.SetLogger(log.TestingLogger()) + pool.SetLogger(log.NewTestingLogger(t)) err := pool.Start() require.NoError(t, err) defer pool.Stop() diff --git a/tm2/pkg/bft/blockchain/reactor.go b/tm2/pkg/bft/blockchain/reactor.go index 007f7ccfb1c..bf5f7ea71b3 100644 --- a/tm2/pkg/bft/blockchain/reactor.go +++ b/tm2/pkg/bft/blockchain/reactor.go @@ -6,11 +6,12 @@ import ( "reflect" "time" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/amino" sm "github.com/gnolang/gno/tm2/pkg/bft/state" "github.com/gnolang/gno/tm2/pkg/bft/store" "github.com/gnolang/gno/tm2/pkg/bft/types" - "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/p2p" ) @@ -102,7 +103,7 @@ func NewBlockchainReactor(state sm.State, blockExec *sm.BlockExecutor, store *st } // SetLogger implements cmn.Service by setting the logger on reactor and pool. -func (bcR *BlockchainReactor) SetLogger(l log.Logger) { +func (bcR *BlockchainReactor) SetLogger(l *slog.Logger) { bcR.BaseService.Logger = l bcR.pool.Logger = l } diff --git a/tm2/pkg/bft/blockchain/reactor_test.go b/tm2/pkg/bft/blockchain/reactor_test.go index f4265e0f78d..982c29033fa 100644 --- a/tm2/pkg/bft/blockchain/reactor_test.go +++ b/tm2/pkg/bft/blockchain/reactor_test.go @@ -6,6 +6,8 @@ import ( "testing" "time" + "golang.org/x/exp/slog" + "github.com/stretchr/testify/assert" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" @@ -50,7 +52,7 @@ type BlockchainReactorPair struct { app proxy.AppConns } -func newBlockchainReactor(logger log.Logger, genDoc *types.GenesisDoc, privVals []types.PrivValidator, maxBlockHeight int64) BlockchainReactorPair { +func newBlockchainReactor(logger *slog.Logger, genDoc *types.GenesisDoc, privVals []types.PrivValidator, maxBlockHeight int64) BlockchainReactorPair { if len(privVals) != 1 { panic("only support one validator") } @@ -77,7 +79,7 @@ func newBlockchainReactor(logger log.Logger, genDoc *types.GenesisDoc, privVals // pool.height is determined from the store. fastSync := true db := dbm.NewMemDB() - blockExec := sm.NewBlockExecutor(db, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}) + blockExec := sm.NewBlockExecutor(db, logger, proxyApp.Consensus(), mock.Mempool{}) sm.SaveState(db, state) // let's add some blocks in @@ -115,6 +117,8 @@ func newBlockchainReactor(logger log.Logger, genDoc *types.GenesisDoc, privVals } func TestNoBlockResponse(t *testing.T) { + t.Parallel() + config = cfg.ResetTestRoot("blockchain_reactor_test") defer os.RemoveAll(config.RootDir) genDoc, privVals := randGenesisDoc(1, false, 30) @@ -123,8 +127,8 @@ func TestNoBlockResponse(t *testing.T) { reactorPairs := make([]BlockchainReactorPair, 2) - reactorPairs[0] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight) - reactorPairs[1] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) + reactorPairs[0] = newBlockchainReactor(log.NewTestingLogger(t), genDoc, privVals, maxBlockHeight) + reactorPairs[1] = newBlockchainReactor(log.NewTestingLogger(t), genDoc, privVals, 0) p2p.MakeConnectedSwitches(config.P2P, 2, func(i int, s *p2p.Switch) *p2p.Switch { s.AddReactor("BLOCKCHAIN", reactorPairs[i].reactor) @@ -174,6 +178,8 @@ func TestNoBlockResponse(t *testing.T) { // Alternatively we could actually dial a TCP conn but // that seems extreme. func TestFlappyBadBlockStopsPeer(t *testing.T) { + t.Parallel() + testutils.FilterStability(t, testutils.Flappy) config = cfg.ResetTestRoot("blockchain_reactor_test") @@ -182,7 +188,7 @@ func TestFlappyBadBlockStopsPeer(t *testing.T) { maxBlockHeight := int64(148) - otherChain := newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight) + otherChain := newBlockchainReactor(log.NewNoopLogger(), genDoc, privVals, maxBlockHeight) defer func() { otherChain.reactor.Stop() otherChain.app.Stop() @@ -190,10 +196,10 @@ func TestFlappyBadBlockStopsPeer(t *testing.T) { reactorPairs := make([]BlockchainReactorPair, 4) - reactorPairs[0] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight) - reactorPairs[1] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) - reactorPairs[2] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) - reactorPairs[3] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) + reactorPairs[0] = newBlockchainReactor(log.NewNoopLogger(), genDoc, privVals, maxBlockHeight) + reactorPairs[1] = newBlockchainReactor(log.NewNoopLogger(), genDoc, privVals, 0) + reactorPairs[2] = newBlockchainReactor(log.NewNoopLogger(), genDoc, privVals, 0) + reactorPairs[3] = newBlockchainReactor(log.NewNoopLogger(), genDoc, privVals, 0) switches := p2p.MakeConnectedSwitches(config.P2P, 4, func(i int, s *p2p.Switch) *p2p.Switch { s.AddReactor("BLOCKCHAIN", reactorPairs[i].reactor) @@ -221,7 +227,7 @@ func TestFlappyBadBlockStopsPeer(t *testing.T) { // mark reactorPairs[3] is an invalid peer reactorPairs[3].reactor.store = otherChain.reactor.store - lastReactorPair := newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0) + lastReactorPair := newBlockchainReactor(log.NewNoopLogger(), genDoc, privVals, 0) reactorPairs = append(reactorPairs, lastReactorPair) switches = append(switches, p2p.MakeConnectedSwitches(config.P2P, 1, func(i int, s *p2p.Switch) *p2p.Switch { @@ -245,6 +251,8 @@ func TestFlappyBadBlockStopsPeer(t *testing.T) { } func TestBcBlockRequestMessageValidateBasic(t *testing.T) { + t.Parallel() + testCases := []struct { testName string requestHeight int64 @@ -258,6 +266,8 @@ func TestBcBlockRequestMessageValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + request := bcBlockRequestMessage{Height: tc.requestHeight} assert.Equal(t, tc.expectErr, request.ValidateBasic() != nil, "Validate Basic had an unexpected result") }) @@ -265,6 +275,8 @@ func TestBcBlockRequestMessageValidateBasic(t *testing.T) { } func TestBcNoBlockResponseMessageValidateBasic(t *testing.T) { + t.Parallel() + testCases := []struct { testName string nonResponseHeight int64 @@ -278,6 +290,8 @@ func TestBcNoBlockResponseMessageValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + nonResponse := bcNoBlockResponseMessage{Height: tc.nonResponseHeight} assert.Equal(t, tc.expectErr, nonResponse.ValidateBasic() != nil, "Validate Basic had an unexpected result") }) @@ -285,6 +299,8 @@ func TestBcNoBlockResponseMessageValidateBasic(t *testing.T) { } func TestBcStatusRequestMessageValidateBasic(t *testing.T) { + t.Parallel() + testCases := []struct { testName string requestHeight int64 @@ -298,6 +314,8 @@ func TestBcStatusRequestMessageValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + request := bcStatusRequestMessage{Height: tc.requestHeight} assert.Equal(t, tc.expectErr, request.ValidateBasic() != nil, "Validate Basic had an unexpected result") }) @@ -305,6 +323,8 @@ func TestBcStatusRequestMessageValidateBasic(t *testing.T) { } func TestBcStatusResponseMessageValidateBasic(t *testing.T) { + t.Parallel() + testCases := []struct { testName string responseHeight int64 @@ -318,13 +338,15 @@ func TestBcStatusResponseMessageValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + response := bcStatusResponseMessage{Height: tc.responseHeight} assert.Equal(t, tc.expectErr, response.ValidateBasic() != nil, "Validate Basic had an unexpected result") }) } } -//---------------------------------------------- +// ---------------------------------------------- // utility funcs func makeTxs(height int64) (txs []types.Tx) { diff --git a/tm2/pkg/bft/config/config.go b/tm2/pkg/bft/config/config.go index 6f148c3b5c1..3cf6afc147b 100644 --- a/tm2/pkg/bft/config/config.go +++ b/tm2/pkg/bft/config/config.go @@ -4,75 +4,146 @@ import ( "fmt" "os" "path/filepath" + "regexp" + "dario.cat/mergo" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" cns "github.com/gnolang/gno/tm2/pkg/bft/consensus/config" mem "github.com/gnolang/gno/tm2/pkg/bft/mempool/config" rpc "github.com/gnolang/gno/tm2/pkg/bft/rpc/config" + eventstore "github.com/gnolang/gno/tm2/pkg/bft/state/eventstore/types" "github.com/gnolang/gno/tm2/pkg/errors" osm "github.com/gnolang/gno/tm2/pkg/os" p2p "github.com/gnolang/gno/tm2/pkg/p2p/config" ) +var ( + errInvalidMoniker = errors.New("moniker not set") + errInvalidDBBackend = errors.New("invalid DB backend") + errInvalidDBPath = errors.New("invalid DB path") + errInvalidGenesisPath = errors.New("invalid genesis path") + errInvalidPrivValidatorKeyPath = errors.New("invalid private validator key path") + errInvalidPrivValidatorStatePath = errors.New("invalid private validator state file path") + errInvalidABCIMechanism = errors.New("invalid ABCI mechanism") + errInvalidPrivValidatorListenAddress = errors.New("invalid PrivValidator listen address") + errInvalidProfListenAddress = errors.New("invalid profiling server listen address") + errInvalidNodeKeyPath = errors.New("invalid p2p node key path") +) + +const ( + levelDBName = "goleveldb" + clevelDBName = "cleveldb" + boltDBName = "boltdb" +) + +const ( + localABCI = "local" + socketABCI = "socket" +) + +// Regular expression for TCP or UNIX socket address +// TCP address: host:port (IPv4 example) +// UNIX address: unix:// followed by the path +var tcpUnixAddressRegex = regexp.MustCompile(`^(?:[0-9]{1,3}(\.[0-9]{1,3}){3}:[0-9]+|unix://.+)`) + // Config defines the top level configuration for a Tendermint node type Config struct { // Top level options use an anonymous struct BaseConfig `toml:",squash"` // Options for services - RPC *rpc.RPCConfig `toml:"rpc"` - P2P *p2p.P2PConfig `toml:"p2p"` - Mempool *mem.MempoolConfig `toml:"mempool"` - Consensus *cns.ConsensusConfig `toml:"consensus"` + RPC *rpc.RPCConfig `toml:"rpc" comment:"##### rpc server configuration options #####"` + P2P *p2p.P2PConfig `toml:"p2p" comment:"##### peer to peer configuration options #####"` + Mempool *mem.MempoolConfig `toml:"mempool" comment:"##### mempool configuration options #####"` + Consensus *cns.ConsensusConfig `toml:"consensus" comment:"##### consensus configuration options #####"` + TxEventStore *eventstore.Config `toml:"tx_event_store" comment:"##### event store #####"` } // DefaultConfig returns a default configuration for a Tendermint node func DefaultConfig() *Config { return &Config{ - BaseConfig: DefaultBaseConfig(), - RPC: rpc.DefaultRPCConfig(), - P2P: p2p.DefaultP2PConfig(), - Mempool: mem.DefaultMempoolConfig(), - Consensus: cns.DefaultConsensusConfig(), + BaseConfig: DefaultBaseConfig(), + RPC: rpc.DefaultRPCConfig(), + P2P: p2p.DefaultP2PConfig(), + Mempool: mem.DefaultMempoolConfig(), + Consensus: cns.DefaultConsensusConfig(), + TxEventStore: eventstore.DefaultEventStoreConfig(), } } -// Like LoadOrMakeConfigWithOptions() but without overriding any defaults. -func LoadOrMakeDefaultConfig(root string) (cfg *Config) { - return LoadOrMakeConfigWithOptions(root, nil) -} - -type ConfigOptions func(cfg *Config) +type Option func(cfg *Config) + +// LoadOrMakeConfigWithOptions loads the configuration located in the given +// root directory, at [defaultConfigFilePath]. +// +// If the config does not exist, it is created, starting from the values in +// `DefaultConfig` and applying the defaults in opts. +func LoadOrMakeConfigWithOptions(root string, opts ...Option) (*Config, error) { + // Initialize the config as default + var ( + cfg = DefaultConfig() + configPath = join(root, defaultConfigFilePath) + ) + + // Config doesn't exist, create it + // from the default one + for _, opt := range opts { + opt(cfg) + } -// LoadOrMakeConfigWithOptions() loads configuration or saves one -// made by modifying the default config with override options -func LoadOrMakeConfigWithOptions(root string, options ConfigOptions) (cfg *Config) { - configPath := join(root, defaultConfigFilePath) + // Check if the config exists if osm.FileExists(configPath) { - cfg = LoadConfigFile(configPath) - cfg.SetRootDir(root) - cfg.EnsureDirs() - } else { - cfg = DefaultConfig() - options(cfg) - cfg.SetRootDir(root) - cfg.EnsureDirs() - WriteConfigFile(configPath, cfg) + // Load the configuration + loadedCfg, loadErr := LoadConfigFile(configPath) + if loadErr != nil { + return nil, loadErr + } + + // Merge the loaded config with the default values + if err := mergo.Merge(loadedCfg, cfg); err != nil { + return nil, err + } + + // Set the root directory + loadedCfg.SetRootDir(root) + + // Make sure the directories are initialized + if err := loadedCfg.EnsureDirs(); err != nil { + return nil, err + } + + return loadedCfg, nil } - if err := cfg.ValidateBasic(); err != nil { - panic(err) + + cfg.SetRootDir(root) + + // Make sure the directories are initialized + if err := cfg.EnsureDirs(); err != nil { + return nil, err } - return cfg + + // Validate the configuration + if validateErr := cfg.ValidateBasic(); validateErr != nil { + return nil, fmt.Errorf("unable to validate config, %w", validateErr) + } + + // Save the config + if err := WriteConfigFile(configPath, cfg); err != nil { + return nil, err + } + + return cfg, nil } // TestConfig returns a configuration that can be used for testing func TestConfig() *Config { return &Config{ - BaseConfig: TestBaseConfig(), - RPC: rpc.TestRPCConfig(), - P2P: p2p.TestP2PConfig(), - Mempool: mem.TestMempoolConfig(), - Consensus: cns.TestConsensusConfig(), + BaseConfig: testBaseConfig(), + RPC: rpc.TestRPCConfig(), + P2P: p2p.TestP2PConfig(), + Mempool: mem.TestMempoolConfig(), + Consensus: cns.TestConsensusConfig(), + TxEventStore: eventstore.DefaultEventStoreConfig(), } } @@ -83,21 +154,27 @@ func (cfg *Config) SetRootDir(root string) *Config { cfg.P2P.RootDir = root cfg.Mempool.RootDir = root cfg.Consensus.RootDir = root + return cfg } // EnsureDirs ensures default directories in root dir (and root dir). -func (cfg *Config) EnsureDirs() { +func (cfg *Config) EnsureDirs() error { rootDir := cfg.BaseConfig.RootDir + if err := osm.EnsureDir(rootDir, DefaultDirPerm); err != nil { - panic(err.Error()) + return fmt.Errorf("no root directory, %w", err) } + if err := osm.EnsureDir(filepath.Join(rootDir, defaultConfigDir), DefaultDirPerm); err != nil { - panic(err.Error()) + return fmt.Errorf("no config directory, %w", err) } + if err := osm.EnsureDir(filepath.Join(rootDir, defaultDataDir), DefaultDirPerm); err != nil { - panic(err.Error()) + return fmt.Errorf("no data directory, %w", err) } + + return nil } // ValidateBasic performs basic validation (checking param bounds, etc.) and @@ -121,16 +198,9 @@ func (cfg *Config) ValidateBasic() error { return nil } -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // BaseConfig -const ( - // LogFormatPlain is a format for colored text - LogFormatPlain = "plain" - // LogFormatJSON is a format for json output - LogFormatJSON = "json" -) - var ( defaultConfigDir = "config" defaultDataDir = "data" @@ -160,18 +230,18 @@ type BaseConfig struct { // TCP or UNIX socket address of the ABCI application, // or the name of an ABCI application compiled in with the Tendermint binary, // or empty if local application instance. - ProxyApp string `toml:"proxy_app"` + ProxyApp string `toml:"proxy_app" comment:"TCP or UNIX socket address of the ABCI application, \n or the name of an ABCI application compiled in with the Tendermint binary"` // Local application instance in lieu of remote app. LocalApp abci.Application // A custom human readable name for this node - Moniker string `toml:"moniker"` + Moniker string `toml:"moniker" comment:"A custom human readable name for this node"` // If this node is many blocks behind the tip of the chain, FastSync // allows them to catchup quickly by downloading blocks in parallel // and verifying their commits - FastSyncMode bool `toml:"fast_sync"` + FastSyncMode bool `toml:"fast_sync" comment:"If this node is many blocks behind the tip of the chain, FastSync\n allows them to catchup quickly by downloading blocks in parallel\n and verifying their commits"` // Database backend: goleveldb | cleveldb | boltdb // * goleveldb (github.com/gnolang/goleveldb - most popular implementation) @@ -185,42 +255,36 @@ type BaseConfig struct { // - EXPERIMENTAL // - may be faster is some use-cases (random reads - indexer) // - use boltdb build tag (go build -tags boltdb) - DBBackend string `toml:"db_backend"` + DBBackend string `toml:"db_backend" comment:"Database backend: goleveldb | cleveldb | boltdb\n * goleveldb (github.com/gnolang/goleveldb - most popular implementation)\n - pure go\n - stable\n * cleveldb (uses levigo wrapper)\n - fast\n - requires gcc\n - use cleveldb build tag (go build -tags cleveldb)\n * boltdb (uses etcd's fork of bolt - go.etcd.io/bbolt)\n - EXPERIMENTAL\n - may be faster is some use-cases (random reads - indexer)\n - use boltdb build tag (go build -tags boltdb)"` // Database directory - DBPath string `toml:"db_dir"` - - // Output level for logging - LogLevel string `toml:"log_level"` - - // Output format: 'plain' (colored text) or 'json' - LogFormat string `toml:"log_format"` + DBPath string `toml:"db_dir" comment:"Database directory"` // Path to the JSON file containing the initial validator set and other meta data - Genesis string `toml:"genesis_file"` + Genesis string `toml:"genesis_file" comment:"Path to the JSON file containing the initial validator set and other meta data"` // Path to the JSON file containing the private key to use as a validator in the consensus protocol - PrivValidatorKey string `toml:"priv_validator_key_file"` + PrivValidatorKey string `toml:"priv_validator_key_file" comment:"Path to the JSON file containing the private key to use as a validator in the consensus protocol"` // Path to the JSON file containing the last sign state of a validator - PrivValidatorState string `toml:"priv_validator_state_file"` + PrivValidatorState string `toml:"priv_validator_state_file" comment:"Path to the JSON file containing the last sign state of a validator"` // TCP or UNIX socket address for Tendermint to listen on for // connections from an external PrivValidator process - PrivValidatorListenAddr string `toml:"priv_validator_laddr"` + PrivValidatorListenAddr string `toml:"priv_validator_laddr" comment:"TCP or UNIX socket address for Tendermint to listen on for\n connections from an external PrivValidator process"` // A JSON file containing the private key to use for p2p authenticated encryption - NodeKey string `toml:"node_key_file"` + NodeKey string `toml:"node_key_file" comment:"Path to the JSON file containing the private key to use for node authentication in the p2p protocol"` // Mechanism to connect to the ABCI application: local | socket - ABCI string `toml:"abci"` + ABCI string `toml:"abci" comment:"Mechanism to connect to the ABCI application: socket | grpc"` // TCP or UNIX socket address for the profiling server to listen on - ProfListenAddress string `toml:"prof_laddr"` + ProfListenAddress string `toml:"prof_laddr" comment:"TCP or UNIX socket address for the profiling server to listen on"` // If true, query the ABCI app on connecting to a new peer // so the app can decide if we should keep the connection or not - FilterPeers bool `toml:"filter_peers"` // false + FilterPeers bool `toml:"filter_peers" comment:"If true, query the ABCI app on connecting to a new peer\n so the app can decide if we should keep the connection or not"` // false } // DefaultBaseConfig returns a default base configuration for a Tendermint node @@ -233,8 +297,6 @@ func DefaultBaseConfig() BaseConfig { Moniker: defaultMoniker, ProxyApp: "tcp://127.0.0.1:26658", ABCI: "socket", - LogLevel: DefaultPackageLogLevels(), - LogFormat: LogFormatPlain, ProfListenAddress: "", FastSyncMode: true, FilterPeers: false, @@ -243,8 +305,8 @@ func DefaultBaseConfig() BaseConfig { } } -// TestBaseConfig returns a base configuration for testing a Tendermint node -func TestBaseConfig() BaseConfig { +// testBaseConfig returns a base configuration for testing a Tendermint node +func testBaseConfig() BaseConfig { cfg := DefaultBaseConfig() cfg.chainID = "tendermint_test" cfg.ProxyApp = "mock://kvstore" @@ -282,28 +344,6 @@ func (cfg BaseConfig) DBDir() string { return join(cfg.RootDir, cfg.DBPath) } -// ValidateBasic performs basic validation (checking param bounds, etc.) and -// returns an error if any check fails. -func (cfg BaseConfig) ValidateBasic() error { - switch cfg.LogFormat { - case LogFormatPlain, LogFormatJSON: - default: - return errors.New("unknown log_format (must be 'plain' or 'json')") - } - return nil -} - -// DefaultLogLevel returns a default log level of "error" -func DefaultLogLevel() string { - return "error" -} - -// DefaultPackageLogLevels returns a default log level setting so all packages -// log at "error", while the `state` and `main` packages log at "info" -func DefaultPackageLogLevels() string { - return fmt.Sprintf("main:info,state:info,*:%s", DefaultLogLevel()) -} - var defaultMoniker = getDefaultMoniker() // getDefaultMoniker returns a default moniker, which is the host name. If runtime @@ -315,3 +355,63 @@ func getDefaultMoniker() string { } return moniker } + +// ValidateBasic performs basic validation (checking param bounds, etc.) and +// returns an error if any check fails. +func (cfg BaseConfig) ValidateBasic() error { + // Verify the moniker + if cfg.Moniker == "" { + return errInvalidMoniker + } + + // Verify the DB backend + if cfg.DBBackend != levelDBName && + cfg.DBBackend != clevelDBName && + cfg.DBBackend != boltDBName { + return errInvalidDBBackend + } + + // Verify the DB path is set + if cfg.DBPath == "" { + return errInvalidDBPath + } + + // Verify the genesis path is set + if cfg.Genesis == "" { + return errInvalidGenesisPath + } + + // Verify the validator private key path is set + if cfg.PrivValidatorKey == "" { + return errInvalidPrivValidatorKeyPath + } + + // Verify the validator state file path is set + if cfg.PrivValidatorState == "" { + return errInvalidPrivValidatorStatePath + } + + // Verify the PrivValidator listen address + if cfg.PrivValidatorListenAddr != "" && + !tcpUnixAddressRegex.MatchString(cfg.PrivValidatorListenAddr) { + return errInvalidPrivValidatorListenAddress + } + + // Verify the p2p private key exists + if cfg.NodeKey == "" { + return errInvalidNodeKeyPath + } + + // Verify the correct ABCI mechanism is set + if cfg.ABCI != localABCI && + cfg.ABCI != socketABCI { + return errInvalidABCIMechanism + } + + // Verify the profiling listen address + if cfg.ProfListenAddress != "" && !tcpUnixAddressRegex.MatchString(cfg.ProfListenAddress) { + return errInvalidProfListenAddress + } + + return nil +} diff --git a/tm2/pkg/bft/config/config_test.go b/tm2/pkg/bft/config/config_test.go new file mode 100644 index 00000000000..adeeade26b3 --- /dev/null +++ b/tm2/pkg/bft/config/config_test.go @@ -0,0 +1,192 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestConfig_LoadOrMakeConfigWithOptions(t *testing.T) { + t.Parallel() + + t.Run("existing configuration", func(t *testing.T) { + t.Parallel() + + // Provide an empty directory + cfgDir := t.TempDir() + cfgPath := join(cfgDir, defaultConfigFilePath) + + // Create a default config + cfg := DefaultConfig() + cfg.SetRootDir(cfgDir) + + // Make an incremental changes + cfg.Moniker = "custom moniker" + + // Make sure the cfg paths are initialized + require.NoError(t, cfg.EnsureDirs()) + + // Write the config + require.NoError(t, WriteConfigFile(cfgPath, cfg)) + + // Load the config + loadedCfg, loadErr := LoadOrMakeConfigWithOptions(cfgDir) + require.NoError(t, loadErr) + + assert.Equal(t, cfg, loadedCfg) + }) + + t.Run("no existing config", func(t *testing.T) { + t.Parallel() + + // Provide an empty directory + cfgDir := t.TempDir() + cfgPath := join(cfgDir, defaultConfigFilePath) + + cfg, err := LoadOrMakeConfigWithOptions(cfgDir) + require.NoError(t, err) + + // Make sure the returned cfg is the default one + expectedCfg := DefaultConfig() + expectedCfg.SetRootDir(cfgDir) + + assert.Equal(t, expectedCfg, cfg) + + // Make sure the returned config was saved + loadedCfg, loadErr := LoadConfigFile(cfgPath) + require.NoError(t, loadErr) + + loadedCfg.SetRootDir(cfgDir) + + assert.Equal(t, cfg, loadedCfg) + }) + + t.Run("no existing config, with options", func(t *testing.T) { + t.Parallel() + + moniker := "dummy moniker" + + // Provide an empty directory + cfgDir := t.TempDir() + cfgPath := join(cfgDir, defaultConfigFilePath) + + cfg, err := LoadOrMakeConfigWithOptions( + cfgDir, + func(cfg *Config) { + cfg.BaseConfig.Moniker = moniker + }, + ) + require.NoError(t, err) + + // Make sure the returned config was saved + loadedCfg, loadErr := LoadConfigFile(cfgPath) + require.NoError(t, loadErr) + + loadedCfg.SetRootDir(cfgDir) + + assert.Equal(t, cfg, loadedCfg) + }) +} + +func TestConfig_ValidateBaseConfig(t *testing.T) { + t.Parallel() + + t.Run("valid default config", func(t *testing.T) { + t.Parallel() + + c := DefaultConfig() + + assert.NoError(t, c.BaseConfig.ValidateBasic()) + }) + + t.Run("invalid moniker", func(t *testing.T) { + t.Parallel() + + c := DefaultConfig() + c.Moniker = "" + + assert.ErrorIs(t, c.BaseConfig.ValidateBasic(), errInvalidMoniker) + }) + + t.Run("invalid DB backend", func(t *testing.T) { + t.Parallel() + + c := DefaultConfig() + c.DBBackend = "totally valid backend" + + assert.ErrorIs(t, c.BaseConfig.ValidateBasic(), errInvalidDBBackend) + }) + + t.Run("DB path not set", func(t *testing.T) { + t.Parallel() + + c := DefaultConfig() + c.DBPath = "" + + assert.ErrorIs(t, c.BaseConfig.ValidateBasic(), errInvalidDBPath) + }) + + t.Run("genesis path not set", func(t *testing.T) { + t.Parallel() + + c := DefaultConfig() + c.Genesis = "" + + assert.ErrorIs(t, c.BaseConfig.ValidateBasic(), errInvalidGenesisPath) + }) + + t.Run("priv validator key path not set", func(t *testing.T) { + t.Parallel() + + c := DefaultConfig() + c.PrivValidatorKey = "" + + assert.ErrorIs(t, c.BaseConfig.ValidateBasic(), errInvalidPrivValidatorKeyPath) + }) + + t.Run("priv validator state path not set", func(t *testing.T) { + t.Parallel() + + c := DefaultConfig() + c.PrivValidatorState = "" + + assert.ErrorIs(t, c.BaseConfig.ValidateBasic(), errInvalidPrivValidatorStatePath) + }) + + t.Run("invalid priv validator listen address", func(t *testing.T) { + t.Parallel() + + c := DefaultConfig() + c.PrivValidatorListenAddr = "beep.boop" + + assert.ErrorIs(t, c.BaseConfig.ValidateBasic(), errInvalidPrivValidatorListenAddress) + }) + + t.Run("node key path not set", func(t *testing.T) { + t.Parallel() + + c := DefaultConfig() + c.NodeKey = "" + + assert.ErrorIs(t, c.BaseConfig.ValidateBasic(), errInvalidNodeKeyPath) + }) + + t.Run("invalid ABCI mechanism", func(t *testing.T) { + t.Parallel() + + c := DefaultConfig() + c.ABCI = "hopes and dreams" + + assert.ErrorIs(t, c.BaseConfig.ValidateBasic(), errInvalidABCIMechanism) + }) + + t.Run("invalid prof listen address", func(t *testing.T) { + t.Parallel() + + c := DefaultConfig() + c.ProfListenAddress = "beep.boop" + + assert.ErrorIs(t, c.BaseConfig.ValidateBasic(), errInvalidProfListenAddress) + }) +} diff --git a/tm2/pkg/bft/config/toml.go b/tm2/pkg/bft/config/toml.go index a35e5674631..f813d23b8fb 100644 --- a/tm2/pkg/bft/config/toml.go +++ b/tm2/pkg/bft/config/toml.go @@ -1,12 +1,9 @@ package config import ( - "bytes" "fmt" - "io/ioutil" "os" "path/filepath" - "text/template" osm "github.com/gnolang/gno/tm2/pkg/os" "github.com/pelletier/go-toml" @@ -15,281 +12,46 @@ import ( // DefaultDirPerm is the default permissions used when creating directories. const DefaultDirPerm = 0o700 -var configTemplate *template.Template - -func init() { - var err error - if configTemplate, err = template.New("configFileTemplate").Parse(defaultConfigTemplate); err != nil { - panic(err) +// LoadConfigFile loads the TOML node configuration from the specified path +func LoadConfigFile(path string) (*Config, error) { + // Read the config file + content, readErr := os.ReadFile(path) + if readErr != nil { + return nil, readErr } -} -func LoadConfigFile(configFilePath string) *Config { - bz, err := os.ReadFile(configFilePath) - if err != nil { - panic(err) + // Parse the node config + var nodeConfig Config + + if unmarshalErr := toml.Unmarshal(content, &nodeConfig); unmarshalErr != nil { + return nil, unmarshalErr } - var config Config - err = toml.Unmarshal(bz, &config) - if err != nil { - panic(err) + + // Validate the config + if validateErr := nodeConfig.ValidateBasic(); validateErr != nil { + return nil, fmt.Errorf("unable to validate config, %w", validateErr) } - return &config + + return &nodeConfig, nil } /****** these are for production settings ***********/ // WriteConfigFile renders config using the template and writes it to configFilePath. -func WriteConfigFile(configFilePath string, config *Config) { - var buffer bytes.Buffer +func WriteConfigFile(configFilePath string, config *Config) error { + // Marshal the config + configRaw, err := toml.Marshal(config) + if err != nil { + return fmt.Errorf("unable to TOML marshal config, %w", err) + } - if err := configTemplate.Execute(&buffer, config); err != nil { - panic(err) + if err := osm.WriteFile(configFilePath, configRaw, 0o644); err != nil { + return fmt.Errorf("unable to write config file, %w", err) } - osm.MustWriteFile(configFilePath, buffer.Bytes(), 0o644) + return nil } -// Note: any changes to the comments/variables/field-names -// must be reflected in the appropriate struct in config/config.go -const defaultConfigTemplate = `# This is a TOML config file. -# For more information, see https://github.com/toml-lang/toml - -##### main base config options ##### - -# TCP or UNIX socket address of the ABCI application, -# or the name of an ABCI application compiled in with the Tendermint binary -proxy_app = "{{ .BaseConfig.ProxyApp }}" - -# A custom human readable name for this node -moniker = "{{ .BaseConfig.Moniker }}" - -# If this node is many blocks behind the tip of the chain, FastSync -# allows them to catchup quickly by downloading blocks in parallel -# and verifying their commits -fast_sync = {{ .BaseConfig.FastSyncMode }} - -# Database backend: goleveldb | cleveldb | boltdb -# * goleveldb (github.com/gnolang/goleveldb - most popular implementation) -# - pure go -# - stable -# * cleveldb (uses levigo wrapper) -# - fast -# - requires gcc -# - use cleveldb build tag (go build -tags cleveldb) -# * boltdb (uses etcd's fork of bolt - go.etcd.io/bbolt) -# - EXPERIMENTAL -# - may be faster is some use-cases (random reads - indexer) -# - use boltdb build tag (go build -tags boltdb) -db_backend = "{{ .BaseConfig.DBBackend }}" - -# Database directory -db_dir = "{{ js .BaseConfig.DBPath }}" - -# Output level for logging, including package level options -log_level = "{{ .BaseConfig.LogLevel }}" - -# Output format: 'plain' (colored text) or 'json' -log_format = "{{ .BaseConfig.LogFormat }}" - -##### additional base config options ##### - -# Path to the JSON file containing the initial validator set and other meta data -genesis_file = "{{ js .BaseConfig.Genesis }}" - -# Path to the JSON file containing the private key to use as a validator in the consensus protocol -priv_validator_key_file = "{{ js .BaseConfig.PrivValidatorKey }}" - -# Path to the JSON file containing the last sign state of a validator -priv_validator_state_file = "{{ js .BaseConfig.PrivValidatorState }}" - -# TCP or UNIX socket address for Tendermint to listen on for -# connections from an external PrivValidator process -priv_validator_laddr = "{{ .BaseConfig.PrivValidatorListenAddr }}" - -# Path to the JSON file containing the private key to use for node authentication in the p2p protocol -node_key_file = "{{ js .BaseConfig.NodeKey }}" - -# Mechanism to connect to the ABCI application: socket | grpc -abci = "{{ .BaseConfig.ABCI }}" - -# TCP or UNIX socket address for the profiling server to listen on -prof_laddr = "{{ .BaseConfig.ProfListenAddress }}" - -# If true, query the ABCI app on connecting to a new peer -# so the app can decide if we should keep the connection or not -filter_peers = {{ .BaseConfig.FilterPeers }} - -##### advanced configuration options ##### - -##### rpc server configuration options ##### -[rpc] - -# TCP or UNIX socket address for the RPC server to listen on -laddr = "{{ .RPC.ListenAddress }}" - -# A list of origins a cross-domain request can be executed from -# Default value '[]' disables cors support -# Use '["*"]' to allow any origin -cors_allowed_origins = [{{ range .RPC.CORSAllowedOrigins }}{{ printf "%q, " . }}{{end}}] - -# A list of methods the client is allowed to use with cross-domain requests -cors_allowed_methods = [{{ range .RPC.CORSAllowedMethods }}{{ printf "%q, " . }}{{end}}] - -# A list of non simple headers the client is allowed to use with cross-domain requests -cors_allowed_headers = [{{ range .RPC.CORSAllowedHeaders }}{{ printf "%q, " . }}{{end}}] - -# TCP or UNIX socket address for the gRPC server to listen on -# NOTE: This server only supports /broadcast_tx_commit -grpc_laddr = "{{ .RPC.GRPCListenAddress }}" - -# Maximum number of simultaneous connections. -# Does not include RPC (HTTP&WebSocket) connections. See max_open_connections -# If you want to accept a larger number than the default, make sure -# you increase your OS limits. -# 0 - unlimited. -# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} -# 1024 - 40 - 10 - 50 = 924 = ~900 -grpc_max_open_connections = {{ .RPC.GRPCMaxOpenConnections }} - -# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool -unsafe = {{ .RPC.Unsafe }} - -# Maximum number of simultaneous connections (including WebSocket). -# Does not include gRPC connections. See grpc_max_open_connections -# If you want to accept a larger number than the default, make sure -# you increase your OS limits. -# 0 - unlimited. -# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} -# 1024 - 40 - 10 - 50 = 924 = ~900 -max_open_connections = {{ .RPC.MaxOpenConnections }} - -# How long to wait for a tx to be committed during /broadcast_tx_commit. -# WARNING: Using a value larger than 10s will result in increasing the -# global HTTP write timeout, which applies to all connections and endpoints. -# See https://github.com/tendermint/classic/issues/3435 -timeout_broadcast_tx_commit = "{{ .RPC.TimeoutBroadcastTxCommit }}" - -# Maximum size of request body, in bytes -max_body_bytes = {{ .RPC.MaxBodyBytes }} - -# Maximum size of request header, in bytes -max_header_bytes = {{ .RPC.MaxHeaderBytes }} - -# The path to a file containing certificate that is used to create the HTTPS server. -# Migth be either absolute path or path related to tendermint's config directory. -# If the certificate is signed by a certificate authority, -# the certFile should be the concatenation of the server's certificate, any intermediates, -# and the CA's certificate. -# NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. -tls_cert_file = "{{ .RPC.TLSCertFile }}" - -# The path to a file containing matching private key that is used to create the HTTPS server. -# Migth be either absolute path or path related to tendermint's config directory. -# NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. -tls_key_file = "{{ .RPC.TLSKeyFile }}" - -##### peer to peer configuration options ##### -[p2p] - -# Address to listen for incoming connections -laddr = "{{ .P2P.ListenAddress }}" - -# Address to advertise to peers for them to dial -# If empty, will use the same port as the laddr, -# and will introspect on the listener or use UPnP -# to figure out the address. -external_address = "{{ .P2P.ExternalAddress }}" - -# Comma separated list of seed nodes to connect to -seeds = "{{ .P2P.Seeds }}" - -# Comma separated list of nodes to keep persistent connections to -persistent_peers = "{{ .P2P.PersistentPeers }}" - -# UPNP port forwarding -upnp = {{ .P2P.UPNP }} - -# Maximum number of inbound peers -max_num_inbound_peers = {{ .P2P.MaxNumInboundPeers }} - -# Maximum number of outbound peers to connect to, excluding persistent peers -max_num_outbound_peers = {{ .P2P.MaxNumOutboundPeers }} - -# Time to wait before flushing messages out on the connection -flush_throttle_timeout = "{{ .P2P.FlushThrottleTimeout }}" - -# Maximum size of a message packet payload, in bytes -max_packet_msg_payload_size = {{ .P2P.MaxPacketMsgPayloadSize }} - -# Rate at which packets can be sent, in bytes/second -send_rate = {{ .P2P.SendRate }} - -# Rate at which packets can be received, in bytes/second -recv_rate = {{ .P2P.RecvRate }} - -# Set true to enable the peer-exchange reactor -pex = {{ .P2P.PexReactor }} - -# Seed mode, in which node constantly crawls the network and looks for -# peers. If another node asks it for addresses, it responds and disconnects. -# -# Does not work if the peer-exchange reactor is disabled. -seed_mode = {{ .P2P.SeedMode }} - -# Comma separated list of peer IDs to keep private (will not be gossiped to other peers) -private_peer_ids = "{{ .P2P.PrivatePeerIDs }}" - -# Toggle to disable guard against peers connecting from the same ip. -allow_duplicate_ip = {{ .P2P.AllowDuplicateIP }} - -# Peer connection configuration. -handshake_timeout = "{{ .P2P.HandshakeTimeout }}" -dial_timeout = "{{ .P2P.DialTimeout }}" - -##### mempool configuration options ##### -[mempool] - -recheck = {{ .Mempool.Recheck }} -broadcast = {{ .Mempool.Broadcast }} -wal_dir = "{{ js .Mempool.WalPath }}" - -# Maximum number of transactions in the mempool -size = {{ .Mempool.Size }} - -# Limit the total size of all txs in the mempool. -# This only accounts for raw transactions (e.g. given 1MB transactions and -# max_txs_bytes=5MB, mempool will only accept 5 transactions). -max_pending_txs_bytes = {{ .Mempool.MaxPendingTxsBytes }} - -# Size of the cache (used to filter transactions we saw earlier) in transactions -cache_size = {{ .Mempool.CacheSize }} - -##### consensus configuration options ##### -[consensus] - -wal_file = "{{ js .Consensus.WalPath }}" - -timeout_propose = "{{ .Consensus.TimeoutPropose }}" -timeout_propose_delta = "{{ .Consensus.TimeoutProposeDelta }}" -timeout_prevote = "{{ .Consensus.TimeoutPrevote }}" -timeout_prevote_delta = "{{ .Consensus.TimeoutPrevoteDelta }}" -timeout_precommit = "{{ .Consensus.TimeoutPrecommit }}" -timeout_precommit_delta = "{{ .Consensus.TimeoutPrecommitDelta }}" -timeout_commit = "{{ .Consensus.TimeoutCommit }}" - -# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) -skip_timeout_commit = {{ .Consensus.SkipTimeoutCommit }} - -# EmptyBlocks mode and possible interval between empty blocks -create_empty_blocks = {{ .Consensus.CreateEmptyBlocks }} -create_empty_blocks_interval = "{{ .Consensus.CreateEmptyBlocksInterval }}" - -# Reactor sleep duration parameters -peer_gossip_sleep_duration = "{{ .Consensus.PeerGossipSleepDuration }}" -peer_query_maj23_sleep_duration = "{{ .Consensus.PeerQueryMaj23SleepDuration }}" -` - /****** these are for test settings ***********/ func ResetTestRoot(testName string) *Config { @@ -298,7 +60,7 @@ func ResetTestRoot(testName string) *Config { func ResetTestRootWithChainID(testName string, chainID string) *Config { // create a unique, concurrency-safe test directory under os.TempDir() - rootDir, err := ioutil.TempDir("", fmt.Sprintf("%s-%s_", chainID, testName)) + rootDir, err := os.MkdirTemp("", fmt.Sprintf("%s-%s_", chainID, testName)) if err != nil { panic(err) } diff --git a/tm2/pkg/bft/config/toml_test.go b/tm2/pkg/bft/config/toml_test.go index 0fe78285997..b75057a52dd 100644 --- a/tm2/pkg/bft/config/toml_test.go +++ b/tm2/pkg/bft/config/toml_test.go @@ -1,10 +1,14 @@ package config import ( + "io" "os" + "reflect" "strings" "testing" + "github.com/gnolang/gno/tm2/pkg/testutils" + "github.com/pelletier/go-toml" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -20,7 +24,7 @@ func ensureFiles(t *testing.T, rootDir string, files ...string) { } func TestEnsureRoot(t *testing.T) { - require := require.New(t) + t.Parallel() // setup temp dir for test tmpDir := t.TempDir() @@ -28,13 +32,13 @@ func TestEnsureRoot(t *testing.T) { // create root dir throwaway := DefaultConfig() throwaway.SetRootDir(tmpDir) - throwaway.EnsureDirs() + require.NoError(t, throwaway.EnsureDirs()) configPath := join(tmpDir, defaultConfigFilePath) - WriteConfigFile(configPath, throwaway) + require.NoError(t, WriteConfigFile(configPath, throwaway)) // make sure config is set properly data, err := os.ReadFile(join(tmpDir, defaultConfigFilePath)) - require.Nil(err) + require.Nil(t, err) if !checkConfig(string(data)) { t.Fatalf("config file missing some information") @@ -44,6 +48,8 @@ func TestEnsureRoot(t *testing.T) { } func TestEnsureTestRoot(t *testing.T) { + t.Parallel() + require := require.New(t) testName := "ensureTestRoot" @@ -95,3 +101,128 @@ func checkConfig(configFile string) bool { } return valid } + +func TestTOML_LoadConfig(t *testing.T) { + t.Parallel() + + t.Run("config does not exist", func(t *testing.T) { + t.Parallel() + + cfg, loadErr := LoadConfigFile("dummy-path") + + assert.Error(t, loadErr) + assert.Nil(t, cfg) + }) + + t.Run("config is not valid toml", func(t *testing.T) { + t.Parallel() + + // Create config file + configFile, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + // Write invalid TOML + _, writeErr := configFile.WriteString("invalid TOML") + require.NoError(t, writeErr) + + cfg, loadErr := LoadConfigFile(configFile.Name()) + + assert.Error(t, loadErr) + assert.Nil(t, cfg) + }) + + t.Run("valid config", func(t *testing.T) { + t.Parallel() + + // Create config file + configFile, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + // Create the default config + defaultConfig := DefaultConfig() + + // Marshal the default config + defaultConfigRaw, marshalErr := toml.Marshal(defaultConfig) + require.NoError(t, marshalErr) + + // Write valid TOML + _, writeErr := configFile.Write(defaultConfigRaw) + require.NoError(t, writeErr) + + cfg, loadErr := LoadConfigFile(configFile.Name()) + require.NoError(t, loadErr) + + assert.EqualValues(t, defaultConfig.BaseConfig, cfg.BaseConfig) + assert.EqualValues(t, defaultConfig.RPC, cfg.RPC) + assert.EqualValues(t, defaultConfig.P2P, cfg.P2P) + assert.EqualValues(t, defaultConfig.Mempool, cfg.Mempool) + assert.EqualValues(t, defaultConfig.Consensus, cfg.Consensus) + assert.Equal(t, defaultConfig.TxEventStore.EventStoreType, cfg.TxEventStore.EventStoreType) + assert.Empty(t, defaultConfig.TxEventStore.Params, cfg.TxEventStore.Params) + }) +} + +func TestTOML_ConfigComments(t *testing.T) { + t.Parallel() + + collectCommentTags := func(v reflect.Value) []string { + var ( + comments = make([]string, 0) + structStack = []reflect.Value{v} + ) + + // Descend on and parse all child fields + for len(structStack) > 0 { + structVal := structStack[len(structStack)-1] + structStack = structStack[:len(structStack)-1] + + // Process all fields of the struct + for i := 0; i < structVal.NumField(); i++ { + fieldVal := structVal.Field(i) + fieldType := structVal.Type().Field(i) + + // If the field is a struct, push it onto the stack for further processing + if fieldVal.Kind() == reflect.Struct { + structStack = append(structStack, fieldVal) + + continue + } + + // Collect the comment tag value from the field + if commentTag := fieldType.Tag.Get("comment"); commentTag != "" { + comments = append(comments, commentTag) + } + } + } + + return comments + } + + cleanComments := func(original string) string { + return strings.ReplaceAll(original, "#", "") + } + + // Create test config file + configFile, cleanup := testutils.NewTestFile(t) + t.Cleanup(cleanup) + + // Create the default config + defaultConfig := DefaultConfig() + + // Write valid TOML + require.NoError(t, WriteConfigFile(configFile.Name(), defaultConfig)) + + // Collect config comments + comments := collectCommentTags(reflect.ValueOf(*defaultConfig)) + require.NotEmpty(t, comments) + + // Read the entire config file + rawConfig, err := io.ReadAll(configFile) + require.NoError(t, err) + + // Verify TOML comments are present + content := cleanComments(string(rawConfig)) + for _, comment := range comments { + assert.Contains(t, content, cleanComments(comment)) + } +} diff --git a/tm2/pkg/bft/consensus/common_test.go b/tm2/pkg/bft/consensus/common_test.go index 7424305c00a..4a0a4eebf03 100644 --- a/tm2/pkg/bft/consensus/common_test.go +++ b/tm2/pkg/bft/consensus/common_test.go @@ -3,7 +3,6 @@ package consensus import ( "bytes" "fmt" - "io/ioutil" "os" "path" "path/filepath" @@ -12,6 +11,8 @@ import ( "testing" "time" + "golang.org/x/exp/slog" + abcicli "github.com/gnolang/gno/tm2/pkg/bft/abci/client" "github.com/gnolang/gno/tm2/pkg/bft/abci/example/counter" "github.com/gnolang/gno/tm2/pkg/bft/abci/example/kvstore" @@ -24,13 +25,11 @@ import ( "github.com/gnolang/gno/tm2/pkg/bft/store" "github.com/gnolang/gno/tm2/pkg/bft/types" tmtime "github.com/gnolang/gno/tm2/pkg/bft/types/time" - "github.com/gnolang/gno/tm2/pkg/colors" "github.com/gnolang/gno/tm2/pkg/crypto" dbm "github.com/gnolang/gno/tm2/pkg/db" "github.com/gnolang/gno/tm2/pkg/events" "github.com/gnolang/gno/tm2/pkg/log" osm "github.com/gnolang/gno/tm2/pkg/os" - "github.com/gnolang/gno/tm2/pkg/p2p" ) const ( @@ -282,7 +281,7 @@ func newConsensusStateWithConfigAndBlockStore(thisConfig *cfg.Config, state sm.S // Make Mempool mempool := mempl.NewCListMempool(thisConfig.Mempool, proxyAppConnMem, 0, state.ConsensusParams.Block.MaxTxBytes) - mempool.SetLogger(log.TestingLogger().With("module", "mempool")) + mempool.SetLogger(log.NewNoopLogger().With("module", "mempool")) if thisConfig.Consensus.WaitForTxs() { mempool.EnableTxsAvailable() } @@ -290,13 +289,13 @@ func newConsensusStateWithConfigAndBlockStore(thisConfig *cfg.Config, state sm.S // Make ConsensusState stateDB := blockDB sm.SaveState(stateDB, state) // for save height 1's validators info - blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyAppConnCon, mempool) + blockExec := sm.NewBlockExecutor(stateDB, log.NewNoopLogger(), proxyAppConnCon, mempool) cs := NewConsensusState(thisConfig.Consensus, state, blockExec, blockStore, mempool) - cs.SetLogger(log.TestingLogger().With("module", "consensus")) + cs.SetLogger(log.NewNoopLogger().With("module", "consensus")) cs.SetPrivValidator(pv) evsw := events.NewEventSwitch() - evsw.SetLogger(log.TestingLogger().With("module", "events")) + evsw.SetLogger(log.NewNoopLogger().With("module", "events")) evsw.Start() cs.SetEventSwitch(evsw) return cs @@ -568,46 +567,13 @@ func ensureNewEventOnChannel(ch <-chan events.Event) { // ------------------------------------------------------------------------------- // consensus nets -// consensusLogger is a TestingLogger which uses a different -// color for each validator ("validator" key must exist). -func consensusLogger() log.Logger { - return log.TestingLoggerWithColorFn(func(keyvals ...interface{}) colors.Color { - for i := 0; i < len(keyvals)-1; i += 2 { - if keyvals[i] == "validator" { - num := keyvals[i+1].(int) - switch num % 8 { - case 0: - return colors.Red - case 1: - return colors.Green - case 2: - return colors.Yellow - case 3: - return colors.Blue - case 4: - return colors.Magenta - case 5: - return colors.Cyan - case 6: - return colors.White - case 7: - return colors.Gray - default: - panic("should not happen") - } - } - } - return colors.None - }).With("module", "consensus") -} - func randConsensusNet(nValidators int, testName string, tickerFunc func() TimeoutTicker, appFunc func() abci.Application, configOpts ...func(*cfg.Config), ) ([]*ConsensusState, cleanupFunc) { genDoc, privVals := randGenesisDoc(nValidators, false, 30) css := make([]*ConsensusState, nValidators) apps := make([]abci.Application, nValidators) - logger := consensusLogger() + logger := log.NewNoopLogger() configRootDirs := make([]string, 0, nValidators) for i := 0; i < nValidators; i++ { stateDB := dbm.NewMemDB() // each state needs its own db @@ -646,7 +612,7 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF genDoc, privVals := randGenesisDoc(nValidators, false, testMinPower) css := make([]*ConsensusState, nPeers) apps := make([]abci.Application, nPeers) - logger := consensusLogger() + logger := log.NewNoopLogger() var peer0Config *cfg.Config configRootDirs := make([]string, 0, nPeers) for i := 0; i < nPeers; i++ { @@ -662,11 +628,11 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF if i < nValidators { privVal = privVals[i] } else { - tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") + tempKeyFile, err := os.CreateTemp("", "priv_validator_key_") if err != nil { panic(err) } - tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") + tempStateFile, err := os.CreateTemp("", "priv_validator_state_") if err != nil { panic(err) } @@ -702,15 +668,6 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF } } -func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int { - for i, s := range switches { - if peer.NodeInfo().ID() == s.NodeInfo().ID() { - return i - } - } - panic("didnt find peer in switches") -} - // ------------------------------------------------------------------------------- // genesis @@ -786,7 +743,7 @@ func (m *mockTicker) Chan() <-chan timeoutInfo { return m.c } -func (*mockTicker) SetLogger(log.Logger) {} +func (*mockTicker) SetLogger(_ *slog.Logger) {} // ------------------------------------ @@ -795,7 +752,7 @@ func newCounter() abci.Application { } func newPersistentKVStore() abci.Application { - dir, err := ioutil.TempDir("", "persistent-kvstore") + dir, err := os.MkdirTemp("", "persistent-kvstore") if err != nil { panic(err) } diff --git a/tm2/pkg/bft/consensus/config/config.go b/tm2/pkg/bft/consensus/config/config.go index 234def843e9..dbd4ffc6364 100644 --- a/tm2/pkg/bft/consensus/config/config.go +++ b/tm2/pkg/bft/consensus/config/config.go @@ -6,7 +6,7 @@ import ( "time" ) -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // ConsensusConfig const ( @@ -29,14 +29,14 @@ type ConsensusConfig struct { TimeoutCommit time.Duration `toml:"timeout_commit"` // Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) - SkipTimeoutCommit bool `toml:"skip_timeout_commit"` + SkipTimeoutCommit bool `toml:"skip_timeout_commit" comment:"Make progress as soon as we have all the precommits (as if TimeoutCommit = 0)"` // EmptyBlocks mode and possible interval between empty blocks - CreateEmptyBlocks bool `toml:"create_empty_blocks"` + CreateEmptyBlocks bool `toml:"create_empty_blocks" comment:"EmptyBlocks mode and possible interval between empty blocks"` CreateEmptyBlocksInterval time.Duration `toml:"create_empty_blocks_interval"` // Reactor sleep duration parameters - PeerGossipSleepDuration time.Duration `toml:"peer_gossip_sleep_duration"` + PeerGossipSleepDuration time.Duration `toml:"peer_gossip_sleep_duration" comment:"Reactor sleep duration parameters"` PeerQueryMaj23SleepDuration time.Duration `toml:"peer_query_maj23_sleep_duration"` } diff --git a/tm2/pkg/bft/consensus/consensus.proto b/tm2/pkg/bft/consensus/consensus.proto index 1859ab28153..f2c8a70156d 100644 --- a/tm2/pkg/bft/consensus/consensus.proto +++ b/tm2/pkg/bft/consensus/consensus.proto @@ -14,75 +14,75 @@ import "google/protobuf/duration.proto"; // messages message NewRoundStepMessage { - sint64 Height = 1; - sint64 Round = 2; - uint32 Step = 3; - sint64 SecondsSinceStartTime = 4; - sint64 LastCommitRound = 5; + sint64 height = 1 [json_name = "Height"]; + sint64 round = 2 [json_name = "Round"]; + uint32 step = 3 [json_name = "Step"]; + sint64 seconds_since_start_time = 4 [json_name = "SecondsSinceStartTime"]; + sint64 last_commit_round = 5 [json_name = "LastCommitRound"]; } message NewValidBlockMessage { - sint64 Height = 1; - sint64 Round = 2; - PartSetHeader BlockPartsHeader = 3; - BitArray BlockParts = 4; - bool IsCommit = 5; + sint64 height = 1 [json_name = "Height"]; + sint64 round = 2 [json_name = "Round"]; + PartSetHeader block_parts_header = 3 [json_name = "BlockPartsHeader"]; + BitArray block_parts = 4 [json_name = "BlockParts"]; + bool is_commit = 5 [json_name = "IsCommit"]; } message ProposalMessage { - Proposal Proposal = 1; + Proposal proposal = 1 [json_name = "Proposal"]; } message ProposalPOLMessage { - sint64 Height = 1; - sint64 ProposalPOLRound = 2; - BitArray ProposalPOL = 3; + sint64 height = 1 [json_name = "Height"]; + sint64 proposal_pol_round = 2 [json_name = "ProposalPOLRound"]; + BitArray proposal_pol = 3 [json_name = "ProposalPOL"]; } message BlockPartMessage { - sint64 Height = 1; - sint64 Round = 2; - Part Part = 3; + sint64 height = 1 [json_name = "Height"]; + sint64 round = 2 [json_name = "Round"]; + Part part = 3 [json_name = "Part"]; } message VoteMessage { - Vote Vote = 1; + Vote vote = 1 [json_name = "Vote"]; } message HasVoteMessage { - sint64 Height = 1; - sint64 Round = 2; - uint32 Type = 3; - sint64 Index = 4; + sint64 height = 1 [json_name = "Height"]; + sint64 round = 2 [json_name = "Round"]; + uint32 type = 3 [json_name = "Type"]; + sint64 index = 4 [json_name = "Index"]; } message VoteSetMaj23Message { - sint64 Height = 1; - sint64 Round = 2; - uint32 Type = 3; - BlockID BlockID = 4; + sint64 height = 1 [json_name = "Height"]; + sint64 round = 2 [json_name = "Round"]; + uint32 type = 3 [json_name = "Type"]; + BlockID block_id = 4 [json_name = "BlockID"]; } message VoteSetBitsMessage { - sint64 Height = 1; - sint64 Round = 2; - uint32 Type = 3; - BlockID BlockID = 4; - BitArray Votes = 5; + sint64 height = 1 [json_name = "Height"]; + sint64 round = 2 [json_name = "Round"]; + uint32 type = 3 [json_name = "Type"]; + BlockID block_id = 4 [json_name = "BlockID"]; + BitArray votes = 5 [json_name = "Votes"]; } message newRoundStepInfo { - HRS HRS = 1; + HRS hrs = 1; } message msgInfo { - google.protobuf.Any Msg = 1; - string PeerID = 2; + google.protobuf.Any msg = 1; + string peer_id = 2 [json_name = "peer_key"]; } message timeoutInfo { - google.protobuf.Duration Duration = 1; - sint64 Height = 2; - sint64 Round = 3; - uint32 Step = 4; + google.protobuf.Duration duration = 1; + sint64 height = 2; + sint64 round = 3; + uint32 step = 4; } \ No newline at end of file diff --git a/tm2/pkg/bft/consensus/mempool_test.go b/tm2/pkg/bft/consensus/mempool_test.go index b9c04cabbed..85914262903 100644 --- a/tm2/pkg/bft/consensus/mempool_test.go +++ b/tm2/pkg/bft/consensus/mempool_test.go @@ -24,6 +24,8 @@ func assertMempool(txn txNotifier) mempl.Mempool { } func TestMempoolNoProgressUntilTxsAvailable(t *testing.T) { + t.Parallel() + config := ResetConfig("consensus_mempool_no_progress_until_txs_available") defer os.RemoveAll(config.RootDir) config.Consensus.CreateEmptyBlocks = false @@ -71,6 +73,8 @@ func TestMempoolProgressAfterCreateEmptyBlocksInterval(t *testing.T) { } func TestMempoolProgressInHigherRound(t *testing.T) { + t.Parallel() + config := ResetConfig("consensus_mempool_progress_in_higher_round") defer os.RemoveAll(config.RootDir) config.Consensus.CreateEmptyBlocks = false @@ -138,6 +142,8 @@ func deliverTxsRange(cs *ConsensusState, start, end int) { } func TestMempoolTxConcurrentWithCommit(t *testing.T) { + t.Parallel() + state, privVals := randGenesisState(1, false, 10) blockDB := dbm.NewMemDB() app := NewCounterApplication() @@ -169,6 +175,8 @@ func TestMempoolTxConcurrentWithCommit(t *testing.T) { } func TestMempoolRmBadTx(t *testing.T) { + t.Parallel() + state, privVals := randGenesisState(1, false, 10) app := NewCounterApplication() blockDB := dbm.NewMemDB() diff --git a/tm2/pkg/bft/consensus/reactor.go b/tm2/pkg/bft/consensus/reactor.go index ab7f3b55e92..27ead3f08f0 100644 --- a/tm2/pkg/bft/consensus/reactor.go +++ b/tm2/pkg/bft/consensus/reactor.go @@ -6,6 +6,8 @@ import ( "sync" "time" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/amino" cstypes "github.com/gnolang/gno/tm2/pkg/bft/consensus/types" sm "github.com/gnolang/gno/tm2/pkg/bft/state" @@ -30,7 +32,7 @@ const ( votesToContributeToBecomeGoodPeer = 10000 ) -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // ConsensusReactor defines a reactor for the consensus service. type ConsensusReactor struct { @@ -365,7 +367,7 @@ func (conR *ConsensusReactor) FastSync() bool { return conR.fastSync } -//-------------------------------------- +// -------------------------------------- // subscribeToBroadcastEvents subscribes for new round steps and votes // using internal pubsub defined on state to broadcast @@ -545,7 +547,7 @@ OUTER_LOOP: } } -func (conR *ConsensusReactor) gossipDataForCatchup(logger log.Logger, rs *cstypes.RoundState, +func (conR *ConsensusReactor) gossipDataForCatchup(logger *slog.Logger, rs *cstypes.RoundState, prs *cstypes.PeerRoundState, ps *PeerState, peer p2p.Peer, ) { if index, ok := prs.ProposalBlockParts.Not().PickRandom(); ok { @@ -659,7 +661,7 @@ OUTER_LOOP: } } -func (conR *ConsensusReactor) gossipVotesForHeight(logger log.Logger, rs *cstypes.RoundState, prs *cstypes.PeerRoundState, ps *PeerState) bool { +func (conR *ConsensusReactor) gossipVotesForHeight(logger *slog.Logger, rs *cstypes.RoundState, prs *cstypes.PeerRoundState, ps *PeerState) bool { // If there are lastCommits to send... if prs.Step == cstypes.RoundStepNewHeight { if ps.PickSendVote(rs.LastCommit) { @@ -865,7 +867,7 @@ func (conR *ConsensusReactor) StringIndented(indent string) string { return s } -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- var ( ErrPeerStateHeightRegression = errors.New("Error peer state height regression") @@ -878,7 +880,7 @@ var ( // Be mindful of what you Expose. type PeerState struct { peer p2p.Peer - logger log.Logger + logger *slog.Logger mtx sync.Mutex // NOTE: Modify below using setters, never directly. PRS cstypes.PeerRoundState `json:"round_state"` // Exposed. @@ -900,7 +902,7 @@ func (pss peerStateStats) String() string { func NewPeerState(peer p2p.Peer) *PeerState { return &PeerState{ peer: peer, - logger: log.NewNopLogger(), + logger: log.NewNoopLogger(), PRS: cstypes.PeerRoundState{ Round: -1, ProposalPOLRound: -1, @@ -913,7 +915,7 @@ func NewPeerState(peer p2p.Peer) *PeerState { // SetLogger allows to set a logger on the peer state. Returns the peer state // itself. -func (ps *PeerState) SetLogger(logger log.Logger) *PeerState { +func (ps *PeerState) SetLogger(logger *slog.Logger) *PeerState { ps.logger = logger return ps } @@ -1336,7 +1338,7 @@ func (ps *PeerState) StringIndented(indent string) string { indent) } -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // Messages // ConsensusMessage is a message that can be sent and received on the ConsensusReactor @@ -1352,7 +1354,7 @@ func decodeMsg(bz []byte) (msg ConsensusMessage, err error) { return } -//------------------------------------- +// ------------------------------------- // NewRoundStepMessage is sent for every step taken in the ConsensusState. // For every height/round/step transition @@ -1391,7 +1393,7 @@ func (m *NewRoundStepMessage) String() string { m.Height, m.Round, m.Step, m.LastCommitRound) } -//------------------------------------- +// ------------------------------------- // NewValidBlockMessage is sent when a validator observes a valid block B in some round r, // i.e., there is a Proposal for block B and 2/3+ prevotes for the block B in the round r. @@ -1435,7 +1437,7 @@ func (m *NewValidBlockMessage) String() string { m.Height, m.Round, m.BlockPartsHeader, m.BlockParts, m.IsCommit) } -//------------------------------------- +// ------------------------------------- // ProposalMessage is sent when a new block is proposed. type ProposalMessage struct { @@ -1452,7 +1454,7 @@ func (m *ProposalMessage) String() string { return fmt.Sprintf("[Proposal %v]", m.Proposal) } -//------------------------------------- +// ------------------------------------- // ProposalPOLMessage is sent when a previous proposal is re-proposed. type ProposalPOLMessage struct { @@ -1483,7 +1485,7 @@ func (m *ProposalPOLMessage) String() string { return fmt.Sprintf("[ProposalPOL H:%v POLR:%v POL:%v]", m.Height, m.ProposalPOLRound, m.ProposalPOL) } -//------------------------------------- +// ------------------------------------- // BlockPartMessage is sent when gossipping a piece of the proposed block. type BlockPartMessage struct { @@ -1511,7 +1513,7 @@ func (m *BlockPartMessage) String() string { return fmt.Sprintf("[BlockPart H:%v R:%v P:%v]", m.Height, m.Round, m.Part) } -//------------------------------------- +// ------------------------------------- // VoteMessage is sent when voting for a proposal (or lack thereof). type VoteMessage struct { @@ -1528,7 +1530,7 @@ func (m *VoteMessage) String() string { return fmt.Sprintf("[Vote %v]", m.Vote) } -//------------------------------------- +// ------------------------------------- // HasVoteMessage is sent to indicate that a particular vote has been received. type HasVoteMessage struct { @@ -1560,7 +1562,7 @@ func (m *HasVoteMessage) String() string { return fmt.Sprintf("[HasVote VI:%v V:{%v/%02d/%v}]", m.Index, m.Height, m.Round, m.Type) } -//------------------------------------- +// ------------------------------------- // VoteSetMaj23Message is sent to indicate that a given BlockID has seen +2/3 votes. type VoteSetMaj23Message struct { @@ -1592,7 +1594,7 @@ func (m *VoteSetMaj23Message) String() string { return fmt.Sprintf("[VSM23 %v/%02d/%v %v]", m.Height, m.Round, m.Type, m.BlockID) } -//------------------------------------- +// ------------------------------------- // VoteSetBitsMessage is sent to communicate the bit-array of votes seen for the BlockID. type VoteSetBitsMessage struct { @@ -1629,4 +1631,4 @@ func (m *VoteSetBitsMessage) String() string { return fmt.Sprintf("[VSB %v/%02d/%v %v %v]", m.Height, m.Round, m.Type, m.BlockID, m.Votes) } -//------------------------------------- +// ------------------------------------- diff --git a/tm2/pkg/bft/consensus/reactor_test.go b/tm2/pkg/bft/consensus/reactor_test.go index 2194d4d007d..96fd2ed987b 100644 --- a/tm2/pkg/bft/consensus/reactor_test.go +++ b/tm2/pkg/bft/consensus/reactor_test.go @@ -6,6 +6,8 @@ import ( "testing" "time" + "golang.org/x/exp/slog" + "github.com/stretchr/testify/assert" "github.com/gnolang/gno/tm2/pkg/amino" @@ -67,7 +69,7 @@ func startConsensusNet(css []*ConsensusState, n int) ([]*ConsensusReactor, []<-c return reactors, blocksSubs, eventSwitches, p2pSwitches } -func stopConsensusNet(logger log.Logger, reactors []*ConsensusReactor, eventSwitches []events.EventSwitch, p2pSwitches []*p2p.Switch) { +func stopConsensusNet(logger *slog.Logger, reactors []*ConsensusReactor, eventSwitches []events.EventSwitch, p2pSwitches []*p2p.Switch) { logger.Info("stopConsensusNet", "n", len(reactors)) for i, r := range reactors { logger.Info("stopConsensusNet: Stopping ConsensusReactor", "i", i) @@ -86,11 +88,13 @@ func stopConsensusNet(logger log.Logger, reactors []*ConsensusReactor, eventSwit // Ensure a testnet makes blocks func TestReactorBasic(t *testing.T) { + t.Parallel() + N := 4 css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) defer cleanup() reactors, blocksSubs, eventSwitches, p2pSwitches := startConsensusNet(css, N) - defer stopConsensusNet(log.TestingLogger(), reactors, eventSwitches, p2pSwitches) + defer stopConsensusNet(log.NewTestingLogger(t), reactors, eventSwitches, p2pSwitches) // wait till everyone makes the first new block timeoutWaitGroup(t, N, func(j int) { <-blocksSubs[j] @@ -101,6 +105,8 @@ func TestReactorBasic(t *testing.T) { // Ensure a testnet makes blocks when there are txs func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) { + t.Parallel() + N := 4 css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter, func(c *cfg.Config) { @@ -108,7 +114,7 @@ func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) { }) defer cleanup() reactors, blocksSubs, eventSwitches, p2pSwitches := startConsensusNet(css, N) - defer stopConsensusNet(log.TestingLogger(), reactors, eventSwitches, p2pSwitches) + defer stopConsensusNet(log.NewTestingLogger(t), reactors, eventSwitches, p2pSwitches) // send a tx if err := assertMempool(css[3].txNotifier).CheckTx([]byte{1, 2, 3}, nil); err != nil { @@ -122,11 +128,13 @@ func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) { } func TestReactorReceiveDoesNotPanicIfAddPeerHasntBeenCalledYet(t *testing.T) { + t.Parallel() + N := 1 css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) defer cleanup() reactors, _, eventSwitches, p2pSwitches := startConsensusNet(css, N) - defer stopConsensusNet(log.TestingLogger(), reactors, eventSwitches, p2pSwitches) + defer stopConsensusNet(log.NewTestingLogger(t), reactors, eventSwitches, p2pSwitches) var ( reactor = reactors[0] @@ -144,11 +152,13 @@ func TestReactorReceiveDoesNotPanicIfAddPeerHasntBeenCalledYet(t *testing.T) { } func TestReactorReceivePanicsIfInitPeerHasntBeenCalledYet(t *testing.T) { + t.Parallel() + N := 1 css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) defer cleanup() reactors, _, eventSwitches, p2pSwitches := startConsensusNet(css, N) - defer stopConsensusNet(log.TestingLogger(), reactors, eventSwitches, p2pSwitches) + defer stopConsensusNet(log.NewTestingLogger(t), reactors, eventSwitches, p2pSwitches) var ( reactor = reactors[0] @@ -166,13 +176,15 @@ func TestReactorReceivePanicsIfInitPeerHasntBeenCalledYet(t *testing.T) { // Test we record stats about votes and block parts from other peers. func TestFlappyReactorRecordsVotesAndBlockParts(t *testing.T) { + t.Parallel() + testutils.FilterStability(t, testutils.Flappy) N := 4 css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) defer cleanup() reactors, blocksSubs, eventSwitches, p2pSwitches := startConsensusNet(css, N) - defer stopConsensusNet(log.TestingLogger(), reactors, eventSwitches, p2pSwitches) + defer stopConsensusNet(log.NewTestingLogger(t), reactors, eventSwitches, p2pSwitches) // wait till everyone makes the first new block timeoutWaitGroup(t, N, func(j int) { @@ -192,8 +204,10 @@ func TestFlappyReactorRecordsVotesAndBlockParts(t *testing.T) { // ensure we can make blocks despite cycling a validator set func TestReactorVotingPowerChange(t *testing.T) { + t.Parallel() + nVals := 4 - logger := log.TestingLogger() + logger := log.NewTestingLogger(t) css, cleanup := randConsensusNet(nVals, "consensus_voting_power_changes_test", newMockTickerFunc(true), newPersistentKVStore) defer cleanup() @@ -254,12 +268,14 @@ func TestReactorVotingPowerChange(t *testing.T) { } func TestReactorValidatorSetChanges(t *testing.T) { + t.Parallel() + nPeers := 7 nVals := 4 css, _, _, cleanup := randConsensusNetWithPeers(nVals, nPeers, "consensus_val_set_changes_test", newMockTickerFunc(true), newPersistentKVStoreWithPath) defer cleanup() - logger := log.TestingLogger() + logger := log.NewTestingLogger(t) reactors, blocksSubs, eventSwitches, p2pSwitches := startConsensusNet(css, nPeers) defer stopConsensusNet(logger, reactors, eventSwitches, p2pSwitches) @@ -350,6 +366,8 @@ func TestReactorValidatorSetChanges(t *testing.T) { // Check we can make blocks with skip_timeout_commit=false func TestReactorWithTimeoutCommit(t *testing.T) { + t.Parallel() + N := 4 css, cleanup := randConsensusNet(N, "consensus_reactor_with_timeout_commit_test", newMockTickerFunc(false), newCounter) defer cleanup() @@ -359,7 +377,7 @@ func TestReactorWithTimeoutCommit(t *testing.T) { } reactors, blocksSubs, eventSwitches, p2pSwitches := startConsensusNet(css, N-1) - defer stopConsensusNet(log.TestingLogger(), reactors, eventSwitches, p2pSwitches) + defer stopConsensusNet(log.NewTestingLogger(t), reactors, eventSwitches, p2pSwitches) // wait till everyone makes the first new block timeoutWaitGroup(t, N-1, func(j int) { @@ -510,6 +528,8 @@ func timeoutWaitGroup(t *testing.T, n int, f func(int), css []*ConsensusState) { // Ensure basic validation of structs is functioning func TestNewRoundStepMessageValidateBasic(t *testing.T) { + t.Parallel() + testCases := []struct { testName string messageHeight int64 @@ -529,6 +549,8 @@ func TestNewRoundStepMessageValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + message := NewRoundStepMessage{ Height: tc.messageHeight, Round: tc.messageRound, @@ -542,6 +564,8 @@ func TestNewRoundStepMessageValidateBasic(t *testing.T) { } func TestNewValidBlockMessageValidateBasic(t *testing.T) { + t.Parallel() + testCases := []struct { malleateFn func(*NewValidBlockMessage) expErr string @@ -569,6 +593,8 @@ func TestNewValidBlockMessageValidateBasic(t *testing.T) { for i, tc := range testCases { tc := tc t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + t.Parallel() + msg := &NewValidBlockMessage{ Height: 1, Round: 0, @@ -588,6 +614,8 @@ func TestNewValidBlockMessageValidateBasic(t *testing.T) { } func TestProposalPOLMessageValidateBasic(t *testing.T) { + t.Parallel() + testCases := []struct { malleateFn func(*ProposalPOLMessage) expErr string @@ -605,6 +633,8 @@ func TestProposalPOLMessageValidateBasic(t *testing.T) { for i, tc := range testCases { tc := tc t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + t.Parallel() + msg := &ProposalPOLMessage{ Height: 1, ProposalPOLRound: 1, @@ -621,6 +651,8 @@ func TestProposalPOLMessageValidateBasic(t *testing.T) { } func TestBlockPartMessageValidateBasic(t *testing.T) { + t.Parallel() + testPart := new(types.Part) testPart.Proof.LeafHash = tmhash.Sum([]byte("leaf")) testCases := []struct { @@ -638,6 +670,8 @@ func TestBlockPartMessageValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + message := BlockPartMessage{ Height: tc.messageHeight, Round: tc.messageRound, @@ -655,6 +689,8 @@ func TestBlockPartMessageValidateBasic(t *testing.T) { } func TestHasVoteMessageValidateBasic(t *testing.T) { + t.Parallel() + const ( validSignedMsgType types.SignedMsgType = 0x01 invalidSignedMsgType types.SignedMsgType = 0x03 @@ -678,6 +714,8 @@ func TestHasVoteMessageValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + message := HasVoteMessage{ Height: tc.messageHeight, Round: tc.messageRound, @@ -691,6 +729,8 @@ func TestHasVoteMessageValidateBasic(t *testing.T) { } func TestVoteSetMaj23MessageValidateBasic(t *testing.T) { + t.Parallel() + const ( validSignedMsgType types.SignedMsgType = 0x01 invalidSignedMsgType types.SignedMsgType = 0x03 @@ -723,6 +763,8 @@ func TestVoteSetMaj23MessageValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + message := VoteSetMaj23Message{ Height: tc.messageHeight, Round: tc.messageRound, @@ -736,6 +778,8 @@ func TestVoteSetMaj23MessageValidateBasic(t *testing.T) { } func TestVoteSetBitsMessageValidateBasic(t *testing.T) { + t.Parallel() + testCases := []struct { //nolint: maligned malleateFn func(*VoteSetBitsMessage) expErr string @@ -762,6 +806,8 @@ func TestVoteSetBitsMessageValidateBasic(t *testing.T) { for i, tc := range testCases { tc := tc t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + t.Parallel() + msg := &VoteSetBitsMessage{ Height: 1, Round: 0, diff --git a/tm2/pkg/bft/consensus/replay.go b/tm2/pkg/bft/consensus/replay.go index 16b4ba3fa87..103cf67f512 100644 --- a/tm2/pkg/bft/consensus/replay.go +++ b/tm2/pkg/bft/consensus/replay.go @@ -4,11 +4,12 @@ import ( "bytes" "errors" "fmt" - "hash/crc32" "io" "reflect" "time" + "golang.org/x/exp/slog" + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" cstypes "github.com/gnolang/gno/tm2/pkg/bft/consensus/types" "github.com/gnolang/gno/tm2/pkg/bft/mempool/mock" @@ -21,8 +22,6 @@ import ( "github.com/gnolang/gno/tm2/pkg/log" ) -var crc32c = crc32.MakeTable(crc32.Castagnoli) - // Functionality to replay blocks and messages on recovery from a crash. // There are two general failure scenarios: // @@ -199,7 +198,7 @@ type Handshaker struct { store sm.BlockStore evsw events.EventSwitch genDoc *types.GenesisDoc - logger log.Logger + logger *slog.Logger nBlocks int // number of blocks applied to the state } @@ -213,12 +212,12 @@ func NewHandshaker(stateDB dbm.DB, state sm.State, store: store, evsw: events.NilEventSwitch(), genDoc: genDoc, - logger: log.NewNopLogger(), + logger: log.NewNoopLogger(), nBlocks: 0, } } -func (h *Handshaker) SetLogger(l log.Logger) { +func (h *Handshaker) SetLogger(l *slog.Logger) { h.logger = l } diff --git a/tm2/pkg/bft/consensus/replay_file.go b/tm2/pkg/bft/consensus/replay_file.go index 4daf1dbb4ab..7bfa3de9231 100644 --- a/tm2/pkg/bft/consensus/replay_file.go +++ b/tm2/pkg/bft/consensus/replay_file.go @@ -320,7 +320,7 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cnscfg.Consensu } mempool := mock.Mempool{} - blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mempool) + blockExec := sm.NewBlockExecutor(stateDB, log.NewNoopLogger(), proxyApp.Consensus(), mempool) consensusState := NewConsensusState(csConfig, state.Copy(), blockExec, blockStore, mempool) diff --git a/tm2/pkg/bft/consensus/replay_test.go b/tm2/pkg/bft/consensus/replay_test.go index 3174207ef8d..981e72d6ca3 100644 --- a/tm2/pkg/bft/consensus/replay_test.go +++ b/tm2/pkg/bft/consensus/replay_test.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "os" "path/filepath" "runtime" @@ -14,6 +13,8 @@ import ( "testing" "time" + "golang.org/x/exp/slog" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -71,7 +72,7 @@ func startNewConsensusStateAndWaitForBlock(t *testing.T, consensusReplayConfig * ) { t.Helper() - logger := log.TestingLogger() + logger := log.NewTestingLogger(t) state, _ := sm.LoadStateFromDBOrGenesisFile(stateDB, consensusReplayConfig.GenesisFile()) privValidator := loadPrivValidator(consensusReplayConfig) cs := newConsensusStateWithConfigAndBlockStore(consensusReplayConfig, state, privValidator, kvstore.NewKVStoreApplication(), blockDB) @@ -124,6 +125,8 @@ func sendTxs(ctx context.Context, cs *ConsensusState) { // TestWALCrash uses crashing WAL to test we can recover from any WAL failure. func TestWALCrash(t *testing.T) { + t.Parallel() + testCases := []struct { name string initFn func(dbm.DB, *ConsensusState, context.Context) @@ -147,6 +150,8 @@ func TestWALCrash(t *testing.T) { tc := tc consensusReplayConfig := ResetConfig(fmt.Sprintf("%s_%d", t.Name(), i)) t.Run(tc.name, func(t *testing.T) { + t.Parallel() + crashWALandCheckLiveness(t, consensusReplayConfig, tc.initFn, tc.lastBlockHeight) }) } @@ -166,7 +171,7 @@ LOOP: t.Logf("====== LOOP %d\n", i) // create consensus state from a clean slate - logger := log.NewNopLogger() + logger := log.NewTestingLogger(t) blockDB := dbm.NewMemDB() stateDB := blockDB state, _ := sm.MakeGenesisStateFromFile(consensusReplayConfig.GenesisFile()) @@ -250,7 +255,7 @@ func (e ReachedLastBlockHeightError) Error() string { return fmt.Sprintf("reached height to stop %d", e.height) } -func (w *crashingWAL) SetLogger(logger log.Logger) { +func (w *crashingWAL) SetLogger(logger *slog.Logger) { w.next.SetLogger(logger) } @@ -507,6 +512,8 @@ func makeTestSim(t *testing.T, name string) (sim testSim) { // Sync from scratch func TestHandshakeReplayAll(t *testing.T) { + t.Parallel() + for _, m := range modes { testHandshakeReplay(t, config, 0, m, nil) } @@ -519,6 +526,8 @@ func TestHandshakeReplayAll(t *testing.T) { // Sync many, not from scratch func TestHandshakeReplaySome(t *testing.T) { + t.Parallel() + for _, m := range modes { testHandshakeReplay(t, config, 1, m, nil) } @@ -531,6 +540,8 @@ func TestHandshakeReplaySome(t *testing.T) { // Sync from lagging by one func TestHandshakeReplayOne(t *testing.T) { + t.Parallel() + for _, m := range modes { testHandshakeReplay(t, config, numBlocks-1, m, nil) } @@ -543,6 +554,8 @@ func TestHandshakeReplayOne(t *testing.T) { // Sync from caught up func TestFlappyHandshakeReplayNone(t *testing.T) { + t.Parallel() + testutils.FilterStability(t, testutils.Flappy) for _, m := range modes { @@ -557,7 +570,9 @@ func TestFlappyHandshakeReplayNone(t *testing.T) { // Test mockProxyApp should not panic when app return ABCIResponses with some empty ResponseDeliverTx func TestMockProxyApp(t *testing.T) { - logger := log.TestingLogger() + t.Parallel() + + logger := log.NewTestingLogger(t) validTxs, invalidTxs := 0, 0 txIndex := 0 @@ -604,7 +619,7 @@ func TestMockProxyApp(t *testing.T) { } func tempWALWithData(data []byte) string { - walFile, err := ioutil.TempFile("", "wal") + walFile, err := os.CreateTemp("", "wal") if err != nil { panic(fmt.Sprintf("failed to create temp WAL file: %v", err)) } @@ -649,7 +664,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin wal, err := walm.NewWAL(walFile, maxMsgSize) require.NoError(t, err) - wal.SetLogger(log.TestingLogger()) + wal.SetLogger(log.NewTestingLogger(t)) err = wal.Start() require.NoError(t, err) defer wal.Stop() @@ -720,7 +735,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin func applyBlock(stateDB dbm.DB, st sm.State, blk *types.Block, proxyApp proxy.AppConns) sm.State { testPartSize := types.BlockPartSizeBytes - blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mempool) + blockExec := sm.NewBlockExecutor(stateDB, log.NewNoopLogger(), proxyApp.Consensus(), mempool) blkID := types.BlockID{Hash: blk.Hash(), PartsHeader: blk.MakePartSet(testPartSize).Header()} newState, err := blockExec.ApplyBlock(st, blkID, blk) @@ -811,6 +826,8 @@ func buildTMStateFromChain(config *cfg.Config, stateDB dbm.DB, state sm.State, c } func TestHandshakePanicsIfAppReturnsWrongAppHash(t *testing.T) { + t.Parallel() + // 1. Initialize tendermint and commit 3 blocks with the following app hashes: // - 0x01 // - 0x02 @@ -1090,6 +1107,8 @@ func (bs *mockBlockStore) LoadSeenCommit(height int64) *types.Commit { // Test handshake/init chain func TestHandshakeUpdatesValidators(t *testing.T) { + t.Parallel() + val, _ := types.RandValidator(true, 10) vals := types.NewValidatorSet([]*types.Validator{val}) app := &initChainApp{vals: vals.ABCIValidatorUpdates()} diff --git a/tm2/pkg/bft/consensus/state.go b/tm2/pkg/bft/consensus/state.go index 988a455f117..c5486bb38a4 100644 --- a/tm2/pkg/bft/consensus/state.go +++ b/tm2/pkg/bft/consensus/state.go @@ -9,6 +9,8 @@ import ( "sync" "time" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/amino" cnscfg "github.com/gnolang/gno/tm2/pkg/bft/consensus/config" cstypes "github.com/gnolang/gno/tm2/pkg/bft/consensus/types" @@ -20,7 +22,6 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/errors" "github.com/gnolang/gno/tm2/pkg/events" - "github.com/gnolang/gno/tm2/pkg/log" osm "github.com/gnolang/gno/tm2/pkg/os" "github.com/gnolang/gno/tm2/pkg/p2p" "github.com/gnolang/gno/tm2/pkg/service" @@ -184,7 +185,7 @@ func NewConsensusState( // Public interface // SetLogger implements Service. -func (cs *ConsensusState) SetLogger(l log.Logger) { +func (cs *ConsensusState) SetLogger(l *slog.Logger) { cs.BaseService.Logger = l cs.timeoutTicker.SetLogger(l) } diff --git a/tm2/pkg/bft/consensus/state_test.go b/tm2/pkg/bft/consensus/state_test.go index 1587fa03057..35877837ab3 100644 --- a/tm2/pkg/bft/consensus/state_test.go +++ b/tm2/pkg/bft/consensus/state_test.go @@ -59,10 +59,12 @@ x * TestHalt1 - if we see +2/3 precommits after timing out into new round, we sh */ -//---------------------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------------------- // ProposeSuite func TestStateProposerSelection0(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(4) height, round := cs1.Height, cs1.Round @@ -103,6 +105,8 @@ func TestStateProposerSelection0(t *testing.T) { // Now let's do it all again, but starting from round 2 instead of 0 func TestStateProposerSelection2(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(4) // test needs more work for more than 3 validators height := cs1.Height newRoundCh := subscribe(cs1.evsw, cstypes.EventNewRound{}) @@ -138,6 +142,8 @@ func TestStateProposerSelection2(t *testing.T) { // a non-validator should timeout into the prevote round func TestStateEnterProposeNoPrivValidator(t *testing.T) { + t.Parallel() + cs, _ := randConsensusState(1) cs.SetPrivValidator(nil) height, round := cs.Height, cs.Round @@ -161,6 +167,8 @@ func TestStateEnterProposeNoPrivValidator(t *testing.T) { // a validator should not timeout of the prevote round (TODO: unless the block is really big!) func TestStateEnterProposeYesPrivValidator(t *testing.T) { + t.Parallel() + cs, _ := randConsensusState(1) height, round := cs.Height, cs.Round @@ -197,6 +205,8 @@ func TestStateEnterProposeYesPrivValidator(t *testing.T) { } func TestStateBadProposal(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(2) height, round := cs1.Height, cs1.Round vs2 := vss[1] @@ -255,11 +265,13 @@ func TestStateBadProposal(t *testing.T) { signAddVotes(cs1, types.PrecommitType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2) } -//---------------------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------------------- // FullRoundSuite // propose, prevote, and precommit a block func TestStateFullRound1(t *testing.T) { + t.Parallel() + cs, vss := randConsensusState(1) height, round := cs.Height, cs.Round @@ -292,6 +304,8 @@ func TestStateFullRound1(t *testing.T) { // nil is proposed, so prevote and precommit nil func TestStateFullRoundNil(t *testing.T) { + t.Parallel() + cs, vss := randConsensusState(1) height, round := cs.Height, cs.Round cs.decideProposal = func(height int64, round int) { @@ -316,6 +330,8 @@ func TestStateFullRoundNil(t *testing.T) { // run through propose, prevote, precommit commit with two validators // where the first validator has to wait for votes from the second func TestStateFullRound2(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(2) vs2 := vss[1] height, round := cs1.Height, cs1.Round @@ -354,12 +370,14 @@ func TestStateFullRound2(t *testing.T) { ensureNewBlock(newBlockCh, height) } -//------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------ // LockSuite // two validators, 4 rounds. // two vals take turns proposing. val1 locks on first one, precommits nil on everything else func TestStateLockNoPOL(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(2) vs2 := vss[1] height, round := cs1.Height, cs1.Round @@ -409,7 +427,7 @@ func TestStateLockNoPOL(t *testing.T) { // but with invalid args. then we enterPrecommitWait, and the timeout to new round ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) - /// + // ----------- round++ // moving to the next round ensureNewRound(newRoundCh, height, round) @@ -532,6 +550,8 @@ func TestStateLockNoPOL(t *testing.T) { // 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka func TestStateLockPOLRelock(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] height, round := cs1.Height, cs1.Round @@ -627,6 +647,8 @@ func TestStateLockPOLRelock(t *testing.T) { // 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka func TestStateLockPOLUnlock(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] height, round := cs1.Height, cs1.Round @@ -727,6 +749,8 @@ func TestStateLockPOLUnlock(t *testing.T) { // then a polka at round 2 that we lock on // then we see the polka from round 1 but shouldn't unlock func TestStateLockPOLSafety1(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] height, round := cs1.Height, cs1.Round @@ -848,6 +872,8 @@ func TestStateLockPOLSafety1(t *testing.T) { // What we want: // dont see P0, lock on P1 at R1, dont unlock using P0 at R2 func TestStateLockPOLSafety2(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] height, round := cs1.Height, cs1.Round @@ -943,6 +969,8 @@ func TestStateLockPOLSafety2(t *testing.T) { // What we want: // P0 proposes B0 at R3. func TestProposeValidBlock(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] height, round := cs1.Height, cs1.Round @@ -1038,6 +1066,8 @@ func TestProposeValidBlock(t *testing.T) { // What we want: // P0 miss to lock B but set valid block to B after receiving delayed prevote. func TestSetValidBlockOnDelayedPrevote(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] height, round := cs1.Height, cs1.Round @@ -1102,6 +1132,8 @@ func TestSetValidBlockOnDelayedPrevote(t *testing.T) { // P0 miss to lock B as Proposal Block is missing, but set valid block to B after // receiving delayed Block Proposal. func TestSetValidBlockOnDelayedProposal(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] height, round := cs1.Height, cs1.Round @@ -1160,6 +1192,8 @@ func TestSetValidBlockOnDelayedProposal(t *testing.T) { // What we want: // P0 waits for timeoutPrecommit before starting next round func TestWaitingTimeoutOnNilPolka(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] height, round := cs1.Height, cs1.Round @@ -1185,6 +1219,8 @@ func TestWaitingTimeoutOnNilPolka(t *testing.T) { // What we want: // P0 waits for timeoutPropose in the next round before entering prevote func TestWaitingTimeoutProposeOnNewRound(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] height, round := cs1.Height, cs1.Round @@ -1223,6 +1259,8 @@ func TestWaitingTimeoutProposeOnNewRound(t *testing.T) { // What we want: // P0 jump to higher round, precommit and start precommit wait func TestRoundSkipOnNilPolkaFromHigherRound(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] height, round := cs1.Height, cs1.Round @@ -1261,6 +1299,8 @@ func TestRoundSkipOnNilPolkaFromHigherRound(t *testing.T) { // What we want: // P0 wait for timeoutPropose to expire before sending prevote. func TestWaitTimeoutProposeOnNilPolkaForTheCurrentRound(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] height, round := cs1.Height, 1 @@ -1290,6 +1330,8 @@ func TestWaitTimeoutProposeOnNilPolkaForTheCurrentRound(t *testing.T) { // What we want: // P0 emit NewValidBlock event upon receiving 2/3+ Precommit for B but hasn't received block B yet func TestEmitNewValidBlockEventOnCommitWithoutBlock(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] height, round := cs1.Height, 1 @@ -1327,6 +1369,8 @@ func TestEmitNewValidBlockEventOnCommitWithoutBlock(t *testing.T) { // P0 receives 2/3+ Precommit for B for round 0, while being in round 1. It emits NewValidBlock event. // After receiving block, it executes block and moves to the next height. func TestCommitFromPreviousRound(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] height, round := cs1.Height, 1 @@ -1439,6 +1483,8 @@ func TestStartNextHeightCorrectly(t *testing.T) { } func TestFlappyResetTimeoutPrecommitUponNewHeight(t *testing.T) { + t.Parallel() + testutils.FilterStability(t, testutils.Flappy) config.Consensus.SkipTimeoutCommit = false @@ -1496,12 +1542,14 @@ func TestFlappyResetTimeoutPrecommitUponNewHeight(t *testing.T) { assert.False(t, rs.TriggeredTimeoutPrecommit, "triggeredTimeoutPrecommit should be false at the beginning of each height") } -//------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------ // SlashingSuite // TODO: Slashing /* func TestStateSlashingPrevotes(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(2) vs2 := vss[1] @@ -1537,6 +1585,8 @@ func TestStateSlashingPrevotes(t *testing.T) { } func TestStateSlashingPrecommits(t *testing.T) { + t.Parallel() + cs1, vss := randConsensusState(2) vs2 := vss[1] @@ -1573,15 +1623,17 @@ func TestStateSlashingPrecommits(t *testing.T) { } */ -//------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------ // CatchupSuite -//------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------ // HaltSuite // 4 vals. // we receive a final precommit after going into next round, but others might have gone to commit already! func TestFlappyStateHalt1(t *testing.T) { + t.Parallel() + testutils.FilterStability(t, testutils.Flappy) cs1, vss := randConsensusState(4) vs2, vs3, vs4 := vss[1], vss[2], vss[3] @@ -1652,6 +1704,8 @@ func TestFlappyStateHalt1(t *testing.T) { } func TestStateOutputsBlockPartsStats(t *testing.T) { + t.Parallel() + // create dummy peer cs, _ := randConsensusState(1) peer := p2pmock.NewPeer(nil) @@ -1694,6 +1748,8 @@ func TestStateOutputsBlockPartsStats(t *testing.T) { } func TestStateOutputVoteStats(t *testing.T) { + t.Parallel() + cs, vss := randConsensusState(2) // create dummy peer peer := p2pmock.NewPeer(nil) diff --git a/tm2/pkg/bft/consensus/ticker.go b/tm2/pkg/bft/consensus/ticker.go index c4f660aae16..4e664304c92 100644 --- a/tm2/pkg/bft/consensus/ticker.go +++ b/tm2/pkg/bft/consensus/ticker.go @@ -3,7 +3,8 @@ package consensus import ( "time" - "github.com/gnolang/gno/tm2/pkg/log" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/service" ) @@ -18,7 +19,7 @@ type TimeoutTicker interface { Chan() <-chan timeoutInfo // on which to receive a timeout ScheduleTimeout(ti timeoutInfo) // reset the timer - SetLogger(log.Logger) + SetLogger(*slog.Logger) } // timeoutTicker wraps time.Timer, @@ -71,7 +72,7 @@ func (t *timeoutTicker) ScheduleTimeout(ti timeoutInfo) { t.tickChan <- ti } -//------------------------------------------------------------- +// ------------------------------------------------------------- // stop the timer and drain if necessary func (t *timeoutTicker) stopTimer() { @@ -86,7 +87,7 @@ func (t *timeoutTicker) stopTimer() { } // send on tickChan to start a new timer. -// timers are interupted and replaced by new ticks from later steps +// timers are interrupted and replaced by new ticks from later steps // timeouts of 0 on the tickChan will be immediately relayed to the tockChan func (t *timeoutTicker) timeoutRoutine() { t.Logger.Debug("Starting timeout routine") diff --git a/tm2/pkg/bft/consensus/types/cstypes.proto b/tm2/pkg/bft/consensus/types/cstypes.proto index 41a20d585c6..7fb87e71442 100644 --- a/tm2/pkg/bft/consensus/types/cstypes.proto +++ b/tm2/pkg/bft/consensus/types/cstypes.proto @@ -12,92 +12,92 @@ import "google/protobuf/timestamp.proto"; // messages message RoundState { - sint64 Height = 1; - sint64 Round = 2; - uint32 Step = 3; - google.protobuf.Timestamp StartTime = 4; - google.protobuf.Timestamp CommitTime = 5; - ValidatorSet Validators = 6; - Proposal Proposal = 7; - Block ProposalBlock = 8; - PartSet ProposalBlockParts = 9; - sint64 LockedRound = 10; - Block LockedBlock = 11; - PartSet LockedBlockParts = 12; - sint64 ValidRound = 13; - Block ValidBlock = 14; - PartSet ValidBlockParts = 15; - HeightVoteSet Votes = 16; - sint64 CommitRound = 17; - VoteSet LastCommit = 18; - ValidatorSet LastValidators = 19; - bool TriggeredTimeoutPrecommit = 20; + sint64 height = 1; + sint64 round = 2; + uint32 step = 3; + google.protobuf.Timestamp start_time = 4; + google.protobuf.Timestamp commit_time = 5; + ValidatorSet validators = 6; + Proposal proposal = 7; + Block proposal_block = 8; + PartSet proposal_block_parts = 9; + sint64 locked_round = 10; + Block locked_block = 11; + PartSet locked_block_parts = 12; + sint64 valid_round = 13; + Block valid_block = 14; + PartSet valid_block_parts = 15; + HeightVoteSet votes = 16; + sint64 commit_round = 17; + VoteSet last_commit = 18; + ValidatorSet last_validators = 19; + bool triggered_timeout_precommit = 20; } message HRS { - sint64 Height = 1; - sint64 Round = 2; - uint32 Step = 3; + sint64 height = 1; + sint64 round = 2; + uint32 step = 3; } message RoundStateSimple { - string HeightRoundStep = 1; - google.protobuf.Timestamp StartTime = 2; - bytes ProposalBlockHash = 3; - bytes LockedBlockHash = 4; - bytes ValidBlockHash = 5; - HeightVoteSet Votes = 6; + string height_round_step = 1 [json_name = "height/round/step"]; + google.protobuf.Timestamp start_time = 2; + bytes proposal_block_hash = 3; + bytes locked_block_hash = 4; + bytes valid_block_hash = 5; + HeightVoteSet votes = 6 [json_name = "height_vote_set"]; } message PeerRoundState { - sint64 Height = 1; - sint64 Round = 2; - uint32 Step = 3; - google.protobuf.Timestamp StartTime = 4; - bool Proposal = 5; - PartSetHeader ProposalBlockPartsHeader = 6; - BitArray ProposalBlockParts = 7; - sint64 ProposalPOLRound = 8; - BitArray ProposalPOL = 9; - BitArray Prevotes = 10; - BitArray Precommits = 11; - sint64 LastCommitRound = 12; - BitArray LastCommit = 13; - sint64 CatchupCommitRound = 14; - BitArray CatchupCommit = 15; + sint64 height = 1; + sint64 round = 2; + uint32 step = 3; + google.protobuf.Timestamp start_time = 4; + bool proposal = 5; + PartSetHeader proposal_block_parts_header = 6; + BitArray proposal_block_parts = 7; + sint64 proposal_pol_round = 8; + BitArray proposal_pol = 9; + BitArray prevotes = 10; + BitArray precommits = 11; + sint64 last_commit_round = 12; + BitArray last_commit = 13; + sint64 catchup_commit_round = 14; + BitArray catchup_commit = 15; } message HeightVoteSet { } message EventNewRoundStep { - HRS HRS = 1; - sint64 SecondsSinceStartTime = 2; - sint64 LastCommitRound = 3; + HRS hrs = 1; + sint64 seconds_since_start_time = 2 [json_name = "SecondsSinceStartTime"]; + sint64 last_commit_round = 3 [json_name = "LastCommitRound"]; } message EventNewValidBlock { - HRS HRS = 1; - PartSetHeader BlockPartsHeader = 2; - BitArray BlockParts = 3; - bool IsCommit = 4; + HRS hrs = 1; + PartSetHeader block_parts_header = 2; + BitArray block_parts = 3; + bool is_commit = 4; } message EventNewRound { - HRS HRS = 1; - Validator Proposer = 2; - sint64 ProposerIndex = 3; + HRS hrs = 1; + Validator proposer = 2; + sint64 proposer_index = 3; } message EventCompleteProposal { - HRS HRS = 1; - BlockID BlockID = 2; + HRS hrs = 1; + BlockID block_id = 2; } message EventTimeoutPropose { - HRS HRS = 1; + HRS hrs = 1; } message EventTimeoutWait { - HRS HRS = 1; + HRS hrs = 1; } \ No newline at end of file diff --git a/tm2/pkg/bft/consensus/types/height_vote_set_test.go b/tm2/pkg/bft/consensus/types/height_vote_set_test.go index 86dc4ee84f6..4c315585daa 100644 --- a/tm2/pkg/bft/consensus/types/height_vote_set_test.go +++ b/tm2/pkg/bft/consensus/types/height_vote_set_test.go @@ -21,6 +21,8 @@ func TestMain(m *testing.M) { } func TestPeerCatchupRounds(t *testing.T) { + t.Parallel() + valSet, privVals := types.RandValidatorSet(10, 1) hvs := NewHeightVoteSet(config.ChainID(), 1, valSet) diff --git a/tm2/pkg/bft/consensus/wal_generator.go b/tm2/pkg/bft/consensus/wal_generator.go index dff1cca1446..8a737452425 100644 --- a/tm2/pkg/bft/consensus/wal_generator.go +++ b/tm2/pkg/bft/consensus/wal_generator.go @@ -9,6 +9,8 @@ import ( "testing" "time" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/bft/abci/example/kvstore" cfg "github.com/gnolang/gno/tm2/pkg/bft/config" "github.com/gnolang/gno/tm2/pkg/bft/mempool/mock" @@ -37,10 +39,10 @@ func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) { app := kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), "wal_generator")) defer app.Close() - logger := log.TestingLogger().With("wal_generator", "wal_generator") + logger := log.NewNoopLogger().With("wal_generator", "wal_generator") logger.Info("generating WAL (last height msg excluded)", "numBlocks", numBlocks) - ///////////////////////////////////////////////////////////////////////////// + // ----------- // COPY PASTE FROM node.go WITH A FEW MODIFICATIONS // NOTE: we can't import node package because of circular dependency. // NOTE: we don't do handshake so need to set state.Version.Consensus.App directly. @@ -75,7 +77,7 @@ func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) { } defer evsw.Stop() mempool := mock.Mempool{} - blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mempool) + blockExec := sm.NewBlockExecutor(stateDB, log.NewNoopLogger(), proxyApp.Consensus(), mempool) consensusState := NewConsensusState(config.Consensus, state.Copy(), blockExec, blockStore, mempool) consensusState.SetLogger(logger) consensusState.SetEventSwitch(evsw) @@ -83,7 +85,7 @@ func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) { consensusState.SetPrivValidator(privValidator) } // END OF COPY PASTE - ///////////////////////////////////////////////////////////////////////////// + // ----------- // set consensus wal to buffered WAL, which will write all incoming msgs to buffer numBlocksWritten := make(chan struct{}) @@ -159,13 +161,13 @@ type heightStopWAL struct { heightToStop int64 signalWhenStopsTo chan<- struct{} - logger log.Logger + logger *slog.Logger } // needed for determinism var fixedTime, _ = time.Parse(time.RFC3339, "2017-01-02T15:04:05Z") -func newHeightStopWAL(logger log.Logger, enc *walm.WALWriter, nBlocks int64, signalStop chan<- struct{}) *heightStopWAL { +func newHeightStopWAL(logger *slog.Logger, enc *walm.WALWriter, nBlocks int64, signalStop chan<- struct{}) *heightStopWAL { return &heightStopWAL{ enc: enc, heightToStop: nBlocks, @@ -174,7 +176,7 @@ func newHeightStopWAL(logger log.Logger, enc *walm.WALWriter, nBlocks int64, sig } } -func (w *heightStopWAL) SetLogger(logger log.Logger) { +func (w *heightStopWAL) SetLogger(logger *slog.Logger) { w.logger = logger } diff --git a/tm2/pkg/bft/consensus/wal_test.go b/tm2/pkg/bft/consensus/wal_test.go index 1b271fb73c7..b5d49dd6354 100644 --- a/tm2/pkg/bft/consensus/wal_test.go +++ b/tm2/pkg/bft/consensus/wal_test.go @@ -13,10 +13,6 @@ import ( "github.com/gnolang/gno/tm2/pkg/log" ) -const ( - walTestFlushInterval = time.Duration(100) * time.Millisecond -) - // ---------------------------------------- // copied over from wal/wal_test.go @@ -53,11 +49,12 @@ func makeTempWAL(t *testing.T, walChunkSize int64) (wal walm.WAL) { // ---------------------------------------- func TestWALTruncate(t *testing.T) { - const maxTestMsgSize = 1024 * 1024 // 1MB - const walChunkSize = 409610 // 4KB + t.Parallel() + + const walChunkSize = 409610 // 4KB wal := makeTempWAL(t, walChunkSize) - wal.SetLogger(log.TestingLogger()) + wal.SetLogger(log.NewTestingLogger(t)) type grouper interface { Group() *auto.Group diff --git a/tm2/pkg/bft/mempool/cache_test.go b/tm2/pkg/bft/mempool/cache_test.go index 5f76d6875ea..2d523db37ae 100644 --- a/tm2/pkg/bft/mempool/cache_test.go +++ b/tm2/pkg/bft/mempool/cache_test.go @@ -13,6 +13,8 @@ import ( ) func TestCacheRemove(t *testing.T) { + t.Parallel() + cache := newMapTxCache(100) numTxs := 10 txs := make([][]byte, numTxs) @@ -35,6 +37,8 @@ func TestCacheRemove(t *testing.T) { } func TestCacheAfterUpdate(t *testing.T) { + t.Parallel() + app := kvstore.NewKVStoreApplication() cc := proxy.NewLocalClientCreator(app) mempool, cleanup := newMempoolWithApp(cc) diff --git a/tm2/pkg/bft/mempool/clist_mempool.go b/tm2/pkg/bft/mempool/clist_mempool.go index 14fc52e19b2..da0a2c22c11 100644 --- a/tm2/pkg/bft/mempool/clist_mempool.go +++ b/tm2/pkg/bft/mempool/clist_mempool.go @@ -9,6 +9,8 @@ import ( "sync/atomic" "time" + "golang.org/x/exp/slog" + auto "github.com/gnolang/gno/tm2/pkg/autofile" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" cfg "github.com/gnolang/gno/tm2/pkg/bft/mempool/config" @@ -63,7 +65,7 @@ type CListMempool struct { // A log of mempool txs wal *auto.AutoFile - logger log.Logger + logger *slog.Logger } var _ Mempool = &CListMempool{} @@ -91,7 +93,7 @@ func NewCListMempool( rechecking: 0, recheckCursor: nil, recheckEnd: nil, - logger: log.NewNopLogger(), + logger: log.NewNoopLogger(), } if config.CacheSize > 0 { mempool.cache = newMapTxCache(config.CacheSize) @@ -111,7 +113,7 @@ func (mem *CListMempool) EnableTxsAvailable() { } // SetLogger sets the Logger. -func (mem *CListMempool) SetLogger(l log.Logger) { +func (mem *CListMempool) SetLogger(l *slog.Logger) { mem.logger = l } diff --git a/tm2/pkg/bft/mempool/clist_mempool_test.go b/tm2/pkg/bft/mempool/clist_mempool_test.go index 60582f1922d..4450dc3b764 100644 --- a/tm2/pkg/bft/mempool/clist_mempool_test.go +++ b/tm2/pkg/bft/mempool/clist_mempool_test.go @@ -35,13 +35,13 @@ func newMempoolWithApp(cc proxy.ClientCreator) (*CListMempool, cleanupFunc) { func newMempoolWithAppAndConfig(cc proxy.ClientCreator, config *cfg.MempoolConfig) (*CListMempool, cleanupFunc) { appConnMem, _ := cc.NewABCIClient() - appConnMem.SetLogger(log.TestingLogger().With("module", "abci-client", "connection", "mempool")) + appConnMem.SetLogger(log.NewNoopLogger().With("module", "abci-client", "connection", "mempool")) err := appConnMem.Start() if err != nil { panic(err) } mempool := NewCListMempool(config, appConnMem, 0, testMaxTxBytes) - mempool.SetLogger(log.TestingLogger()) + mempool.SetLogger(log.NewNoopLogger()) return mempool, func() { if config.RootDir != "" { os.RemoveAll(config.RootDir) @@ -269,7 +269,7 @@ func TestSerialReap(t *testing.T) { defer cleanup() appConnCon, _ := cc.NewABCIClient() - appConnCon.SetLogger(log.TestingLogger().With("module", "abci-client", "connection", "consensus")) + appConnCon.SetLogger(log.NewTestingLogger(t).With("module", "abci-client", "connection", "consensus")) err := appConnCon.Start() require.Nil(t, err) @@ -509,7 +509,7 @@ func TestMempoolMaxPendingTxsBytes(t *testing.T) { assert.EqualValues(t, 8, mempool.TxsBytes()) appConnCon, _ := cc.NewABCIClient() - appConnCon.SetLogger(log.TestingLogger().With("module", "abci-client", "connection", "consensus")) + appConnCon.SetLogger(log.NewTestingLogger(t).With("module", "abci-client", "connection", "consensus")) err = appConnCon.Start() require.Nil(t, err) defer appConnCon.Stop() diff --git a/tm2/pkg/bft/mempool/config/config.go b/tm2/pkg/bft/mempool/config/config.go index 06e8d98672f..198b34291bc 100644 --- a/tm2/pkg/bft/mempool/config/config.go +++ b/tm2/pkg/bft/mempool/config/config.go @@ -2,7 +2,7 @@ package config import "github.com/gnolang/gno/tm2/pkg/errors" -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // MempoolConfig // MempoolConfig defines the configuration options for the Tendermint mempool @@ -11,9 +11,9 @@ type MempoolConfig struct { Recheck bool `toml:"recheck"` Broadcast bool `toml:"broadcast"` WalPath string `toml:"wal_dir"` - Size int `toml:"size"` - MaxPendingTxsBytes int64 `toml:"max_pending_txs_bytes"` - CacheSize int `toml:"cache_size"` + Size int `toml:"size" comment:"Maximum number of transactions in the mempool"` + MaxPendingTxsBytes int64 `toml:"max_pending_txs_bytes" comment:"Limit the total size of all txs in the mempool.\n This only accounts for raw transactions (e.g. given 1MB transactions and\n max_txs_bytes=5MB, mempool will only accept 5 transactions)."` + CacheSize int `toml:"cache_size" comment:"Size of the cache (used to filter transactions we saw earlier) in transactions"` } // DefaultMempoolConfig returns a default configuration for the Tendermint mempool diff --git a/tm2/pkg/bft/mempool/mempool.proto b/tm2/pkg/bft/mempool/mempool.proto index 7eb0a7c8ce5..d85de7add85 100644 --- a/tm2/pkg/bft/mempool/mempool.proto +++ b/tm2/pkg/bft/mempool/mempool.proto @@ -5,5 +5,5 @@ option go_package = "github.com/gnolang/gno/tm2/pkg/bft/mempool/pb"; // messages message TxMessage { - bytes Tx = 1; + bytes tx = 1 [json_name = "Tx"]; } \ No newline at end of file diff --git a/tm2/pkg/bft/mempool/reactor.go b/tm2/pkg/bft/mempool/reactor.go index f6ee67b3fc4..7147c11169b 100644 --- a/tm2/pkg/bft/mempool/reactor.go +++ b/tm2/pkg/bft/mempool/reactor.go @@ -7,11 +7,12 @@ import ( "sync" "time" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/amino" cfg "github.com/gnolang/gno/tm2/pkg/bft/mempool/config" "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/clist" - "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/p2p" ) @@ -112,7 +113,7 @@ func NewReactor(config *cfg.MempoolConfig, mempool *CListMempool) *Reactor { } // SetLogger sets the Logger on the reactor and the underlying mempool. -func (memR *Reactor) SetLogger(l log.Logger) { +func (memR *Reactor) SetLogger(l *slog.Logger) { memR.Logger = l memR.mempool.SetLogger(l) } @@ -248,7 +249,7 @@ func (memR *Reactor) broadcastTxRoutine(peer p2p.Peer) { } } -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // Messages // MempoolMessage is a message sent or received by the Reactor. @@ -259,7 +260,7 @@ func (memR *Reactor) decodeMsg(bz []byte) (msg MempoolMessage, err error) { return } -//------------------------------------- +// ------------------------------------- // TxMessage is a MempoolMessage containing a transaction. type TxMessage struct { diff --git a/tm2/pkg/bft/mempool/reactor_test.go b/tm2/pkg/bft/mempool/reactor_test.go index c3b77fb46df..e7a3c43a6b9 100644 --- a/tm2/pkg/bft/mempool/reactor_test.go +++ b/tm2/pkg/bft/mempool/reactor_test.go @@ -13,7 +13,6 @@ import ( memcfg "github.com/gnolang/gno/tm2/pkg/bft/mempool/config" "github.com/gnolang/gno/tm2/pkg/bft/proxy" "github.com/gnolang/gno/tm2/pkg/bft/types" - "github.com/gnolang/gno/tm2/pkg/colors" "github.com/gnolang/gno/tm2/pkg/errors" "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/p2p" @@ -30,43 +29,10 @@ func (ps peerState) GetHeight() int64 { return ps.height } -// mempoolLogger is a TestingLogger which uses a different -// color for each validator ("validator" key must exist). -func mempoolLogger() log.Logger { - return log.TestingLoggerWithColorFn(func(keyvals ...interface{}) colors.Color { - for i := 0; i < len(keyvals)-1; i += 2 { - if keyvals[i] == "validator" { - num := keyvals[i+1].(int) - switch num % 8 { - case 0: - return colors.Red - case 1: - return colors.Green - case 2: - return colors.Yellow - case 3: - return colors.Blue - case 4: - return colors.Magenta - case 5: - return colors.Cyan - case 6: - return colors.White - case 7: - return colors.Gray - default: - panic("should not happen") - } - } - } - return colors.None - }) -} - // connect N mempool reactors through N switches func makeAndConnectReactors(mconfig *memcfg.MempoolConfig, pconfig *p2pcfg.P2PConfig, n int) []*Reactor { reactors := make([]*Reactor, n) - logger := mempoolLogger() + logger := log.NewNoopLogger() for i := 0; i < n; i++ { app := kvstore.NewKVStoreApplication() cc := proxy.NewLocalClientCreator(app) @@ -140,6 +106,8 @@ const ( ) func TestReactorBroadcastTxMessage(t *testing.T) { + t.Parallel() + mconfig := memcfg.TestMempoolConfig() pconfig := p2pcfg.TestP2PConfig() const N = 4 @@ -162,6 +130,8 @@ func TestReactorBroadcastTxMessage(t *testing.T) { } func TestReactorNoBroadcastToSender(t *testing.T) { + t.Parallel() + mconfig := memcfg.TestMempoolConfig() pconfig := p2pcfg.TestP2PConfig() const N = 2 @@ -179,6 +149,8 @@ func TestReactorNoBroadcastToSender(t *testing.T) { } func TestFlappyBroadcastTxForPeerStopsWhenPeerStops(t *testing.T) { + t.Parallel() + testutils.FilterStability(t, testutils.Flappy) if testing.Short() { @@ -205,6 +177,8 @@ func TestFlappyBroadcastTxForPeerStopsWhenPeerStops(t *testing.T) { } func TestFlappyBroadcastTxForPeerStopsWhenReactorStops(t *testing.T) { + t.Parallel() + testutils.FilterStability(t, testutils.Flappy) if testing.Short() { @@ -227,6 +201,8 @@ func TestFlappyBroadcastTxForPeerStopsWhenReactorStops(t *testing.T) { } func TestMempoolIDsBasic(t *testing.T) { + t.Parallel() + ids := newMempoolIDs() peer := mock.NewPeer(net.IP{127, 0, 0, 1}) @@ -241,6 +217,8 @@ func TestMempoolIDsBasic(t *testing.T) { } func TestMempoolIDsPanicsIfNodeRequestsOvermaxActiveIDs(t *testing.T) { + t.Parallel() + if testing.Short() { return } diff --git a/tm2/pkg/bft/node/node.go b/tm2/pkg/bft/node/node.go index 23b42cec6b9..57cc18b8ad7 100644 --- a/tm2/pkg/bft/node/node.go +++ b/tm2/pkg/bft/node/node.go @@ -9,8 +9,12 @@ import ( "net/http" _ "net/http/pprof" //nolint:gosec "strings" + "sync" "time" + "golang.org/x/exp/slog" + + "github.com/gnolang/gno/tm2/pkg/bft/state/eventstore/file" "github.com/rs/cors" "github.com/gnolang/gno/tm2/pkg/amino" @@ -25,8 +29,8 @@ import ( _ "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" rpcserver "github.com/gnolang/gno/tm2/pkg/bft/rpc/lib/server" sm "github.com/gnolang/gno/tm2/pkg/bft/state" - "github.com/gnolang/gno/tm2/pkg/bft/state/txindex" - "github.com/gnolang/gno/tm2/pkg/bft/state/txindex/null" + "github.com/gnolang/gno/tm2/pkg/bft/state/eventstore" + "github.com/gnolang/gno/tm2/pkg/bft/state/eventstore/null" "github.com/gnolang/gno/tm2/pkg/bft/store" "github.com/gnolang/gno/tm2/pkg/bft/types" tmtime "github.com/gnolang/gno/tm2/pkg/bft/types/time" @@ -35,7 +39,6 @@ import ( dbm "github.com/gnolang/gno/tm2/pkg/db" "github.com/gnolang/gno/tm2/pkg/errors" "github.com/gnolang/gno/tm2/pkg/events" - "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/p2p" "github.com/gnolang/gno/tm2/pkg/service" verset "github.com/gnolang/gno/tm2/pkg/versionset" @@ -73,12 +76,12 @@ func DefaultGenesisDocProviderFunc(config *cfg.Config) GenesisDocProvider { } // NodeProvider takes a config and a logger and returns a ready to go Node. -type NodeProvider func(*cfg.Config, log.Logger) (*Node, error) +type NodeProvider func(*cfg.Config, *slog.Logger) (*Node, error) // DefaultNewNode returns a Tendermint node with default settings for the // PrivValidator, ClientCreator, GenesisDoc, and DBProvider. // It implements NodeProvider. -func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) { +func DefaultNewNode(config *cfg.Config, logger *slog.Logger) (*Node, error) { // Generate node PrivKey nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) if err != nil { @@ -154,18 +157,19 @@ type Node struct { isListening bool // services - evsw events.EventSwitch - stateDB dbm.DB - blockStore *store.BlockStore // store the blockchain to disk - bcReactor p2p.Reactor // for fast-syncing - mempoolReactor *mempl.Reactor // for gossipping transactions - mempool mempl.Mempool - consensusState *cs.ConsensusState // latest consensus state - consensusReactor *cs.ConsensusReactor // for participating in the consensus - proxyApp proxy.AppConns // connection to the application - rpcListeners []net.Listener // rpc servers - txIndexer txindex.TxIndexer - indexerService *txindex.IndexerService + evsw events.EventSwitch + stateDB dbm.DB + blockStore *store.BlockStore // store the blockchain to disk + bcReactor p2p.Reactor // for fast-syncing + mempoolReactor *mempl.Reactor // for gossipping transactions + mempool mempl.Mempool + consensusState *cs.ConsensusState // latest consensus state + consensusReactor *cs.ConsensusReactor // for participating in the consensus + proxyApp proxy.AppConns // connection to the application + rpcListeners []net.Listener // rpc servers + txEventStore eventstore.TxEventStore + eventStoreService *eventstore.Service + firstBlockSignal <-chan struct{} } func initDBs(config *cfg.Config, dbProvider DBProvider) (blockStore *store.BlockStore, stateDB dbm.DB, err error) { @@ -184,7 +188,7 @@ func initDBs(config *cfg.Config, dbProvider DBProvider) (blockStore *store.Block return } -func createAndStartProxyAppConns(clientCreator proxy.ClientCreator, logger log.Logger) (proxy.AppConns, error) { +func createAndStartProxyAppConns(clientCreator proxy.ClientCreator, logger *slog.Logger) (proxy.AppConns, error) { proxyApp := proxy.NewAppConns(clientCreator) proxyApp.SetLogger(logger.With("module", "proxy")) if err := proxyApp.Start(); err != nil { @@ -193,40 +197,40 @@ func createAndStartProxyAppConns(clientCreator proxy.ClientCreator, logger log.L return proxyApp, nil } -func createAndStartIndexerService(config *cfg.Config, dbProvider DBProvider, - evsw events.EventSwitch, logger log.Logger, -) (*txindex.IndexerService, txindex.TxIndexer, error) { - var txIndexer txindex.TxIndexer = &null.TxIndex{} - /* - switch config.TxIndex.Indexer { - case "kv": - store, err := dbProvider(&DBContext{"tx_index", config}) - if err != nil { - return nil, nil, err - } - switch { - case config.TxIndex.IndexTags != "": - txIndexer = kv.NewTxIndex(store, kv.IndexTags(splitAndTrimEmpty(config.TxIndex.IndexTags, ",", " "))) - case config.TxIndex.IndexAllTags: - txIndexer = kv.NewTxIndex(store, kv.IndexAllTags()) - default: - txIndexer = kv.NewTxIndex(store) - } - default: - txIndexer = &null.TxIndex{} +func createAndStartEventStoreService( + cfg *cfg.Config, + evsw events.EventSwitch, + logger *slog.Logger, +) (*eventstore.Service, eventstore.TxEventStore, error) { + var ( + err error + txEventStore eventstore.TxEventStore + ) + + // Instantiate the event store based on the configuration + switch cfg.TxEventStore.EventStoreType { + case file.EventStoreType: + // Transaction events should be logged to files + txEventStore, err = file.NewTxEventStore(cfg.TxEventStore) + if err != nil { + return nil, nil, fmt.Errorf("unable to create file tx event store, %w", err) } - */ + default: + // Transaction event storing should be omitted + txEventStore = null.NewNullEventStore() + } - indexerService := txindex.NewIndexerService(txIndexer, evsw) - indexerService.SetLogger(logger.With("module", "txindex")) + indexerService := eventstore.NewEventStoreService(txEventStore, evsw) + indexerService.SetLogger(logger.With("module", "eventstore")) if err := indexerService.Start(); err != nil { return nil, nil, err } - return indexerService, txIndexer, nil + + return indexerService, txEventStore, nil } func doHandshake(stateDB dbm.DB, state sm.State, blockStore sm.BlockStore, - genDoc *types.GenesisDoc, evsw events.EventSwitch, proxyApp proxy.AppConns, consensusLogger log.Logger, + genDoc *types.GenesisDoc, evsw events.EventSwitch, proxyApp proxy.AppConns, consensusLogger *slog.Logger, ) error { handshaker := cs.NewHandshaker(stateDB, state, blockStore, genDoc) handshaker.SetLogger(consensusLogger) @@ -237,7 +241,7 @@ func doHandshake(stateDB dbm.DB, state sm.State, blockStore sm.BlockStore, return nil } -func logNodeStartupInfo(state sm.State, pubKey crypto.PubKey, logger, consensusLogger log.Logger) { +func logNodeStartupInfo(state sm.State, pubKey crypto.PubKey, logger, consensusLogger *slog.Logger) { // Log the version info. logger.Info("Version info", "version", version.Version, @@ -261,7 +265,7 @@ func onlyValidatorIsUs(state sm.State, privVal types.PrivValidator) bool { } func createMempoolAndMempoolReactor(config *cfg.Config, proxyApp proxy.AppConns, - state sm.State, logger log.Logger, + state sm.State, logger *slog.Logger, ) (*mempl.Reactor, *mempl.CListMempool) { mempool := mempl.NewCListMempool( config.Mempool, @@ -285,7 +289,7 @@ func createBlockchainReactor(config *cfg.Config, blockExec *sm.BlockExecutor, blockStore *store.BlockStore, fastSync bool, - logger log.Logger, + logger *slog.Logger, ) (bcReactor p2p.Reactor, err error) { bcReactor = bc.NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync) @@ -301,7 +305,7 @@ func createConsensusReactor(config *cfg.Config, privValidator types.PrivValidator, fastSync bool, evsw events.EventSwitch, - consensusLogger log.Logger, + consensusLogger *slog.Logger, ) (*cs.ConsensusReactor, *cs.ConsensusState) { consensusState := cs.NewConsensusState( config.Consensus, @@ -386,7 +390,7 @@ func createSwitch(config *cfg.Config, consensusReactor *cs.ConsensusReactor, nodeInfo p2p.NodeInfo, nodeKey *p2p.NodeKey, - p2pLogger log.Logger, + p2pLogger *slog.Logger, ) *p2p.Switch { sw := p2p.NewSwitch( config.P2P, @@ -412,7 +416,7 @@ func NewNode(config *cfg.Config, clientCreator proxy.ClientCreator, genesisDocProvider GenesisDocProvider, dbProvider DBProvider, - logger log.Logger, + logger *slog.Logger, options ...Option, ) (*Node, error) { blockStore, stateDB, err := initDBs(config, dbProvider) @@ -431,14 +435,28 @@ func NewNode(config *cfg.Config, return nil, err } - // EventSwitch and IndexerService must be started before the handshake because - // we might need to index the txs of the replayed block as this might not have happened + // EventSwitch and EventStoreService must be started before the handshake because + // we might need to store the txs of the replayed block as this might not have happened // when the node stopped last time (i.e. the node stopped after it saved the block // but before it indexed the txs, or, endblocker panicked) evsw := events.NewEventSwitch() - // Transaction indexing - indexerService, txIndexer, err := createAndStartIndexerService(config, dbProvider, evsw, logger) + // Signal readiness when receiving the first block. + const readinessListenerID = "first_block_listener" + + cFirstBlock := make(chan struct{}) + var once sync.Once + evsw.AddListener(readinessListenerID, func(ev events.Event) { + if _, ok := ev.(types.EventNewBlock); ok { + once.Do(func() { + close(cFirstBlock) + evsw.RemoveListener(readinessListenerID) + }) + } + }) + + // Transaction event storing + eventStoreService, txEventStore, err := createAndStartEventStoreService(config, evsw, logger) if err != nil { return nil, err } @@ -500,7 +518,7 @@ func NewNode(config *cfg.Config, privValidator, fastSync, evsw, consensusLogger, ) - nodeInfo, err := makeNodeInfo(config, nodeKey, txIndexer, genDoc, state) + nodeInfo, err := makeNodeInfo(config, nodeKey, txEventStore, genDoc, state) if err != nil { return nil, errors.Wrap(err, "error making NodeInfo") } @@ -541,17 +559,18 @@ func NewNode(config *cfg.Config, nodeInfo: nodeInfo, nodeKey: nodeKey, - evsw: evsw, - stateDB: stateDB, - blockStore: blockStore, - bcReactor: bcReactor, - mempoolReactor: mempoolReactor, - mempool: mempool, - consensusState: consensusState, - consensusReactor: consensusReactor, - proxyApp: proxyApp, - txIndexer: txIndexer, - indexerService: indexerService, + evsw: evsw, + stateDB: stateDB, + blockStore: blockStore, + bcReactor: bcReactor, + mempoolReactor: mempoolReactor, + mempool: mempool, + consensusState: consensusState, + consensusReactor: consensusReactor, + proxyApp: proxyApp, + txEventStore: txEventStore, + eventStoreService: eventStoreService, + firstBlockSignal: cFirstBlock, } node.BaseService = *service.NewBaseService(logger, "Node", node) @@ -626,7 +645,7 @@ func (n *Node) OnStop() { // first stop the non-reactor services n.evsw.Stop() - n.indexerService.Stop() + n.eventStoreService.Stop() // now stop the reactors n.sw.Stop() @@ -651,6 +670,11 @@ func (n *Node) OnStop() { } } +// Ready signals that the node is ready by returning a blocking channel. This channel is closed when the node receives its first block. +func (n *Node) Ready() <-chan struct{} { + return n.firstBlockSignal +} + // ConfigureRPC sets all variables in rpccore so they will serve // rpc calls from this node func (n *Node) ConfigureRPC() { @@ -664,7 +688,6 @@ func (n *Node) ConfigureRPC() { rpccore.SetPubKey(pubKey) rpccore.SetGenesisDoc(n.genesisDoc) rpccore.SetProxyAppQuery(n.proxyApp.Query()) - rpccore.SetTxIndexer(n.txIndexer) rpccore.SetConsensusReactor(n.consensusReactor) rpccore.SetLogger(n.Logger.With("module", "rpc")) rpccore.SetEventSwitch(n.evsw) @@ -839,15 +862,13 @@ func (n *Node) NodeInfo() p2p.NodeInfo { func makeNodeInfo( config *cfg.Config, nodeKey *p2p.NodeKey, - txIndexer txindex.TxIndexer, + txEventStore eventstore.TxEventStore, genDoc *types.GenesisDoc, state sm.State, ) (p2p.NodeInfo, error) { - txIndexerStatus := "on" - if _, ok := txIndexer.(*null.TxIndex); ok { - txIndexerStatus = "off" - } else if txIndexer == nil { - txIndexerStatus = "none" + txIndexerStatus := eventstore.StatusOff + if txEventStore.GetType() != null.EventStoreType { + txIndexerStatus = eventstore.StatusOn } bcChannel := bc.BlockchainChannel @@ -939,7 +960,7 @@ func saveGenesisDoc(db dbm.DB, genDoc *types.GenesisDoc) { func createAndStartPrivValidatorSocketClient( listenAddr string, - logger log.Logger, + logger *slog.Logger, ) (types.PrivValidator, error) { pve, err := privval.NewSignerListener(listenAddr, logger) if err != nil { diff --git a/tm2/pkg/bft/node/node_test.go b/tm2/pkg/bft/node/node_test.go index 2352df49762..3057a41b5f3 100644 --- a/tm2/pkg/bft/node/node_test.go +++ b/tm2/pkg/bft/node/node_test.go @@ -33,7 +33,7 @@ func TestNodeStartStop(t *testing.T) { defer os.RemoveAll(config.RootDir) // create & start node - n, err := DefaultNewNode(config, log.TestingLogger()) + n, err := DefaultNewNode(config, log.NewNoopLogger()) require.NoError(t, err) err = n.Start() require.NoError(t, err) @@ -96,7 +96,7 @@ func TestNodeDelayedStart(t *testing.T) { now := tmtime.Now() // create & start node - n, err := DefaultNewNode(config, log.TestingLogger()) + n, err := DefaultNewNode(config, log.NewTestingLogger(t)) n.GenesisDoc().GenesisTime = now.Add(2 * time.Second) require.NoError(t, err) @@ -108,12 +108,45 @@ func TestNodeDelayedStart(t *testing.T) { assert.Equal(t, true, startTime.After(n.GenesisDoc().GenesisTime)) } +func TestNodeReady(t *testing.T) { + config := cfg.ResetTestRoot("node_node_test") + defer os.RemoveAll(config.RootDir) + + // Create & start node + n, err := DefaultNewNode(config, log.NewTestingLogger(t)) + require.NoError(t, err) + + // Assert that blockstore has zero block before waiting for the first block + require.Equal(t, int64(0), n.BlockStore().Height()) + + // Assert that first block signal is not alreay received by calling Ready + select { + case <-n.Ready(): + require.FailNow(t, "first block signal should not be close before starting the node") + default: // ok + } + + err = n.Start() + require.NoError(t, err) + defer n.Stop() + + // Wait until the node is ready or timeout + select { + case <-time.After(time.Second): + require.FailNow(t, "timeout while waiting for first block signal") + case <-n.Ready(): // ready + } + + // Check that blockstore have at last one block + require.GreaterOrEqual(t, n.BlockStore().Height(), int64(1)) +} + func TestNodeSetAppVersion(t *testing.T) { config := cfg.ResetTestRoot("node_app_version_test") defer os.RemoveAll(config.RootDir) // create & start node - n, err := DefaultNewNode(config, log.TestingLogger()) + n, err := DefaultNewNode(config, log.NewTestingLogger(t)) require.NoError(t, err) // default config uses the kvstore app @@ -138,7 +171,7 @@ func TestNodeSetPrivValTCP(t *testing.T) { dialer := privval.DialTCPFn(addr, 100*time.Millisecond, ed25519.GenPrivKey()) dialerEndpoint := privval.NewSignerDialerEndpoint( - log.TestingLogger(), + log.NewTestingLogger(t), dialer, ) privval.SignerDialerEndpointTimeoutReadWrite(100 * time.Millisecond)(dialerEndpoint) @@ -157,7 +190,7 @@ func TestNodeSetPrivValTCP(t *testing.T) { }() defer signerServer.Stop() - n, err := DefaultNewNode(config, log.TestingLogger()) + n, err := DefaultNewNode(config, log.NewTestingLogger(t)) require.NoError(t, err) assert.IsType(t, &privval.SignerClient{}, n.PrivValidator()) } @@ -170,7 +203,7 @@ func TestPrivValidatorListenAddrNoProtocol(t *testing.T) { defer os.RemoveAll(config.RootDir) config.BaseConfig.PrivValidatorListenAddr = addrNoPrefix - _, err := DefaultNewNode(config, log.TestingLogger()) + _, err := DefaultNewNode(config, log.NewTestingLogger(t)) assert.Error(t, err) } @@ -184,7 +217,7 @@ func TestNodeSetPrivValIPC(t *testing.T) { dialer := privval.DialUnixFn(tmpfile) dialerEndpoint := privval.NewSignerDialerEndpoint( - log.TestingLogger(), + log.NewTestingLogger(t), dialer, ) privval.SignerDialerEndpointTimeoutReadWrite(100 * time.Millisecond)(dialerEndpoint) @@ -201,7 +234,7 @@ func TestNodeSetPrivValIPC(t *testing.T) { }() defer pvsc.Stop() - n, err := DefaultNewNode(config, log.TestingLogger()) + n, err := DefaultNewNode(config, log.NewTestingLogger(t)) require.NoError(t, err) assert.IsType(t, &privval.SignerClient{}, n.PrivValidator()) } @@ -228,7 +261,7 @@ func TestCreateProposalBlock(t *testing.T) { require.Nil(t, err) defer proxyApp.Stop() - logger := log.TestingLogger() + logger := log.NewTestingLogger(t) var height int64 = 1 state, stateDB := state(1, height) @@ -289,7 +322,7 @@ func TestNodeNewNodeCustomReactors(t *testing.T) { proxy.DefaultClientCreator(nil, config.ProxyApp, config.ABCI, config.DBDir()), DefaultGenesisDocProviderFunc(config), DefaultDBProvider, - log.TestingLogger(), + log.NewTestingLogger(t), CustomReactors(map[string]p2p.Reactor{"FOO": cr, "BLOCKCHAIN": customBlockchainReactor}), ) require.NoError(t, err) diff --git a/tm2/pkg/bft/privval/file_test.go b/tm2/pkg/bft/privval/file_test.go index 306db4177e5..a798a7ddc64 100644 --- a/tm2/pkg/bft/privval/file_test.go +++ b/tm2/pkg/bft/privval/file_test.go @@ -3,7 +3,6 @@ package privval import ( "encoding/base64" "fmt" - "io/ioutil" "os" "testing" "time" @@ -18,11 +17,13 @@ import ( ) func TestGenLoadValidator(t *testing.T) { + t.Parallel() + assert := assert.New(t) - tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") + tempKeyFile, err := os.CreateTemp("", "priv_validator_key_") require.Nil(t, err) - tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") + tempStateFile, err := os.CreateTemp("", "priv_validator_state_") require.Nil(t, err) privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) @@ -38,9 +39,11 @@ func TestGenLoadValidator(t *testing.T) { } func TestResetValidator(t *testing.T) { - tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") + t.Parallel() + + tempKeyFile, err := os.CreateTemp("", "priv_validator_key_") require.Nil(t, err) - tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") + tempStateFile, err := os.CreateTemp("", "priv_validator_state_") require.Nil(t, err) privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) @@ -66,11 +69,13 @@ func TestResetValidator(t *testing.T) { } func TestLoadOrGenValidator(t *testing.T) { + t.Parallel() + assert := assert.New(t) - tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") + tempKeyFile, err := os.CreateTemp("", "priv_validator_key_") require.Nil(t, err) - tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") + tempStateFile, err := os.CreateTemp("", "priv_validator_state_") require.Nil(t, err) tempKeyFilePath := tempKeyFile.Name() @@ -89,6 +94,8 @@ func TestLoadOrGenValidator(t *testing.T) { } func TestUnmarshalValidatorState(t *testing.T) { + t.Parallel() + assert, require := assert.New(t), require.New(t) // create some fixed values @@ -114,6 +121,8 @@ func TestUnmarshalValidatorState(t *testing.T) { } func TestUnmarshalValidatorKey(t *testing.T) { + t.Parallel() + assert, require := assert.New(t), require.New(t) // create some fixed values @@ -158,11 +167,13 @@ func TestUnmarshalValidatorKey(t *testing.T) { } func TestSignVote(t *testing.T) { + t.Parallel() + assert := assert.New(t) - tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") + tempKeyFile, err := os.CreateTemp("", "priv_validator_key_") require.Nil(t, err) - tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") + tempStateFile, err := os.CreateTemp("", "priv_validator_state_") require.Nil(t, err) privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) @@ -204,11 +215,13 @@ func TestSignVote(t *testing.T) { } func TestSignProposal(t *testing.T) { + t.Parallel() + assert := assert.New(t) - tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") + tempKeyFile, err := os.CreateTemp("", "priv_validator_key_") require.Nil(t, err) - tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") + tempStateFile, err := os.CreateTemp("", "priv_validator_state_") require.Nil(t, err) privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) @@ -248,9 +261,11 @@ func TestSignProposal(t *testing.T) { } func TestDifferByTimestamp(t *testing.T) { - tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") + t.Parallel() + + tempKeyFile, err := os.CreateTemp("", "priv_validator_key_") require.Nil(t, err) - tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") + tempStateFile, err := os.CreateTemp("", "priv_validator_state_") require.Nil(t, err) privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) diff --git a/tm2/pkg/bft/privval/signer_client_test.go b/tm2/pkg/bft/privval/signer_client_test.go index 1ba6b2e08d5..a896547aec3 100644 --- a/tm2/pkg/bft/privval/signer_client_test.go +++ b/tm2/pkg/bft/privval/signer_client_test.go @@ -53,6 +53,8 @@ func getSignerTestCases(t *testing.T) []signerTestCase { } func TestSignerClose(t *testing.T) { + t.Parallel() + for _, tc := range getSignerTestCases(t) { err := tc.signerClient.Close() assert.NoError(t, err) @@ -63,6 +65,8 @@ func TestSignerClose(t *testing.T) { } func TestSignerPing(t *testing.T) { + t.Parallel() + for _, tc := range getSignerTestCases(t) { defer tc.signerServer.Stop() defer tc.signerClient.Close() @@ -73,6 +77,8 @@ func TestSignerPing(t *testing.T) { } func TestSignerGetPubKey(t *testing.T) { + t.Parallel() + for _, tc := range getSignerTestCases(t) { defer tc.signerServer.Stop() defer tc.signerClient.Close() @@ -90,6 +96,8 @@ func TestSignerGetPubKey(t *testing.T) { } func TestSignerProposal(t *testing.T) { + t.Parallel() + for _, tc := range getSignerTestCases(t) { ts := time.Now() want := &types.Proposal{Timestamp: ts} @@ -106,6 +114,8 @@ func TestSignerProposal(t *testing.T) { } func TestSignerVote(t *testing.T) { + t.Parallel() + for _, tc := range getSignerTestCases(t) { ts := time.Now() want := &types.Vote{Timestamp: ts, Type: types.PrecommitType} @@ -122,6 +132,8 @@ func TestSignerVote(t *testing.T) { } func TestSignerVoteResetDeadline(t *testing.T) { + t.Parallel() + for _, tc := range getSignerTestCases(t) { ts := time.Now() want := &types.Vote{Timestamp: ts, Type: types.PrecommitType} @@ -148,6 +160,8 @@ func TestSignerVoteResetDeadline(t *testing.T) { } func TestFlappySignerVoteKeepAlive(t *testing.T) { + t.Parallel() + testutils.FilterStability(t, testutils.Flappy) for _, tc := range getSignerTestCases(t) { @@ -175,6 +189,8 @@ func TestFlappySignerVoteKeepAlive(t *testing.T) { } func TestSignerSignProposalErrors(t *testing.T) { + t.Parallel() + for _, tc := range getSignerTestCases(t) { // Replace service with a mock that always fails tc.signerServer.privVal = types.NewErroringMockPV() @@ -197,6 +213,8 @@ func TestSignerSignProposalErrors(t *testing.T) { } func TestSignerSignVoteErrors(t *testing.T) { + t.Parallel() + for _, tc := range getSignerTestCases(t) { ts := time.Now() vote := &types.Vote{Timestamp: ts, Type: types.PrecommitType} @@ -243,6 +261,8 @@ func brokenHandler(privVal types.PrivValidator, request SignerMessage, chainID s } func TestSignerUnexpectedResponse(t *testing.T) { + t.Parallel() + for _, tc := range getSignerTestCases(t) { tc.signerServer.privVal = types.NewMockPV() tc.mockPV = types.NewMockPV() diff --git a/tm2/pkg/bft/privval/signer_dialer_endpoint.go b/tm2/pkg/bft/privval/signer_dialer_endpoint.go index 40a76b6cb9f..275c406919f 100644 --- a/tm2/pkg/bft/privval/signer_dialer_endpoint.go +++ b/tm2/pkg/bft/privval/signer_dialer_endpoint.go @@ -3,7 +3,8 @@ package privval import ( "time" - "github.com/gnolang/gno/tm2/pkg/log" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/service" ) @@ -41,7 +42,7 @@ type SignerDialerEndpoint struct { // dialer and respond to any signature requests over the connection // using the given privVal. func NewSignerDialerEndpoint( - logger log.Logger, + logger *slog.Logger, dialer SocketDialer, ) *SignerDialerEndpoint { sd := &SignerDialerEndpoint{ diff --git a/tm2/pkg/bft/privval/signer_listener_endpoint.go b/tm2/pkg/bft/privval/signer_listener_endpoint.go index 30ed24268ca..7ce42534290 100644 --- a/tm2/pkg/bft/privval/signer_listener_endpoint.go +++ b/tm2/pkg/bft/privval/signer_listener_endpoint.go @@ -6,7 +6,8 @@ import ( "sync" "time" - "github.com/gnolang/gno/tm2/pkg/log" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/service" ) @@ -30,7 +31,7 @@ type SignerListenerEndpoint struct { // NewSignerListenerEndpoint returns an instance of SignerListenerEndpoint. func NewSignerListenerEndpoint( - logger log.Logger, + logger *slog.Logger, listener net.Listener, ) *SignerListenerEndpoint { sc := &SignerListenerEndpoint{ diff --git a/tm2/pkg/bft/privval/signer_listener_endpoint_test.go b/tm2/pkg/bft/privval/signer_listener_endpoint_test.go index 8e5b1613454..bb026cd9acc 100644 --- a/tm2/pkg/bft/privval/signer_listener_endpoint_test.go +++ b/tm2/pkg/bft/privval/signer_listener_endpoint_test.go @@ -5,6 +5,8 @@ import ( "testing" "time" + "golang.org/x/exp/slog" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -32,6 +34,8 @@ type dialerTestCase struct { // SignerDialerEndpoint.dialer() call inside SignerDialerEndpoint.acceptNewConnection() to return // successfully immediately, putting an instant stop to any retry attempts. func TestSignerRemoteRetryTCPOnly(t *testing.T) { + t.Parallel() + var ( attemptCh = make(chan int) retries = 10 @@ -60,7 +64,7 @@ func TestSignerRemoteRetryTCPOnly(t *testing.T) { }(ln, attemptCh) dialerEndpoint := NewSignerDialerEndpoint( - log.TestingLogger(), + log.NewTestingLogger(t), DialTCPFn(ln.Addr().String(), testTimeoutReadWrite, ed25519.GenPrivKey()), ) SignerDialerEndpointTimeoutReadWrite(time.Millisecond)(dialerEndpoint) @@ -83,9 +87,11 @@ func TestSignerRemoteRetryTCPOnly(t *testing.T) { } func TestRetryConnToRemoteSigner(t *testing.T) { + t.Parallel() + for _, tc := range getDialerTestCases(t) { var ( - logger = log.TestingLogger() + logger = log.NewTestingLogger(t) chainID = random.RandStr(12) mockPV = types.NewMockPV() endpointIsOpenCh = make(chan struct{}) @@ -130,9 +136,9 @@ func TestRetryConnToRemoteSigner(t *testing.T) { } } -// ///////////////////////////////// +// ----------- -func newSignerListenerEndpoint(logger log.Logger, ln net.Listener, timeoutReadWrite time.Duration) *SignerListenerEndpoint { +func newSignerListenerEndpoint(logger *slog.Logger, ln net.Listener, timeoutReadWrite time.Duration) *SignerListenerEndpoint { var listener net.Listener if ln.Addr().Network() == "unix" { @@ -168,7 +174,7 @@ func getMockEndpoints( t.Helper() var ( - logger = log.TestingLogger() + logger = log.NewTestingLogger(t) endpointIsOpenCh = make(chan struct{}) dialerEndpoint = NewSignerDialerEndpoint( diff --git a/tm2/pkg/bft/privval/socket_dialers_test.go b/tm2/pkg/bft/privval/socket_dialers_test.go index 150b9218575..a2f2cf9743b 100644 --- a/tm2/pkg/bft/privval/socket_dialers_test.go +++ b/tm2/pkg/bft/privval/socket_dialers_test.go @@ -40,6 +40,8 @@ func getDialerTestCases(t *testing.T) []dialerTestCase { } func TestIsConnTimeoutForFundamentalTimeouts(t *testing.T) { + t.Parallel() + // Generate a networking timeout tcpAddr := "127.0.0.1:34985" dialer := DialTCPFn(tcpAddr, time.Millisecond, ed25519.GenPrivKey()) @@ -49,6 +51,8 @@ func TestIsConnTimeoutForFundamentalTimeouts(t *testing.T) { } func TestIsConnTimeoutForWrappedConnTimeouts(t *testing.T) { + t.Parallel() + tcpAddr := "127.0.0.1:34985" dialer := DialTCPFn(tcpAddr, time.Millisecond, ed25519.GenPrivKey()) _, err := dialer() diff --git a/tm2/pkg/bft/privval/socket_listeners_test.go b/tm2/pkg/bft/privval/socket_listeners_test.go index a3630903197..3177aec71dc 100644 --- a/tm2/pkg/bft/privval/socket_listeners_test.go +++ b/tm2/pkg/bft/privval/socket_listeners_test.go @@ -1,7 +1,6 @@ package privval import ( - "io/ioutil" "net" "os" "testing" @@ -29,7 +28,7 @@ type listenerTestCase struct { // testUnixAddr will attempt to obtain a platform-independent temporary file // name for a Unix socket func testUnixAddr() (string, error) { - f, err := ioutil.TempFile("", "tendermint-privval-test-*") + f, err := os.CreateTemp("", "tendermint-privval-test-*") if err != nil { return "", err } @@ -89,6 +88,8 @@ func listenerTestCases(t *testing.T, timeoutAccept, timeoutReadWrite time.Durati } func TestListenerTimeoutAccept(t *testing.T) { + t.Parallel() + for _, tc := range listenerTestCases(t, time.Millisecond, time.Second) { _, err := tc.listener.Accept() opErr, ok := err.(*net.OpError) diff --git a/tm2/pkg/bft/privval/utils.go b/tm2/pkg/bft/privval/utils.go index c190235ddf3..904474ba4f7 100644 --- a/tm2/pkg/bft/privval/utils.go +++ b/tm2/pkg/bft/privval/utils.go @@ -4,9 +4,10 @@ import ( "fmt" "net" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" "github.com/gnolang/gno/tm2/pkg/errors" - "github.com/gnolang/gno/tm2/pkg/log" osm "github.com/gnolang/gno/tm2/pkg/os" ) @@ -25,7 +26,7 @@ func IsConnTimeout(err error) bool { } // NewSignerListener creates a new SignerListenerEndpoint using the corresponding listen address -func NewSignerListener(listenAddr string, logger log.Logger) (*SignerListenerEndpoint, error) { +func NewSignerListener(listenAddr string, logger *slog.Logger) (*SignerListenerEndpoint, error) { var listener net.Listener protocol, address := osm.ProtocolAndAddress(listenAddr) diff --git a/tm2/pkg/bft/privval/utils_test.go b/tm2/pkg/bft/privval/utils_test.go index 6e5562e4cbd..63ee6a6076a 100644 --- a/tm2/pkg/bft/privval/utils_test.go +++ b/tm2/pkg/bft/privval/utils_test.go @@ -9,6 +9,8 @@ import ( ) func TestIsConnTimeoutForNonTimeoutErrors(t *testing.T) { + t.Parallel() + assert.False(t, IsConnTimeout(errors.Wrap(ErrDialRetryMax, "max retries exceeded"))) assert.False(t, IsConnTimeout(errors.New("completely irrelevant error"))) } diff --git a/tm2/pkg/bft/rpc/client/helpers_test.go b/tm2/pkg/bft/rpc/client/helpers_test.go index a58ed5adfd6..4d0b54c2358 100644 --- a/tm2/pkg/bft/rpc/client/helpers_test.go +++ b/tm2/pkg/bft/rpc/client/helpers_test.go @@ -15,6 +15,8 @@ import ( ) func TestWaitForHeight(t *testing.T) { + t.Parallel() + assert, require := assert.New(t), require.New(t) // test with error result - immediate failure diff --git a/tm2/pkg/bft/rpc/client/localclient.go b/tm2/pkg/bft/rpc/client/localclient.go index 20209f74071..15e2b916c71 100644 --- a/tm2/pkg/bft/rpc/client/localclient.go +++ b/tm2/pkg/bft/rpc/client/localclient.go @@ -1,6 +1,8 @@ package client import ( + "golang.org/x/exp/slog" + nm "github.com/gnolang/gno/tm2/pkg/bft/node" "github.com/gnolang/gno/tm2/pkg/bft/rpc/core" ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" @@ -24,7 +26,7 @@ For real clients, you probably want to use client.HTTP. For more powerful control during testing, you probably want the "client/mock" package. */ type Local struct { - Logger log.Logger + Logger *slog.Logger ctx *rpctypes.Context } @@ -37,7 +39,7 @@ type Local struct { func NewLocal(node *nm.Node) *Local { node.ConfigureRPC() return &Local{ - Logger: log.NewNopLogger(), + Logger: log.NewNoopLogger(), ctx: &rpctypes.Context{}, } } @@ -45,7 +47,7 @@ func NewLocal(node *nm.Node) *Local { var _ Client = (*Local)(nil) // SetLogger allows to set a logger on the client. -func (c *Local) SetLogger(l log.Logger) { +func (c *Local) SetLogger(l *slog.Logger) { c.Logger = l } diff --git a/tm2/pkg/bft/rpc/client/main_test.go b/tm2/pkg/bft/rpc/client/main_test.go index 58dd9538412..759104a3029 100644 --- a/tm2/pkg/bft/rpc/client/main_test.go +++ b/tm2/pkg/bft/rpc/client/main_test.go @@ -1,7 +1,6 @@ package client_test import ( - "io/ioutil" "os" "testing" @@ -14,7 +13,7 @@ var node *nm.Node func TestMain(m *testing.M) { // start a tendermint node (and kvstore) in the background to test against - dir, err := ioutil.TempDir("/tmp", "rpc-client-test") + dir, err := os.MkdirTemp("/tmp", "rpc-client-test") if err != nil { panic(err) } diff --git a/tm2/pkg/bft/rpc/client/mock/client.go b/tm2/pkg/bft/rpc/client/mock/client.go index f7a617da9fe..46db69debb3 100644 --- a/tm2/pkg/bft/rpc/client/mock/client.go +++ b/tm2/pkg/bft/rpc/client/mock/client.go @@ -50,7 +50,7 @@ type Call struct { Error error } -// GetResponse will generate the apporiate response for us, when +// GetResponse will generate the appropriate response for us, when // using the Call struct to configure a Mock handler. // // When configuring a response, if only one of Response or Error is diff --git a/tm2/pkg/bft/rpc/client/rpc_test.go b/tm2/pkg/bft/rpc/client/rpc_test.go index 7b649e1dda8..e09ae8d4466 100644 --- a/tm2/pkg/bft/rpc/client/rpc_test.go +++ b/tm2/pkg/bft/rpc/client/rpc_test.go @@ -35,6 +35,8 @@ func GetClients() []client.Client { } func TestNilCustomHTTPClient(t *testing.T) { + t.Parallel() + require.Panics(t, func() { client.NewHTTPWithClient("http://example.com", "/websocket", nil) }) @@ -44,6 +46,8 @@ func TestNilCustomHTTPClient(t *testing.T) { } func TestCustomHTTPClient(t *testing.T) { + t.Parallel() + remote := rpctest.GetConfig().RPC.ListenAddress c := client.NewHTTPWithClient(remote, "/websocket", http.DefaultClient) status, err := c.Status() @@ -52,6 +56,8 @@ func TestCustomHTTPClient(t *testing.T) { } func TestCorsEnabled(t *testing.T) { + t.Parallel() + origin := rpctest.GetConfig().RPC.CORSAllowedOrigins[0] remote := strings.Replace(rpctest.GetConfig().RPC.ListenAddress, "tcp", "http", -1) @@ -68,6 +74,8 @@ func TestCorsEnabled(t *testing.T) { // Make sure status is correct (we connect properly) func TestStatus(t *testing.T) { + t.Parallel() + for i, c := range GetClients() { moniker := rpctest.GetConfig().Moniker status, err := c.Status() @@ -78,6 +86,8 @@ func TestStatus(t *testing.T) { // Make sure info is correct (we connect properly) func TestInfo(t *testing.T) { + t.Parallel() + for i, c := range GetClients() { // status, err := c.Status() // require.Nil(t, err, "%+v", err) @@ -90,6 +100,8 @@ func TestInfo(t *testing.T) { } func TestNetInfo(t *testing.T) { + t.Parallel() + for i, c := range GetClients() { nc, ok := c.(client.NetworkClient) require.True(t, ok, "%d", i) @@ -101,6 +113,8 @@ func TestNetInfo(t *testing.T) { } func TestDumpConsensusState(t *testing.T) { + t.Parallel() + for i, c := range GetClients() { // FIXME: fix server so it doesn't panic on invalid input nc, ok := c.(client.NetworkClient) @@ -113,6 +127,8 @@ func TestDumpConsensusState(t *testing.T) { } func TestConsensusState(t *testing.T) { + t.Parallel() + for i, c := range GetClients() { // FIXME: fix server so it doesn't panic on invalid input nc, ok := c.(client.NetworkClient) @@ -124,6 +140,8 @@ func TestConsensusState(t *testing.T) { } func TestHealth(t *testing.T) { + t.Parallel() + for i, c := range GetClients() { nc, ok := c.(client.NetworkClient) require.True(t, ok, "%d", i) @@ -133,6 +151,8 @@ func TestHealth(t *testing.T) { } func TestGenesisAndValidators(t *testing.T) { + t.Parallel() + for i, c := range GetClients() { // make sure this is the right genesis file gen, err := c.Genesis() @@ -173,6 +193,8 @@ func TestABCIQuery(t *testing.T) { // Make some app checks func TestAppCalls(t *testing.T) { + t.Parallel() + assert, require := assert.New(t), require.New(t) for i, c := range GetClients() { // get an offset of height to avoid racing and guessing @@ -265,6 +287,8 @@ func TestAppCalls(t *testing.T) { } func TestBroadcastTxSync(t *testing.T) { + t.Parallel() + require := require.New(t) // TODO (melekes): use mempool which is set on RPC rather than getting it from node @@ -344,6 +368,8 @@ func TestNumUnconfirmedTxs(t *testing.T) { /* func TestTx(t *testing.T) { + t.Parallel() + // first we broadcast a tx c := getHTTPClient() _, _, tx := MakeTxKV() @@ -398,6 +424,8 @@ func TestTx(t *testing.T) { } func TestTxSearch(t *testing.T) { + t.Parallel() + // first we broadcast a tx c := getHTTPClient() _, _, tx := MakeTxKV() @@ -519,6 +547,8 @@ func testBatchedJSONRPCCalls(t *testing.T, c *client.HTTP) { } func TestBatchedJSONRPCCallsCancellation(t *testing.T) { + t.Parallel() + c := getHTTPClient() _, _, tx1 := MakeTxKV() _, _, tx2 := MakeTxKV() @@ -537,6 +567,8 @@ func TestBatchedJSONRPCCallsCancellation(t *testing.T) { } func TestSendingEmptyJSONRPCRequestBatch(t *testing.T) { + t.Parallel() + c := getHTTPClient() batch := c.NewBatch() _, err := batch.Send() @@ -544,6 +576,8 @@ func TestSendingEmptyJSONRPCRequestBatch(t *testing.T) { } func TestClearingEmptyJSONRPCRequestBatch(t *testing.T) { + t.Parallel() + c := getHTTPClient() batch := c.NewBatch() require.Zero(t, batch.Clear(), "clearing an empty batch of JSON RPC requests should result in a 0 result") diff --git a/tm2/pkg/bft/rpc/config/config.go b/tm2/pkg/bft/rpc/config/config.go index d0148badad1..76c490bf94c 100644 --- a/tm2/pkg/bft/rpc/config/config.go +++ b/tm2/pkg/bft/rpc/config/config.go @@ -19,33 +19,33 @@ type RPCConfig struct { RootDir string `toml:"home"` // TCP or UNIX socket address for the RPC server to listen on - ListenAddress string `toml:"laddr"` + ListenAddress string `toml:"laddr" comment:"TCP or UNIX socket address for the RPC server to listen on"` // A list of origins a cross-domain request can be executed from. // If the special '*' value is present in the list, all origins will be allowed. // An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com). // Only one wildcard can be used per origin. - CORSAllowedOrigins []string `toml:"cors_allowed_origins"` + CORSAllowedOrigins []string `toml:"cors_allowed_origins" comment:"A list of origins a cross-domain request can be executed from\n Default value '[]' disables cors support\n Use '[\"*\"]' to allow any origin"` // A list of methods the client is allowed to use with cross-domain requests. - CORSAllowedMethods []string `toml:"cors_allowed_methods"` + CORSAllowedMethods []string `toml:"cors_allowed_methods" comment:"A list of methods the client is allowed to use with cross-domain requests"` // A list of non simple headers the client is allowed to use with cross-domain requests. - CORSAllowedHeaders []string `toml:"cors_allowed_headers"` + CORSAllowedHeaders []string `toml:"cors_allowed_headers" comment:"A list of non simple headers the client is allowed to use with cross-domain requests"` // TCP or UNIX socket address for the gRPC server to listen on // NOTE: This server only supports /broadcast_tx_commit - GRPCListenAddress string `toml:"grpc_laddr"` + GRPCListenAddress string `toml:"grpc_laddr" comment:"TCP or UNIX socket address for the gRPC server to listen on\n NOTE: This server only supports /broadcast_tx_commit"` // Maximum number of simultaneous connections. // Does not include RPC (HTTP&WebSocket) connections. See max_open_connections // If you want to accept a larger number than the default, make sure // you increase your OS limits. // 0 - unlimited. - GRPCMaxOpenConnections int `toml:"grpc_max_open_connections"` + GRPCMaxOpenConnections int `toml:"grpc_max_open_connections" comment:"Maximum number of simultaneous connections.\n Does not include RPC (HTTP&WebSocket) connections. See max_open_connections\n If you want to accept a larger number than the default, make sure\n you increase your OS limits.\n 0 - unlimited.\n Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}\n 1024 - 40 - 10 - 50 = 924 = ~900"` // Activate unsafe RPC commands like /dial_persistent_peers and /unsafe_flush_mempool - Unsafe bool `toml:"unsafe"` + Unsafe bool `toml:"unsafe" comment:"Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool"` // Maximum number of simultaneous connections (including WebSocket). // Does not include gRPC connections. See grpc_max_open_connections @@ -54,35 +54,35 @@ type RPCConfig struct { // 0 - unlimited. // Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} // 1024 - 40 - 10 - 50 = 924 = ~900 - MaxOpenConnections int `toml:"max_open_connections"` + MaxOpenConnections int `toml:"max_open_connections" comment:"Maximum number of simultaneous connections (including WebSocket).\n Does not include gRPC connections. See grpc_max_open_connections\n If you want to accept a larger number than the default, make sure\n you increase your OS limits.\n 0 - unlimited.\n Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}\n 1024 - 40 - 10 - 50 = 924 = ~900"` // How long to wait for a tx to be committed during /broadcast_tx_commit // WARNING: Using a value larger than 10s will result in increasing the // global HTTP write timeout, which applies to all connections and endpoints. // See https://github.com/gnolang/gno/tm2/pkg/bft/issues/3435 - TimeoutBroadcastTxCommit time.Duration `toml:"timeout_broadcast_tx_commit"` + TimeoutBroadcastTxCommit time.Duration `toml:"timeout_broadcast_tx_commit" comment:"How long to wait for a tx to be committed during /broadcast_tx_commit.\n WARNING: Using a value larger than 10s will result in increasing the\n global HTTP write timeout, which applies to all connections and endpoints.\n See https://github.com/tendermint/classic/issues/3435"` // Maximum size of request body, in bytes - MaxBodyBytes int64 `toml:"max_body_bytes"` + MaxBodyBytes int64 `toml:"max_body_bytes" comment:"Maximum size of request body, in bytes"` // Maximum size of request header, in bytes - MaxHeaderBytes int `toml:"max_header_bytes"` + MaxHeaderBytes int `toml:"max_header_bytes" comment:"Maximum size of request header, in bytes"` // The path to a file containing certificate that is used to create the HTTPS server. - // Migth be either absolute path or path related to tendermint's config directory. + // Might be either absolute path or path related to tendermint's config directory. // // If the certificate is signed by a certificate authority, // the certFile should be the concatenation of the server's certificate, any intermediates, // and the CA's certificate. // // NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. - TLSCertFile string `toml:"tls_cert_file"` + TLSCertFile string `toml:"tls_cert_file" comment:"The path to a file containing certificate that is used to create the HTTPS server.\n Might be either absolute path or path related to tendermint's config directory.\n If the certificate is signed by a certificate authority,\n the certFile should be the concatenation of the server's certificate, any intermediates,\n and the CA's certificate.\n NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run."` // The path to a file containing matching private key that is used to create the HTTPS server. - // Migth be either absolute path or path related to tendermint's config directory. + // Might be either absolute path or path related to tendermint's config directory. // // NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. - TLSKeyFile string `toml:"tls_key_file"` + TLSKeyFile string `toml:"tls_key_file" comment:"The path to a file containing matching private key that is used to create the HTTPS server.\n Might be either absolute path or path related to tendermint's config directory.\n NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run."` } // DefaultRPCConfig returns a default configuration for the RPC server diff --git a/tm2/pkg/bft/rpc/core/blocks_test.go b/tm2/pkg/bft/rpc/core/blocks_test.go index a4d689ffd23..0fcd40f6d14 100644 --- a/tm2/pkg/bft/rpc/core/blocks_test.go +++ b/tm2/pkg/bft/rpc/core/blocks_test.go @@ -8,6 +8,8 @@ import ( ) func TestBlockchainInfo(t *testing.T) { + t.Parallel() + cases := []struct { min, max int64 height int64 diff --git a/tm2/pkg/bft/rpc/core/net_test.go b/tm2/pkg/bft/rpc/core/net_test.go index 42bf79fcce1..3273837b6ce 100644 --- a/tm2/pkg/bft/rpc/core/net_test.go +++ b/tm2/pkg/bft/rpc/core/net_test.go @@ -13,13 +13,15 @@ import ( ) func TestUnsafeDialSeeds(t *testing.T) { + t.Parallel() + sw := p2p.MakeSwitch(p2pcfg.DefaultP2PConfig(), 1, "testing", "123.123.123", func(n int, sw *p2p.Switch) *p2p.Switch { return sw }) err := sw.Start() require.NoError(t, err) defer sw.Stop() - logger = log.TestingLogger() + logger = log.NewNoopLogger() p2pPeers = sw testCases := []struct { @@ -43,13 +45,15 @@ func TestUnsafeDialSeeds(t *testing.T) { } func TestUnsafeDialPeers(t *testing.T) { + t.Parallel() + sw := p2p.MakeSwitch(p2pcfg.DefaultP2PConfig(), 1, "testing", "123.123.123", func(n int, sw *p2p.Switch) *p2p.Switch { return sw }) err := sw.Start() require.NoError(t, err) defer sw.Stop() - logger = log.TestingLogger() + logger = log.NewNoopLogger() p2pPeers = sw testCases := []struct { diff --git a/tm2/pkg/bft/rpc/core/pipe.go b/tm2/pkg/bft/rpc/core/pipe.go index fd6c8fc0692..977dfd54e1f 100644 --- a/tm2/pkg/bft/rpc/core/pipe.go +++ b/tm2/pkg/bft/rpc/core/pipe.go @@ -3,6 +3,8 @@ package core import ( "fmt" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/bft/consensus" cnscfg "github.com/gnolang/gno/tm2/pkg/bft/consensus/config" cstypes "github.com/gnolang/gno/tm2/pkg/bft/consensus/types" @@ -10,12 +12,10 @@ import ( "github.com/gnolang/gno/tm2/pkg/bft/proxy" cfg "github.com/gnolang/gno/tm2/pkg/bft/rpc/config" sm "github.com/gnolang/gno/tm2/pkg/bft/state" - "github.com/gnolang/gno/tm2/pkg/bft/state/txindex" "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/crypto" dbm "github.com/gnolang/gno/tm2/pkg/db" "github.com/gnolang/gno/tm2/pkg/events" - "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/p2p" ) @@ -25,7 +25,7 @@ const ( maxPerPage = 100 ) -//---------------------------------------------- +// ---------------------------------------------- // These interfaces are used by RPC and must be thread safe type Consensus interface { @@ -50,7 +50,7 @@ type peers interface { Peers() p2p.IPeerSet } -//---------------------------------------------- +// ---------------------------------------------- // These package level globals come with setters // that are expected to be called only once, on startup @@ -68,13 +68,12 @@ var ( // objects pubKey crypto.PubKey genDoc *types.GenesisDoc // cache the genesis structure - txIndexer txindex.TxIndexer consensusReactor *consensus.ConsensusReactor evsw events.EventSwitch gTxDispatcher *txDispatcher mempool mempl.Mempool - logger log.Logger + logger *slog.Logger config cfg.RPCConfig ) @@ -115,15 +114,11 @@ func SetProxyAppQuery(appConn proxy.AppConnQuery) { proxyAppQuery = appConn } -func SetTxIndexer(indexer txindex.TxIndexer) { - txIndexer = indexer -} - func SetConsensusReactor(conR *consensus.ConsensusReactor) { consensusReactor = conR } -func SetLogger(l log.Logger) { +func SetLogger(l *slog.Logger) { logger = l } @@ -169,12 +164,3 @@ func validatePerPage(perPage int) int { } return perPage } - -func validateSkipCount(page, perPage int) int { - skipCount := (page - 1) * perPage - if skipCount < 0 { - return 0 - } - - return skipCount -} diff --git a/tm2/pkg/bft/rpc/core/pipe_test.go b/tm2/pkg/bft/rpc/core/pipe_test.go index 3147f600734..6136f66c9d8 100644 --- a/tm2/pkg/bft/rpc/core/pipe_test.go +++ b/tm2/pkg/bft/rpc/core/pipe_test.go @@ -8,6 +8,8 @@ import ( ) func TestPaginationPage(t *testing.T) { + t.Parallel() + cases := []struct { totalCount int perPage int @@ -51,6 +53,8 @@ func TestPaginationPage(t *testing.T) { } func TestPaginationPerPage(t *testing.T) { + t.Parallel() + cases := []struct { totalCount int perPage int diff --git a/tm2/pkg/bft/rpc/core/types/responses_test.go b/tm2/pkg/bft/rpc/core/types/responses_test.go index d309a6b63a1..268a8d25c34 100644 --- a/tm2/pkg/bft/rpc/core/types/responses_test.go +++ b/tm2/pkg/bft/rpc/core/types/responses_test.go @@ -9,6 +9,8 @@ import ( ) func TestStatusIndexer(t *testing.T) { + t.Parallel() + var status *ResultStatus assert.False(t, status.TxIndexEnabled()) diff --git a/tm2/pkg/bft/rpc/lib/client/args_test.go b/tm2/pkg/bft/rpc/lib/client/args_test.go index 8f44939f0aa..2a7e749f094 100644 --- a/tm2/pkg/bft/rpc/lib/client/args_test.go +++ b/tm2/pkg/bft/rpc/lib/client/args_test.go @@ -15,6 +15,8 @@ type Foo struct { } func TestArgToJSON(t *testing.T) { + t.Parallel() + assert := assert.New(t) require := require.New(t) diff --git a/tm2/pkg/bft/rpc/lib/client/http_client.go b/tm2/pkg/bft/rpc/lib/client/http_client.go index d5708f97d40..c02d029f27a 100644 --- a/tm2/pkg/bft/rpc/lib/client/http_client.go +++ b/tm2/pkg/bft/rpc/lib/client/http_client.go @@ -4,7 +4,7 @@ import ( "bytes" "encoding/json" "fmt" - "io/ioutil" + "io" "net" "net/http" "net/url" @@ -78,12 +78,6 @@ func parseRemoteAddr(remoteAddr string) (network string, s string, err error) { return "", "", fmt.Errorf("invalid addr: %s", remoteAddr) } - // accept http(s) as an alias for tcp - switch protocol { - case protoHTTP, protoHTTPS: - protocol = protoTCP - } - return protocol, address, nil } @@ -99,6 +93,12 @@ func makeHTTPDialer(remoteAddr string) func(string, string) (net.Conn, error) { return makeErrorDialer(err) } + // net.Dial doesn't understand http/https, so change it to TCP + switch protocol { + case protoHTTP, protoHTTPS: + protocol = protoTCP + } + return func(proto, addr string) (net.Conn, error) { return net.Dial(protocol, address) } @@ -201,7 +201,7 @@ func (c *JSONRPCClient) Call(method string, params map[string]interface{}, resul return nil, errors.New("server at '%s' returned %s", c.address, httpResponse.Status) } - responseBytes, err := ioutil.ReadAll(httpResponse.Body) + responseBytes, err := io.ReadAll(httpResponse.Body) if err != nil { return nil, err } @@ -238,7 +238,7 @@ func (c *JSONRPCClient) sendBatch(requests []*jsonRPCBufferedRequest) ([]interfa return nil, errors.New("server at '%s' returned %s", c.address, httpResponse.Status) } - responseBytes, err := ioutil.ReadAll(httpResponse.Body) + responseBytes, err := io.ReadAll(httpResponse.Body) if err != nil { return nil, err } @@ -332,7 +332,7 @@ func (c *URIClient) Call(method string, params map[string]interface{}, result in return nil, errors.New("server at '%s' returned %s", c.address, resp.Status) } - responseBytes, err := ioutil.ReadAll(resp.Body) + responseBytes, err := io.ReadAll(resp.Body) if err != nil { return nil, err } diff --git a/tm2/pkg/bft/rpc/lib/client/http_client_test.go b/tm2/pkg/bft/rpc/lib/client/http_client_test.go new file mode 100644 index 00000000000..460f5b9947b --- /dev/null +++ b/tm2/pkg/bft/rpc/lib/client/http_client_test.go @@ -0,0 +1,58 @@ +package rpcclient + +import ( + "testing" + + "github.com/jaekwon/testify/assert" +) + +func Test_parseRemoteAddr(t *testing.T) { + t.Parallel() + + tt := []struct { + remoteAddr string + network, s, errContains string + }{ + {"127.0.0.1", "tcp", "127.0.0.1", ""}, + {"https://example.com", "https", "example.com", ""}, + {"wss://[::1]", "wss", "[::1]", ""}, + // no error cases - they cannot happen! + } + + for _, tc := range tt { + n, s, err := parseRemoteAddr(tc.remoteAddr) + if tc.errContains != "" { + _ = assert.NotNil(t, err) && assert.Contains(t, err.Error(), tc.errContains) + } + assert.NoError(t, err) + assert.Equal(t, n, tc.network) + assert.Equal(t, s, tc.s) + } +} + +// Following tests check that we correctly translate http/https to tcp, +// and other protocols are left intact from parseRemoteAddr() + +func Test_makeHTTPDialer(t *testing.T) { + t.Parallel() + + dl := makeHTTPDialer("https://.") + _, err := dl("hello", "world") + if assert.NotNil(t, err) { + e := err.Error() + assert.Contains(t, e, "dial tcp:", "should convert https to tcp") + assert.Contains(t, e, "address .:", "should have parsed the address (as incorrect)") + } +} + +func Test_makeHTTPDialer_noConvert(t *testing.T) { + t.Parallel() + + dl := makeHTTPDialer("udp://.") + _, err := dl("hello", "world") + if assert.NotNil(t, err) { + e := err.Error() + assert.Contains(t, e, "dial udp:", "udp protocol should remain the same") + assert.Contains(t, e, "address .:", "should have parsed the address (as incorrect)") + } +} diff --git a/tm2/pkg/bft/rpc/lib/client/integration_test.go b/tm2/pkg/bft/rpc/lib/client/integration_test.go index 486540a989f..85b4c94594b 100644 --- a/tm2/pkg/bft/rpc/lib/client/integration_test.go +++ b/tm2/pkg/bft/rpc/lib/client/integration_test.go @@ -19,6 +19,8 @@ import ( ) func TestWSClientReconnectWithJitter(t *testing.T) { + t.Parallel() + n := 8 maxReconnectAttempts := 3 // Max wait time is ceil(1+0.999) + ceil(2+0.999) + ceil(4+0.999) + ceil(...) = 2 + 3 + 5 = 10s + ... @@ -27,7 +29,7 @@ func TestWSClientReconnectWithJitter(t *testing.T) { errNotConnected := errors.New("not connected") clientMap := make(map[int]*WSClient) buf := new(bytes.Buffer) - logger := log.NewTMLogger(buf) + logger := log.NewNoopLogger() for i := 0; i < n; i++ { c := NewWSClient("tcp://foo", "/websocket") c.Dialer = func(string, string) (net.Conn, error) { diff --git a/tm2/pkg/bft/rpc/lib/client/ws_client.go b/tm2/pkg/bft/rpc/lib/client/ws_client.go index 040437d11ff..4e159a3e3dc 100644 --- a/tm2/pkg/bft/rpc/lib/client/ws_client.go +++ b/tm2/pkg/bft/rpc/lib/client/ws_client.go @@ -226,7 +226,7 @@ func (c *WSClient) CallWithArrayParams(ctx context.Context, method string, param return c.Send(ctx, request) } -/////////////////////////////////////////////////////////////////////////////// +// ----------- // Private methods func (c *WSClient) dial() error { diff --git a/tm2/pkg/bft/rpc/lib/client/ws_client_test.go b/tm2/pkg/bft/rpc/lib/client/ws_client_test.go index b3a495f25b2..c902ee709e0 100644 --- a/tm2/pkg/bft/rpc/lib/client/ws_client_test.go +++ b/tm2/pkg/bft/rpc/lib/client/ws_client_test.go @@ -58,6 +58,8 @@ func (h *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } func TestWSClientReconnectsAfterReadFailure(t *testing.T) { + t.Parallel() + var wg sync.WaitGroup // start server @@ -91,6 +93,8 @@ func TestWSClientReconnectsAfterReadFailure(t *testing.T) { } func TestWSClientReconnectsAfterWriteFailure(t *testing.T) { + t.Parallel() + var wg sync.WaitGroup // start server @@ -121,6 +125,8 @@ func TestWSClientReconnectsAfterWriteFailure(t *testing.T) { } func TestWSClientReconnectFailure(t *testing.T) { + t.Parallel() + // start server h := &myHandler{} s := httptest.NewServer(h) @@ -172,6 +178,8 @@ func TestWSClientReconnectFailure(t *testing.T) { } func TestNotBlockingOnStop(t *testing.T) { + t.Parallel() + timeout := 2 * time.Second s := httptest.NewServer(&myHandler{}) c := startClient(t, s.Listener.Addr()) @@ -200,7 +208,7 @@ func startClient(t *testing.T, addr net.Addr) *WSClient { c := NewWSClient(addr.String(), "/websocket") err := c.Start() require.Nil(t, err) - c.SetLogger(log.TestingLogger()) + c.SetLogger(log.NewTestingLogger(t)) return c } diff --git a/tm2/pkg/bft/rpc/lib/rpc_test.go b/tm2/pkg/bft/rpc/lib/rpc_test.go index 2c1aebaae14..386e641cb53 100644 --- a/tm2/pkg/bft/rpc/lib/rpc_test.go +++ b/tm2/pkg/bft/rpc/lib/rpc_test.go @@ -15,7 +15,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/gnolang/gno/tm2/pkg/colors" "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/random" @@ -88,22 +87,9 @@ func TestMain(m *testing.M) { os.Exit(code) } -var colorFn = func(keyvals ...interface{}) colors.Color { - for i := 0; i < len(keyvals)-1; i += 2 { - if keyvals[i] == "socket" { - if keyvals[i+1] == "tcp" { - return colors.Blue - } else if keyvals[i+1] == "unix" { - return colors.Cyan - } - } - } - return colors.None -} - // launch unix and tcp servers func setup() { - logger := log.NewTMLoggerWithColorFn(log.NewSyncWriter(os.Stdout), colorFn) + logger := log.NewNoopLogger() cmd := exec.Command("rm", "-f", unixSocket) err := cmd.Start() @@ -280,6 +266,8 @@ func testWithWSClient(t *testing.T, cl *client.WSClient) { // ------------- func TestServersAndClientsBasic(t *testing.T) { + t.Parallel() + serverAddrs := [...]string{tcpAddr, unixAddr} for _, addr := range serverAddrs { cl1 := client.NewURIClient(addr) @@ -291,7 +279,7 @@ func TestServersAndClientsBasic(t *testing.T) { testWithHTTPClient(t, cl2) cl3 := client.NewWSClient(addr, websocketEndpoint) - cl3.SetLogger(log.TestingLogger()) + cl3.SetLogger(log.NewTestingLogger(t)) err := cl3.Start() require.Nil(t, err) fmt.Printf("=== testing server on %s using WS client", addr) @@ -309,6 +297,8 @@ func TestServersAndClientsBasic(t *testing.T) { } func TestHexStringArg(t *testing.T) { + t.Parallel() + cl := client.NewURIClient(tcpAddr) // should NOT be handled as hex val := "0xabc" @@ -318,6 +308,8 @@ func TestHexStringArg(t *testing.T) { } func TestQuotedStringArg(t *testing.T) { + t.Parallel() + cl := client.NewURIClient(tcpAddr) // should NOT be unquoted val := "\"abc\"" @@ -327,8 +319,10 @@ func TestQuotedStringArg(t *testing.T) { } func TestWSNewWSRPCFunc(t *testing.T) { + t.Parallel() + cl := client.NewWSClient(tcpAddr, websocketEndpoint) - cl.SetLogger(log.TestingLogger()) + cl.SetLogger(log.NewTestingLogger(t)) err := cl.Start() require.Nil(t, err) defer cl.Stop() @@ -352,8 +346,10 @@ func TestWSNewWSRPCFunc(t *testing.T) { } func TestWSHandlesArrayParams(t *testing.T) { + t.Parallel() + cl := client.NewWSClient(tcpAddr, websocketEndpoint) - cl.SetLogger(log.TestingLogger()) + cl.SetLogger(log.NewTestingLogger(t)) err := cl.Start() require.Nil(t, err) defer cl.Stop() @@ -377,8 +373,10 @@ func TestWSHandlesArrayParams(t *testing.T) { // TestWSClientPingPong checks that a client & server exchange pings // & pongs so connection stays alive. func TestWSClientPingPong(t *testing.T) { + t.Parallel() + cl := client.NewWSClient(tcpAddr, websocketEndpoint) - cl.SetLogger(log.TestingLogger()) + cl.SetLogger(log.NewTestingLogger(t)) err := cl.Start() require.Nil(t, err) defer cl.Stop() diff --git a/tm2/pkg/bft/rpc/lib/server/handlers.go b/tm2/pkg/bft/rpc/lib/server/handlers.go index aa3fadd4d18..374d417f4c5 100644 --- a/tm2/pkg/bft/rpc/lib/server/handlers.go +++ b/tm2/pkg/bft/rpc/lib/server/handlers.go @@ -6,14 +6,17 @@ import ( "encoding/hex" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "reflect" + "regexp" "runtime/debug" "sort" "strings" "time" + "golang.org/x/exp/slog" + "github.com/gorilla/websocket" "github.com/gnolang/gno/tm2/pkg/amino" @@ -25,7 +28,7 @@ import ( // RegisterRPCFuncs adds a route for each function in the funcMap, as well as general jsonrpc and websocket handlers for all functions. // "result" is the interface on which the result objects are registered, and is populated with every RPCResponse -func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc, logger log.Logger) { +func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc, logger *slog.Logger) { // HTTP endpoints for funcName, rpcFunc := range funcMap { mux.HandleFunc("/"+funcName, makeHTTPHandler(rpcFunc, logger)) @@ -99,9 +102,9 @@ func funcReturnTypes(f interface{}) []reflect.Type { // rpc.json // jsonrpc calls grab the given method's function info and runs reflect.Call -func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.HandlerFunc { +func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger *slog.Logger) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - b, err := ioutil.ReadAll(r.Body) + b, err := io.ReadAll(r.Body) if err != nil { WriteRPCResponseHTTP(w, types.RPCInvalidRequestError(types.JSONRPCStringID(""), errors.Wrap(err, "error reading request body"))) return @@ -256,7 +259,7 @@ func jsonParamsToArgs(rpcFunc *RPCFunc, raw []byte) ([]reflect.Value, error) { // rpc.http // convert from a function name to the http handler -func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWriter, *http.Request) { +func makeHTTPHandler(rpcFunc *RPCFunc, logger *slog.Logger) func(http.ResponseWriter, *http.Request) { // Exception for websocket endpoints if rpcFunc.ws { return func(w http.ResponseWriter, r *http.Request) { @@ -290,7 +293,7 @@ func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWrit } } -// Covert an http query to a list of properly typed values. +// Convert an http query to a list of properly typed values. // To be properly decoded the arg must be a concrete type from tendermint (if its an interface). func httpParamsToArgs(rpcFunc *RPCFunc, r *http.Request) ([]reflect.Value, error) { // skip types.Context @@ -356,9 +359,11 @@ func nonJSONStringToArg(rt reflect.Type, arg string) (reflect.Value, error, bool } } +var reInt = regexp.MustCompile(`^-?[0-9]+$`) + // NOTE: rt.Kind() isn't a pointer. func _nonJSONStringToArg(rt reflect.Type, arg string) (reflect.Value, error, bool) { - isIntString := RE_INT.Match([]byte(arg)) + isIntString := reInt.Match([]byte(arg)) isQuotedString := strings.HasPrefix(arg, `"`) && strings.HasSuffix(arg, `"`) isHexString := strings.HasPrefix(strings.ToLower(arg), "0x") @@ -758,7 +763,7 @@ type WebsocketManager struct { websocket.Upgrader funcMap map[string]*RPCFunc - logger log.Logger + logger *slog.Logger wsConnOptions []func(*wsConnection) } @@ -773,13 +778,13 @@ func NewWebsocketManager(funcMap map[string]*RPCFunc, wsConnOptions ...func(*wsC return true }, }, - logger: log.NewNopLogger(), + logger: log.NewNoopLogger(), wsConnOptions: wsConnOptions, } } // SetLogger sets the logger. -func (wm *WebsocketManager) SetLogger(l log.Logger) { +func (wm *WebsocketManager) SetLogger(l *slog.Logger) { wm.logger = l } diff --git a/tm2/pkg/bft/rpc/lib/server/handlers_test.go b/tm2/pkg/bft/rpc/lib/server/handlers_test.go index 8bf2cc0f7f1..75c64151619 100644 --- a/tm2/pkg/bft/rpc/lib/server/handlers_test.go +++ b/tm2/pkg/bft/rpc/lib/server/handlers_test.go @@ -1,9 +1,8 @@ package rpcserver_test import ( - "bytes" "encoding/json" - "io/ioutil" + "io" "net/http" "net/http/httptest" "strings" @@ -18,11 +17,11 @@ import ( "github.com/gnolang/gno/tm2/pkg/log" ) -////////////////////////////////////////////////////////////////////////////// +// ----------- // HTTP REST API // TODO -////////////////////////////////////////////////////////////////////////////// +// ----------- // JSON-RPC over HTTP func testMux() *http.ServeMux { @@ -30,9 +29,8 @@ func testMux() *http.ServeMux { "c": rs.NewRPCFunc(func(ctx *types.Context, s string, i int) (string, error) { return "foo", nil }, "s,i"), } mux := http.NewServeMux() - buf := new(bytes.Buffer) - logger := log.NewTMLogger(buf) - rs.RegisterRPCFuncs(mux, funcMap, logger) + + rs.RegisterRPCFuncs(mux, funcMap, log.NewNoopLogger()) return mux } @@ -43,6 +41,8 @@ func statusOK(code int) bool { return code >= 200 && code <= 299 } // do not crash our RPC handlers. // See Issue https://github.com/gnolang/gno/tm2/pkg/bft/issues/708. func TestRPCParams(t *testing.T) { + t.Parallel() + mux := testMux() tests := []struct { payload string @@ -70,7 +70,7 @@ func TestRPCParams(t *testing.T) { res := rec.Result() // Always expecting back a JSONRPCResponse assert.True(t, statusOK(res.StatusCode), "#%d: should always return 2XX", i) - blob, err := ioutil.ReadAll(res.Body) + blob, err := io.ReadAll(res.Body) if err != nil { t.Errorf("#%d: err reading body: %v", i, err) continue @@ -91,6 +91,8 @@ func TestRPCParams(t *testing.T) { } func TestJSONRPCID(t *testing.T) { + t.Parallel() + mux := testMux() tests := []struct { payload string @@ -118,7 +120,7 @@ func TestJSONRPCID(t *testing.T) { res := rec.Result() // Always expecting back a JSONRPCResponse assert.True(t, statusOK(res.StatusCode), "#%d: should always return 2XX", i) - blob, err := ioutil.ReadAll(res.Body) + blob, err := io.ReadAll(res.Body) if err != nil { t.Errorf("#%d: err reading body: %v", i, err) continue @@ -138,6 +140,8 @@ func TestJSONRPCID(t *testing.T) { } func TestRPCNotification(t *testing.T) { + t.Parallel() + mux := testMux() body := strings.NewReader(`{"jsonrpc": "2.0", "id": ""}`) req, _ := http.NewRequest("POST", "http://localhost/", body) @@ -147,12 +151,14 @@ func TestRPCNotification(t *testing.T) { // Always expecting back a JSONRPCResponse require.True(t, statusOK(res.StatusCode), "should always return 2XX") - blob, err := ioutil.ReadAll(res.Body) + blob, err := io.ReadAll(res.Body) require.Nil(t, err, "reading from the body should not give back an error") require.Equal(t, len(blob), 0, "a notification SHOULD NOT be responded to by the server") } func TestRPCNotificationInBatch(t *testing.T) { + t.Parallel() + mux := testMux() tests := []struct { payload string @@ -182,7 +188,7 @@ func TestRPCNotificationInBatch(t *testing.T) { res := rec.Result() // Always expecting back a JSONRPCResponse assert.True(t, statusOK(res.StatusCode), "#%d: should always return 2XX", i) - blob, err := ioutil.ReadAll(res.Body) + blob, err := io.ReadAll(res.Body) if err != nil { t.Errorf("#%d: err reading body: %v", i, err) continue @@ -219,6 +225,8 @@ func TestRPCNotificationInBatch(t *testing.T) { } func TestUnknownRPCPath(t *testing.T) { + t.Parallel() + mux := testMux() req, _ := http.NewRequest("GET", "http://localhost/unknownrpcpath", nil) rec := httptest.NewRecorder() @@ -229,10 +237,12 @@ func TestUnknownRPCPath(t *testing.T) { require.Equal(t, http.StatusNotFound, res.StatusCode, "should always return 404") } -////////////////////////////////////////////////////////////////////////////// +// ----------- // JSON-RPC over WEBSOCKETS func TestWebsocketManagerHandler(t *testing.T) { + t.Parallel() + s := newWSServer() defer s.Close() @@ -262,7 +272,7 @@ func newWSServer() *httptest.Server { "c": rs.NewWSRPCFunc(func(ctx *types.Context, s string, i int) (string, error) { return "foo", nil }, "s,i"), } wm := rs.NewWebsocketManager(funcMap) - wm.SetLogger(log.TestingLogger()) + wm.SetLogger(log.NewNoopLogger()) mux := http.NewServeMux() mux.HandleFunc("/websocket", wm.WebsocketHandler) diff --git a/tm2/pkg/bft/rpc/lib/server/http_params.go b/tm2/pkg/bft/rpc/lib/server/http_params.go index 32fdedd2f50..da6e98e7400 100644 --- a/tm2/pkg/bft/rpc/lib/server/http_params.go +++ b/tm2/pkg/bft/rpc/lib/server/http_params.go @@ -9,21 +9,6 @@ import ( "github.com/gnolang/gno/tm2/pkg/errors" ) -var ( - // Parts of regular expressions - atom = "[A-Z0-9!#$%&'*+\\-/=?^_`{|}~]+" - dotAtom = atom + `(?:\.` + atom + `)*` - domain = `[A-Z0-9.-]+\.[A-Z]{2,4}` - - RE_INT = regexp.MustCompile(`^-?[0-9]+$`) - RE_HEX = regexp.MustCompile(`^(?i)[a-f0-9]+$`) - RE_EMAIL = regexp.MustCompile(`^(?i)(` + dotAtom + `)@(` + dotAtom + `)$`) - RE_ADDRESS = regexp.MustCompile(`^(?i)[a-z0-9]{25,34}$`) - RE_HOST = regexp.MustCompile(`^(?i)(` + domain + `)$`) - - // RE_ID12 = regexp.MustCompile(`^[a-zA-Z0-9]{12}$`) -) - func GetParam(r *http.Request, param string) string { s := r.URL.Query().Get(param) if s == "" { diff --git a/tm2/pkg/bft/rpc/lib/server/http_server.go b/tm2/pkg/bft/rpc/lib/server/http_server.go index feaba1ba938..102a8203ac5 100644 --- a/tm2/pkg/bft/rpc/lib/server/http_server.go +++ b/tm2/pkg/bft/rpc/lib/server/http_server.go @@ -11,11 +11,12 @@ import ( "strings" "time" + "golang.org/x/exp/slog" + "golang.org/x/net/netutil" types "github.com/gnolang/gno/tm2/pkg/bft/rpc/lib/types" "github.com/gnolang/gno/tm2/pkg/errors" - "github.com/gnolang/gno/tm2/pkg/log" ) // Config is a RPC server configuration. @@ -47,7 +48,7 @@ func DefaultConfig() *Config { // StartHTTPServer takes a listener and starts an HTTP server with the given handler. // It wraps handler with RecoverAndLogHandler. // NOTE: This function blocks - you may want to call it in a go-routine. -func StartHTTPServer(listener net.Listener, handler http.Handler, logger log.Logger, config *Config) error { +func StartHTTPServer(listener net.Listener, handler http.Handler, logger *slog.Logger, config *Config) error { logger.Info(fmt.Sprintf("Starting RPC HTTP server on %s", listener.Addr())) s := &http.Server{ Handler: RecoverAndLogHandler(maxBytesHandler{h: handler, n: config.MaxBodyBytes}, logger), @@ -68,7 +69,7 @@ func StartHTTPAndTLSServer( listener net.Listener, handler http.Handler, certFile, keyFile string, - logger log.Logger, + logger *slog.Logger, config *Config, ) error { logger.Info(fmt.Sprintf("Starting RPC HTTPS server on %s (cert: %q, key: %q)", @@ -134,12 +135,12 @@ func WriteRPCResponseArrayHTTP(w http.ResponseWriter, res []types.RPCResponse) { } } -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // RecoverAndLogHandler wraps an HTTP handler, adding error logging. // If the inner function panics, the outer function recovers, logs, sends an // HTTP 500 error response. -func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler { +func RecoverAndLogHandler(handler http.Handler, logger *slog.Logger) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Wrap the ResponseWriter to remember the status rww := &ResponseWriterWrapper{-1, w} diff --git a/tm2/pkg/bft/rpc/lib/server/http_server_test.go b/tm2/pkg/bft/rpc/lib/server/http_server_test.go index 6753a339981..6c6d9ad14d6 100644 --- a/tm2/pkg/bft/rpc/lib/server/http_server_test.go +++ b/tm2/pkg/bft/rpc/lib/server/http_server_test.go @@ -1,11 +1,9 @@ package rpcserver import ( - "bytes" "crypto/tls" "fmt" "io" - "io/ioutil" "net" "net/http" "net/http/httptest" @@ -22,6 +20,8 @@ import ( ) func TestMaxOpenConnections(t *testing.T) { + t.Parallel() + const max = 5 // max simultaneous connections // Start the server. @@ -40,7 +40,7 @@ func TestMaxOpenConnections(t *testing.T) { l, err := Listen("tcp://127.0.0.1:0", config) require.NoError(t, err) defer l.Close() - go StartHTTPServer(l, mux, log.TestingLogger(), config) + go StartHTTPServer(l, mux, log.NewTestingLogger(t), config) // Make N GET calls to the server. attempts := max * 2 @@ -58,7 +58,7 @@ func TestMaxOpenConnections(t *testing.T) { return } defer r.Body.Close() - io.Copy(ioutil.Discard, r.Body) + io.Copy(io.Discard, r.Body) }() } wg.Wait() @@ -71,6 +71,8 @@ func TestMaxOpenConnections(t *testing.T) { } func TestStartHTTPAndTLSServer(t *testing.T) { + t.Parallel() + ln, err := net.Listen("tcp", "localhost:0") require.NoError(t, err) defer ln.Close() @@ -80,7 +82,7 @@ func TestStartHTTPAndTLSServer(t *testing.T) { fmt.Fprint(w, "some body") }) - go StartHTTPAndTLSServer(ln, mux, "test.crt", "test.key", log.TestingLogger(), DefaultConfig()) + go StartHTTPAndTLSServer(ln, mux, "test.crt", "test.key", log.NewTestingLogger(t), DefaultConfig()) tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, @@ -91,12 +93,14 @@ func TestStartHTTPAndTLSServer(t *testing.T) { defer res.Body.Close() assert.Equal(t, http.StatusOK, res.StatusCode) - body, err := ioutil.ReadAll(res.Body) + body, err := io.ReadAll(res.Body) require.NoError(t, err) assert.Equal(t, []byte("some body"), body) } func TestRecoverAndLogHandler(t *testing.T) { + t.Parallel() + tests := []struct { name string panicArg any @@ -156,18 +160,15 @@ func TestRecoverAndLogHandler(t *testing.T) { } }`, }, - { - name: "panic with nil", - panicArg: nil, - expectedResponse: ``, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var ( req, _ = http.NewRequest(http.MethodGet, "", nil) resp = httptest.NewRecorder() - logger = log.NewTMLogger(&bytes.Buffer{}) + logger = log.NewNoopLogger() // Create a handler that will always panic with argument tt.panicArg handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { panic(tt.panicArg) diff --git a/tm2/pkg/bft/rpc/lib/server/parse_test.go b/tm2/pkg/bft/rpc/lib/server/parse_test.go index a28dd255f23..aece3f20f3a 100644 --- a/tm2/pkg/bft/rpc/lib/server/parse_test.go +++ b/tm2/pkg/bft/rpc/lib/server/parse_test.go @@ -13,6 +13,8 @@ import ( ) func TestParseJSONMap(t *testing.T) { + t.Parallel() + input := []byte(`{"value":"1234","height":22}`) // naive is float,string @@ -101,6 +103,8 @@ func TestParseJSONMap(t *testing.T) { } func TestParseJSONArray(t *testing.T) { + t.Parallel() + input := []byte(`["1234",22]`) // naive is float,string @@ -134,6 +138,8 @@ func TestParseJSONArray(t *testing.T) { } func TestParseJSONRPC(t *testing.T) { + t.Parallel() + demo := func(ctx *types.Context, height int, name string) {} call := NewRPCFunc(demo, "height,name") @@ -170,6 +176,8 @@ func TestParseJSONRPC(t *testing.T) { } func TestParseURI(t *testing.T) { + t.Parallel() + demo := func(ctx *types.Context, height int, name string) {} call := NewRPCFunc(demo, "height,name") diff --git a/tm2/pkg/bft/rpc/lib/test/main.go b/tm2/pkg/bft/rpc/lib/test/main.go index fe7fc49c65e..3fd8ea0bf61 100644 --- a/tm2/pkg/bft/rpc/lib/test/main.go +++ b/tm2/pkg/bft/rpc/lib/test/main.go @@ -3,7 +3,6 @@ package main import ( "fmt" "net/http" - "os" "github.com/gnolang/gno/tm2/pkg/log" osm "github.com/gnolang/gno/tm2/pkg/os" @@ -27,7 +26,7 @@ type Result struct { func main() { var ( mux = http.NewServeMux() - logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + logger = log.NewNoopLogger() ) // Stop upon receiving SIGTERM or CTRL-C. diff --git a/tm2/pkg/bft/rpc/lib/types/types_test.go b/tm2/pkg/bft/rpc/lib/types/types_test.go index e047c69bd77..55ee8ed3945 100644 --- a/tm2/pkg/bft/rpc/lib/types/types_test.go +++ b/tm2/pkg/bft/rpc/lib/types/types_test.go @@ -31,6 +31,8 @@ var responseTests = []responseTest{ } func TestResponses(t *testing.T) { + t.Parallel() + assert := assert.New(t) for _, tt := range responseTests { jsonid := tt.id @@ -52,6 +54,8 @@ func TestResponses(t *testing.T) { } func TestUnmarshallResponses(t *testing.T) { + t.Parallel() + assert := assert.New(t) for _, tt := range responseTests { response := &RPCResponse{} @@ -66,6 +70,8 @@ func TestUnmarshallResponses(t *testing.T) { } func TestRPCError(t *testing.T) { + t.Parallel() + assert.Equal(t, "RPC error 12 - Badness: One worse than a code 11", fmt.Sprintf("%v", &RPCError{ Code: 12, diff --git a/tm2/pkg/bft/rpc/test/helpers.go b/tm2/pkg/bft/rpc/test/helpers.go index d8e1a498600..0b6d39c6839 100644 --- a/tm2/pkg/bft/rpc/test/helpers.go +++ b/tm2/pkg/bft/rpc/test/helpers.go @@ -28,7 +28,6 @@ type Options struct { var ( globalConfig *cfg.Config defaultOptions = Options{ - suppressStdout: false, recreateConfig: false, } ) @@ -114,13 +113,7 @@ func StopTendermint(node *nm.Node) { func NewTendermint(app abci.Application, opts *Options) *nm.Node { // Create & start node config := GetConfig(opts.recreateConfig) - var logger log.Logger - if opts.suppressStdout { - logger = log.NewNopLogger() - } else { - logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) - logger.SetLevel(log.LevelError) - } + pvKeyFile := config.PrivValidatorKeyFile() pvKeyStateFile := config.PrivValidatorStateFile() pv := privval.LoadOrGenFilePV(pvKeyFile, pvKeyStateFile) @@ -132,7 +125,7 @@ func NewTendermint(app abci.Application, opts *Options) *nm.Node { node, err := nm.NewNode(config, pv, nodeKey, papp, nm.DefaultGenesisDocProviderFunc(config), nm.DefaultDBProvider, - logger) + log.NewNoopLogger()) if err != nil { panic(err) } diff --git a/tm2/pkg/bft/state/eventstore/file/file.go b/tm2/pkg/bft/state/eventstore/file/file.go new file mode 100644 index 00000000000..f4cd74721f5 --- /dev/null +++ b/tm2/pkg/bft/state/eventstore/file/file.go @@ -0,0 +1,92 @@ +package file + +import ( + "fmt" + + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/autofile" + storetypes "github.com/gnolang/gno/tm2/pkg/bft/state/eventstore/types" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/errors" +) + +const ( + EventStoreType = "file" + Path = "path" +) + +var ( + errMissingPath = errors.New("missing path param") + errInvalidType = errors.New("invalid config for file event store specified") +) + +// TxEventStore is the implementation of a transaction event store +// that outputs to the local filesystem +type TxEventStore struct { + headPath string + group *autofile.Group +} + +// NewTxEventStore creates a new file-based tx event store +func NewTxEventStore(cfg *storetypes.Config) (*TxEventStore, error) { + // Parse config params + if EventStoreType != cfg.EventStoreType { + return nil, errInvalidType + } + + headPath, ok := cfg.GetParam(Path).(string) + if !ok { + return nil, errMissingPath + } + + return &TxEventStore{ + headPath: headPath, + }, nil +} + +// Start starts the file transaction event store, by opening the autofile group +func (t *TxEventStore) Start() error { + // Open the group + group, err := autofile.OpenGroup(t.headPath) + if err != nil { + return fmt.Errorf("unable to open file group for writing, %w", err) + } + + t.group = group + + return nil +} + +// Stop stops the file transaction event store, by closing the autofile group +func (t *TxEventStore) Stop() error { + // Close off the group + t.group.Close() + + return nil +} + +// GetType returns the file transaction event store type +func (t *TxEventStore) GetType() string { + return EventStoreType +} + +// Append marshals the transaction using amino, and writes it to the disk +func (t *TxEventStore) Append(tx types.TxResult) error { + // Serialize the transaction using amino + txRaw, err := amino.MarshalJSON(tx) + if err != nil { + return fmt.Errorf("unable to marshal transaction, %w", err) + } + + // Write the serialized transaction info to the file group + if err = t.group.WriteLine(string(txRaw)); err != nil { + return fmt.Errorf("unable to save transaction event, %w", err) + } + + // Flush output to storage + if err := t.group.FlushAndSync(); err != nil { + return fmt.Errorf("unable to flush and sync transaction event, %w", err) + } + + return nil +} diff --git a/tm2/pkg/bft/state/eventstore/file/file_test.go b/tm2/pkg/bft/state/eventstore/file/file_test.go new file mode 100644 index 00000000000..46d87582ce4 --- /dev/null +++ b/tm2/pkg/bft/state/eventstore/file/file_test.go @@ -0,0 +1,140 @@ +package file + +import ( + "bufio" + "testing" + + "github.com/gnolang/gno/tm2/pkg/amino" + storetypes "github.com/gnolang/gno/tm2/pkg/bft/state/eventstore/types" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/testutils" + "github.com/stretchr/testify/assert" +) + +// generateTestTransactions generates random transaction results +func generateTestTransactions(count int) []types.TxResult { + txs := make([]types.TxResult, count) + + for i := 0; i < count; i++ { + txs[i] = types.TxResult{} + } + + return txs +} + +func TestTxEventStore_New(t *testing.T) { + t.Parallel() + + t.Run("invalid file path specified", func(t *testing.T) { + t.Parallel() + + cfg := &storetypes.Config{ + EventStoreType: "invalid", + } + + i, err := NewTxEventStore(cfg) + + assert.Nil(t, i) + assert.ErrorIs(t, err, errInvalidType) + }) + + t.Run("invalid file path specified", func(t *testing.T) { + t.Parallel() + + cfg := &storetypes.Config{ + EventStoreType: EventStoreType, + Params: nil, + } + + i, err := NewTxEventStore(cfg) + + assert.Nil(t, i) + assert.ErrorIs(t, err, errMissingPath) + }) + + t.Run("valid file path specified", func(t *testing.T) { + t.Parallel() + + headPath := "." + + cfg := &storetypes.Config{ + EventStoreType: EventStoreType, + Params: map[string]any{ + Path: headPath, + }, + } + + i, err := NewTxEventStore(cfg) + if i == nil { + t.Fatalf("unable to create event store") + } + + assert.NoError(t, err) + assert.Equal(t, headPath, i.headPath) + assert.Equal(t, EventStoreType, i.GetType()) + }) +} + +func TestTxEventStore_Append(t *testing.T) { + t.Parallel() + + headFile, cleanup := testutils.NewTestFile(t) + t.Cleanup(func() { + cleanup() + }) + + eventStore, err := NewTxEventStore(&storetypes.Config{ + EventStoreType: EventStoreType, + Params: map[string]any{ + Path: headFile.Name(), + }, + }) + if err != nil { + t.Fatalf("unable to create tx event store, %v", err) + } + + // Start the event store + if err = eventStore.Start(); err != nil { + t.Fatalf("unable to start event store, %v", err) + } + + t.Cleanup(func() { + // Stop the event store + if err = eventStore.Stop(); err != nil { + t.Fatalf("unable to stop event store gracefully, %v", err) + } + }) + + numTxs := 10 + txs := generateTestTransactions(numTxs) + + for _, tx := range txs { + if err = eventStore.Append(tx); err != nil { + t.Fatalf("unable to store transaction, %v", err) + } + } + + // Make sure the file group's size is valid + if eventStore.group.ReadGroupInfo().TotalSize == 0 { + t.Fatalf("invalid group size") + } + + // Open file for reading + scanner := bufio.NewScanner(headFile) + + linesRead := 0 + for scanner.Scan() { + line := scanner.Bytes() + + var txRes types.TxResult + if err = amino.UnmarshalJSON(line, &txRes); err != nil { + t.Fatalf("unable to read store line") + } + + assert.Equal(t, txs[linesRead], txRes) + + linesRead++ + } + + assert.Equal(t, numTxs, linesRead) +} diff --git a/tm2/pkg/bft/state/eventstore/mock_test.go b/tm2/pkg/bft/state/eventstore/mock_test.go new file mode 100644 index 00000000000..087d8f6e3e9 --- /dev/null +++ b/tm2/pkg/bft/state/eventstore/mock_test.go @@ -0,0 +1,89 @@ +package eventstore + +import ( + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/events" + "github.com/gnolang/gno/tm2/pkg/service" +) + +// TxEventStore // + +type ( + startDelegate func() error + stopDelegate func() error + getTypeDelegate func() string + appendDelegate func(types.TxResult) error +) + +type mockEventStore struct { + startFn startDelegate + stopFn stopDelegate + getTypeFn getTypeDelegate + appendFn appendDelegate +} + +func (m mockEventStore) Start() error { + if m.startFn != nil { + return m.startFn() + } + + return nil +} + +func (m mockEventStore) Stop() error { + if m.stopFn != nil { + return m.stopFn() + } + + return nil +} + +func (m mockEventStore) GetType() string { + if m.getTypeFn != nil { + return m.getTypeFn() + } + + return "" +} + +func (m mockEventStore) Append(result types.TxResult) error { + if m.appendFn != nil { + return m.appendFn(result) + } + + return nil +} + +// EventSwitch // + +type ( + fireEventDelegate func(events.Event) + addListenerDelegate func(string, events.EventCallback) + removeListenerDelegate func(string) +) + +type mockEventSwitch struct { + service.BaseService + + fireEventFn fireEventDelegate + addListenerFn addListenerDelegate + removeListenerFn removeListenerDelegate +} + +func (m *mockEventSwitch) FireEvent(ev events.Event) { + if m.fireEventFn != nil { + m.fireEventFn(ev) + } +} + +func (m *mockEventSwitch) AddListener(listenerID string, cb events.EventCallback) { + if m.addListenerFn != nil { + m.addListenerFn(listenerID, cb) + } +} + +func (m *mockEventSwitch) RemoveListener(listenerID string) { + if m.removeListenerFn != nil { + m.removeListenerFn(listenerID) + } +} diff --git a/tm2/pkg/bft/state/eventstore/null/null.go b/tm2/pkg/bft/state/eventstore/null/null.go new file mode 100644 index 00000000000..40e3566d89e --- /dev/null +++ b/tm2/pkg/bft/state/eventstore/null/null.go @@ -0,0 +1,35 @@ +package null + +import ( + "github.com/gnolang/gno/tm2/pkg/bft/state/eventstore" + "github.com/gnolang/gno/tm2/pkg/bft/types" +) + +var _ eventstore.TxEventStore = (*TxEventStore)(nil) + +const ( + EventStoreType = "none" +) + +// TxEventStore acts as a /dev/null +type TxEventStore struct{} + +func NewNullEventStore() *TxEventStore { + return &TxEventStore{} +} + +func (t TxEventStore) Start() error { + return nil +} + +func (t TxEventStore) Stop() error { + return nil +} + +func (t TxEventStore) Append(_ types.TxResult) error { + return nil +} + +func (t TxEventStore) GetType() string { + return EventStoreType +} diff --git a/tm2/pkg/bft/state/eventstore/store.go b/tm2/pkg/bft/state/eventstore/store.go new file mode 100644 index 00000000000..10ef9eefc9b --- /dev/null +++ b/tm2/pkg/bft/state/eventstore/store.go @@ -0,0 +1,24 @@ +package eventstore + +import "github.com/gnolang/gno/tm2/pkg/bft/types" + +const ( + StatusOn = "on" + StatusOff = "off" +) + +// TxEventStore stores transaction events for later processing +type TxEventStore interface { + // Start starts the transaction event store + Start() error + + // Stop stops the transaction event store + Stop() error + + // GetType returns the event store type + GetType() string + + // Append analyzes and appends a single transaction + // to the event store + Append(result types.TxResult) error +} diff --git a/tm2/pkg/bft/state/eventstore/store_service.go b/tm2/pkg/bft/state/eventstore/store_service.go new file mode 100644 index 00000000000..d6ed40c4151 --- /dev/null +++ b/tm2/pkg/bft/state/eventstore/store_service.go @@ -0,0 +1,84 @@ +package eventstore + +import ( + "context" + "fmt" + + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/events" + "github.com/gnolang/gno/tm2/pkg/service" +) + +// Service connects the event bus and event store together in order +// to store events coming from event bus +type Service struct { + service.BaseService + + cancelFn context.CancelFunc + + txEventStore TxEventStore + evsw events.EventSwitch +} + +// NewEventStoreService returns a new service instance +func NewEventStoreService(idr TxEventStore, evsw events.EventSwitch) *Service { + is := &Service{txEventStore: idr, evsw: evsw} + is.BaseService = *service.NewBaseService(nil, "EventStoreService", is) + + return is +} + +func (is *Service) OnStart() error { + // Create a context for the intermediary monitor service + ctx, cancelFn := context.WithCancel(context.Background()) + is.cancelFn = cancelFn + + // Start the event store + if err := is.txEventStore.Start(); err != nil { + return fmt.Errorf("unable to start transaction event store, %w", err) + } + + // Start the intermediary monitor service + go is.monitorTxEvents(ctx) + + return nil +} + +func (is *Service) OnStop() { + // Close off any routines + is.cancelFn() + + // Attempt to gracefully stop the event store + if err := is.txEventStore.Stop(); err != nil { + is.Logger.Error( + fmt.Sprintf("unable to gracefully stop event store, %v", err), + ) + } +} + +// monitorTxEvents acts as an intermediary feed service for the supplied +// event store. It relays transaction events that come from the event stream +func (is *Service) monitorTxEvents(ctx context.Context) { + // Create a subscription for transaction events + subCh := events.SubscribeToEvent(is.evsw, "tx-event-store", types.EventTx{}) + + for { + select { + case <-ctx.Done(): + return + case evRaw := <-subCh: + // Cast the event + ev, ok := evRaw.(types.EventTx) + if !ok { + is.Logger.Error("invalid transaction result type cast") + + continue + } + + // Alert the actual tx event store + if err := is.txEventStore.Append(ev.Result); err != nil { + is.Logger.Error("unable to store transaction", "err", err) + } + } + } +} diff --git a/tm2/pkg/bft/state/eventstore/store_service_test.go b/tm2/pkg/bft/state/eventstore/store_service_test.go new file mode 100644 index 00000000000..3fa5e8a7941 --- /dev/null +++ b/tm2/pkg/bft/state/eventstore/store_service_test.go @@ -0,0 +1,159 @@ +package eventstore + +import ( + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/events" + "github.com/stretchr/testify/assert" +) + +// generateTxEvents generates random transaction events +func generateTxEvents(count int) []types.EventTx { + txEvents := make([]types.EventTx, count) + + for i := 0; i < count; i++ { + txEvents[i] = types.EventTx{ + Result: types.TxResult{}, + } + } + + return txEvents +} + +func TestEventStoreService_Monitor(t *testing.T) { + t.Parallel() + + const defaultTimeout = 5 * time.Second + + var ( + startCalled = false + stopCalled = false + receivedResults = make([]types.TxResult, 0) + receivedSize atomic.Int64 + + cb events.EventCallback + cbSet atomic.Bool + + mockEventStore = &mockEventStore{ + startFn: func() error { + startCalled = true + + return nil + }, + stopFn: func() error { + stopCalled = true + + return nil + }, + appendFn: func(result types.TxResult) error { + receivedResults = append(receivedResults, result) + + // Atomic because we are accessing this size from a routine + receivedSize.Store(int64(len(receivedResults))) + + return nil + }, + } + mockEventSwitch = &mockEventSwitch{ + fireEventFn: func(event events.Event) { + // Exec the callback on event fire + cb(event) + }, + addListenerFn: func(_ string, callback events.EventCallback) { + // Attach callback + cb = callback + + // Atomic because we are accessing this info from a routine + cbSet.Store(true) + }, + } + ) + + // Create a new event store instance + i := NewEventStoreService(mockEventStore, mockEventSwitch) + if i == nil { + t.Fatal("unable to create event store service") + } + + // Start the event store + if err := i.OnStart(); err != nil { + t.Fatalf("unable to start event store, %v", err) + } + + assert.True(t, startCalled) + + t.Cleanup(func() { + // Stop the event store + i.OnStop() + + assert.True(t, stopCalled) + }) + + // Fire off the events so the event store can catch them + numEvents := 1000 + txEvents := generateTxEvents(numEvents) + + var wg sync.WaitGroup + + // Start a routine that asynchronously pushes events + wg.Add(1) + go func() { + defer wg.Done() + + timeout := time.After(defaultTimeout) + + for { + select { + case <-timeout: + return + default: + // If the callback is set, fire the events + if !cbSet.Load() { + // Listener not set yet + continue + } + + for _, event := range txEvents { + mockEventSwitch.FireEvent(event) + } + + return + } + } + }() + + // Start a routine that monitors received results + wg.Add(1) + go func() { + defer wg.Done() + + timeout := time.After(defaultTimeout) + + for { + select { + case <-timeout: + return + default: + if int(receivedSize.Load()) == numEvents { + return + } + } + } + }() + + wg.Wait() + + // Make sure all results were received + if len(receivedResults) != numEvents { + t.Fatalf("invalid number of results received, %d", len(receivedResults)) + } + + // Make sure all results match + for index, event := range txEvents { + assert.Equal(t, event.Result, receivedResults[index]) + } +} diff --git a/tm2/pkg/bft/state/eventstore/types/config.go b/tm2/pkg/bft/state/eventstore/types/config.go new file mode 100644 index 00000000000..5b152f254fd --- /dev/null +++ b/tm2/pkg/bft/state/eventstore/types/config.go @@ -0,0 +1,30 @@ +package types + +import "github.com/gnolang/gno/tm2/pkg/bft/state/eventstore/null" + +// EventStoreParams defines the arbitrary event store config params +type EventStoreParams map[string]any + +// Config defines the specific event store configuration +type Config struct { + EventStoreType string `toml:"event_store_type" comment:"Type of event store"` + Params EventStoreParams `toml:"event_store_params" comment:"Event store parameters"` +} + +// GetParam fetches the specific config param, if any. +// Returns nil if the param is not present +func (c *Config) GetParam(name string) any { + if c.Params != nil { + return c.Params[name] + } + + return nil +} + +// DefaultEventStoreConfig returns the default event store config +func DefaultEventStoreConfig() *Config { + return &Config{ + EventStoreType: null.EventStoreType, + Params: make(EventStoreParams), + } +} diff --git a/tm2/pkg/bft/state/eventstore/types/config_test.go b/tm2/pkg/bft/state/eventstore/types/config_test.go new file mode 100644 index 00000000000..0f5683b7c61 --- /dev/null +++ b/tm2/pkg/bft/state/eventstore/types/config_test.go @@ -0,0 +1,45 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConfig_GetParam(t *testing.T) { + t.Parallel() + + const paramName = "param" + + testTable := []struct { + name string + cfg *Config + + expectedParam any + }{ + { + "param not set", + &Config{}, + nil, + }, + { + "valid param set", + &Config{ + Params: map[string]any{ + paramName: 10, + }, + }, + 10, + }, + } + + for _, testCase := range testTable { + testCase := testCase + + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + + assert.Equal(t, testCase.expectedParam, testCase.cfg.GetParam(paramName)) + }) + } +} diff --git a/tm2/pkg/bft/state/execution.go b/tm2/pkg/bft/state/execution.go index e7920459172..d831fc3678e 100644 --- a/tm2/pkg/bft/state/execution.go +++ b/tm2/pkg/bft/state/execution.go @@ -3,6 +3,8 @@ package state import ( "fmt" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/amino" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" "github.com/gnolang/gno/tm2/pkg/bft/fail" @@ -14,10 +16,9 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto" dbm "github.com/gnolang/gno/tm2/pkg/db" "github.com/gnolang/gno/tm2/pkg/events" - "github.com/gnolang/gno/tm2/pkg/log" ) -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // BlockExecutor handles block execution and state updates. // It exposes ApplyBlock(), which validates & executes the block, updates state w/ ABCI responses, // then commits and updates the mempool atomically, then saves state. @@ -37,14 +38,14 @@ type BlockExecutor struct { // and update both with block results after commit. mempool mempl.Mempool - logger log.Logger + logger *slog.Logger } type BlockExecutorOption func(executor *BlockExecutor) // NewBlockExecutor returns a new BlockExecutor with a NopEventBus. // Call SetEventBus to provide one. -func NewBlockExecutor(db dbm.DB, logger log.Logger, proxyApp proxy.AppConnConsensus, mempool mempl.Mempool, options ...BlockExecutorOption) *BlockExecutor { +func NewBlockExecutor(db dbm.DB, logger *slog.Logger, proxyApp proxy.AppConnConsensus, mempool mempl.Mempool, options ...BlockExecutorOption) *BlockExecutor { res := &BlockExecutor{ db: db, proxyApp: proxyApp, @@ -143,7 +144,7 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b // Events are fired after everything else. // NOTE: if we crash between Commit and Save, events wont be fired during replay - fireEvents(blockExec.logger, blockExec.evsw, block, abciResponses) + fireEvents(blockExec.evsw, block, abciResponses) return state, nil } @@ -200,13 +201,13 @@ func (blockExec *BlockExecutor) Commit( return res.Data, err } -//--------------------------------------------------------- +// --------------------------------------------------------- // Helper functions for executing blocks and updating state // Executes block's transactions on proxyAppConn. // Returns a list of transaction results and updates to the validator set func execBlockOnProxyApp( - logger log.Logger, + logger *slog.Logger, proxyAppConn proxy.AppConnConsensus, block *types.Block, stateDB dbm.DB, @@ -398,7 +399,7 @@ func updateState( // Fire NewBlock, NewBlockHeader. // Fire TxEvent for every tx. // NOTE: if Tendermint crashes before commit, some or all of these events may be published again. -func fireEvents(logger log.Logger, evsw events.EventSwitch, block *types.Block, abciResponses *ABCIResponses) { +func fireEvents(evsw events.EventSwitch, block *types.Block, abciResponses *ABCIResponses) { evsw.FireEvent(types.EventNewBlock{ Block: block, ResultBeginBlock: abciResponses.BeginBlock, @@ -425,7 +426,7 @@ func fireEvents(logger log.Logger, evsw events.EventSwitch, block *types.Block, } } -//---------------------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------------------- // Execute block without state. TODO: eliminate // ExecCommitBlock executes and commits a block on the proxyApp without validating or mutating the state. @@ -433,7 +434,7 @@ func fireEvents(logger log.Logger, evsw events.EventSwitch, block *types.Block, func ExecCommitBlock( appConnConsensus proxy.AppConnConsensus, block *types.Block, - logger log.Logger, + logger *slog.Logger, stateDB dbm.DB, ) ([]byte, error) { _, err := execBlockOnProxyApp(logger, appConnConsensus, block, stateDB) diff --git a/tm2/pkg/bft/state/execution_test.go b/tm2/pkg/bft/state/execution_test.go index 849c87d3359..18dd8746840 100644 --- a/tm2/pkg/bft/state/execution_test.go +++ b/tm2/pkg/bft/state/execution_test.go @@ -29,6 +29,8 @@ var ( ) func TestApplyBlock(t *testing.T) { + t.Parallel() + cc := proxy.NewLocalClientCreator(kvstore.NewKVStoreApplication()) proxyApp := proxy.NewAppConns(cc) err := proxyApp.Start() @@ -37,7 +39,7 @@ func TestApplyBlock(t *testing.T) { state, stateDB, _ := makeState(1, 1) - blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}) + blockExec := sm.NewBlockExecutor(stateDB, log.NewTestingLogger(t), proxyApp.Consensus(), mock.Mempool{}) evsw := events.NewEventSwitch() blockExec.SetEventSwitch(evsw) @@ -53,6 +55,8 @@ func TestApplyBlock(t *testing.T) { // TestBeginBlockValidators ensures we send absent validators list. func TestBeginBlockValidators(t *testing.T) { + t.Parallel() + app := &testApp{} cc := proxy.NewLocalClientCreator(app) proxyApp := proxy.NewAppConns(cc) @@ -86,7 +90,7 @@ func TestBeginBlockValidators(t *testing.T) { // block for height 2 block, _ := state.MakeBlock(2, makeTxs(2), lastCommit, state.Validators.GetProposer().Address) - _, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB) + _, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.NewTestingLogger(t), stateDB) require.Nil(t, err, tc.desc) // -> app receives a list of validators with a bool indicating if they signed @@ -104,6 +108,8 @@ func TestBeginBlockValidators(t *testing.T) { } func TestValidateValidatorUpdates(t *testing.T) { + t.Parallel() + pubkey1 := ed25519.GenPrivKey().PubKey() pubkey2 := ed25519.GenPrivKey().PubKey() @@ -164,6 +170,8 @@ func TestValidateValidatorUpdates(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() + err := sm.ValidateValidatorUpdates(tc.abciUpdates, tc.validatorParams) if tc.shouldErr { assert.Error(t, err) @@ -175,6 +183,8 @@ func TestValidateValidatorUpdates(t *testing.T) { } func TestUpdateValidators(t *testing.T) { + t.Parallel() + pubkey1 := ed25519.GenPrivKey().PubKey() val1 := types.NewValidator(pubkey1, 10) pubkey2 := ed25519.GenPrivKey().PubKey() @@ -230,6 +240,8 @@ func TestUpdateValidators(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() + err := tc.currentSet.UpdateWithABCIValidatorUpdates(tc.abciUpdates) if tc.shouldErr { assert.Error(t, err) @@ -250,6 +262,8 @@ func TestUpdateValidators(t *testing.T) { // TestEndBlockValidatorUpdates ensures we update validator set and send an event. func TestEndBlockValidatorUpdates(t *testing.T) { + t.Parallel() + app := &testApp{} cc := proxy.NewLocalClientCreator(app) proxyApp := proxy.NewAppConns(cc) @@ -259,7 +273,7 @@ func TestEndBlockValidatorUpdates(t *testing.T) { state, stateDB, _ := makeState(1, 1) - blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}) + blockExec := sm.NewBlockExecutor(stateDB, log.NewTestingLogger(t), proxyApp.Consensus(), mock.Mempool{}) evsw := events.NewEventSwitch() err = evsw.Start() @@ -316,6 +330,8 @@ LOOP: // TestEndBlockValidatorUpdatesResultingInEmptySet checks that processing validator updates that // would result in empty set causes no panic, an error is raised and NextValidators is not updated func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) { + t.Parallel() + app := &testApp{} cc := proxy.NewLocalClientCreator(app) proxyApp := proxy.NewAppConns(cc) @@ -324,7 +340,7 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) { defer proxyApp.Stop() state, stateDB, _ := makeState(1, 1) - blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}) + blockExec := sm.NewBlockExecutor(stateDB, log.NewTestingLogger(t), proxyApp.Consensus(), mock.Mempool{}) block := makeBlock(state, 1) blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} diff --git a/tm2/pkg/bft/state/helpers_test.go b/tm2/pkg/bft/state/helpers_test.go index 219baaf0f55..ca4175185a9 100644 --- a/tm2/pkg/bft/state/helpers_test.go +++ b/tm2/pkg/bft/state/helpers_test.go @@ -8,7 +8,6 @@ import ( "github.com/gnolang/gno/tm2/pkg/bft/proxy" sm "github.com/gnolang/gno/tm2/pkg/bft/state" "github.com/gnolang/gno/tm2/pkg/bft/types" - tmtime "github.com/gnolang/gno/tm2/pkg/bft/types/time" "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" dbm "github.com/gnolang/gno/tm2/pkg/db" @@ -190,24 +189,7 @@ func makeHeaderPartsResponsesParams(state sm.State, params abci.ConsensusParams) return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses } -func randomGenesisDoc() *types.GenesisDoc { - pubkey := ed25519.GenPrivKey().PubKey() - return &types.GenesisDoc{ - GenesisTime: tmtime.Now(), - ChainID: "abc", - Validators: []types.GenesisValidator{ - { - Address: pubkey.Address(), - PubKey: pubkey, - Power: 10, - Name: "myval", - }, - }, - ConsensusParams: types.DefaultConsensusParams(), - } -} - -//---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- type testApp struct { abci.BaseApplication diff --git a/tm2/pkg/bft/state/state_test.go b/tm2/pkg/bft/state/state_test.go index a48f4f0a3e4..d19b8526d98 100644 --- a/tm2/pkg/bft/state/state_test.go +++ b/tm2/pkg/bft/state/state_test.go @@ -42,6 +42,8 @@ func setupTestCase(t *testing.T) (func(t *testing.T), dbm.DB, sm.State) { // TestStateCopy tests the correct copying behaviour of State. func TestStateCopy(t *testing.T) { + t.Parallel() + t.Helper() tearDown, _, state := setupTestCase(t) @@ -62,6 +64,8 @@ func TestStateCopy(t *testing.T) { // TestMakeGenesisStateNilValidators tests state's consistency when genesis file's validators field is nil. func TestMakeGenesisStateNilValidators(t *testing.T) { + t.Parallel() + doc := types.GenesisDoc{ ChainID: "dummy", Validators: nil, @@ -75,6 +79,8 @@ func TestMakeGenesisStateNilValidators(t *testing.T) { // TestStateSaveLoad tests saving and loading State from a db. func TestStateSaveLoad(t *testing.T) { + t.Parallel() + tearDown, stateDB, state := setupTestCase(t) defer tearDown(t) //nolint: vetshadow @@ -91,6 +97,8 @@ func TestStateSaveLoad(t *testing.T) { // TestABCIResponsesSaveLoad tests saving and loading ABCIResponses. func TestABCIResponsesSaveLoad1(t *testing.T) { + t.Parallel() + tearDown, stateDB, state := setupTestCase(t) defer tearDown(t) //nolint: vetshadow @@ -129,6 +137,8 @@ func TestABCIResponsesSaveLoad1(t *testing.T) { // TestResultsSaveLoad tests saving and loading ABCI results. func TestABCIResponsesSaveLoad2(t *testing.T) { + t.Parallel() + tearDown, stateDB, _ := setupTestCase(t) defer tearDown(t) //nolint: vetshadow @@ -223,6 +233,8 @@ func TestABCIResponsesSaveLoad2(t *testing.T) { // TestValidatorSimpleSaveLoad tests saving and loading validators. func TestValidatorSimpleSaveLoad(t *testing.T) { + t.Parallel() + tearDown, stateDB, state := setupTestCase(t) defer tearDown(t) //nolint: vetshadow @@ -256,6 +268,8 @@ func TestValidatorSimpleSaveLoad(t *testing.T) { // TestValidatorChangesSaveLoad tests saving and loading a validator set with changes. func TestOneValidatorChangesSaveLoad(t *testing.T) { + t.Parallel() + tearDown, stateDB, state := setupTestCase(t) defer tearDown(t) @@ -309,6 +323,8 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) { } func TestProposerFrequency(t *testing.T) { + t.Parallel() + // some explicit test cases testCases := []struct { powers []int64 @@ -432,6 +448,8 @@ func testProposerFreq(t *testing.T, caseNum int, valSet *types.ValidatorSet) { // TestProposerPriorityDoesNotGetResetToZero assert that we preserve accum when calling updateState // see https://github.com/tendermint/classic/issues/2718 func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) { + t.Parallel() + tearDown, _, state := setupTestCase(t) defer tearDown(t) val1VotingPower := int64(10) @@ -534,6 +552,8 @@ func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) { } func TestProposerPriorityProposerAlternates(t *testing.T) { + t.Parallel() + // Regression test that would fail if the inner workings of // IncrementProposerPriority change. // Additionally, make sure that same power validators alternate if both @@ -670,6 +690,8 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { } func TestLargeGenesisValidator(t *testing.T) { + t.Parallel() + tearDown, _, state := setupTestCase(t) defer tearDown(t) @@ -822,6 +844,8 @@ func TestLargeGenesisValidator(t *testing.T) { } func TestStoreLoadValidatorsIncrementsProposerPriority(t *testing.T) { + t.Parallel() + const valSetSize = 2 tearDown, stateDB, state := setupTestCase(t) defer tearDown(t) @@ -845,6 +869,8 @@ func TestStoreLoadValidatorsIncrementsProposerPriority(t *testing.T) { // TestValidatorChangesSaveLoad tests saving and loading a validator set with // changes. func TestManyValidatorChangesSaveLoad(t *testing.T) { + t.Parallel() + const valSetSize = 7 tearDown, stateDB, state := setupTestCase(t) defer tearDown(t) @@ -890,6 +916,8 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) { } func TestStateMakeBlock(t *testing.T) { + t.Parallel() + tearDown, _, state := setupTestCase(t) defer tearDown(t) @@ -905,6 +933,8 @@ func TestStateMakeBlock(t *testing.T) { // TestConsensusParamsChangesSaveLoad tests saving and loading consensus params // with changes. func TestConsensusParamsChangesSaveLoad(t *testing.T) { + t.Parallel() + tearDown, stateDB, state := setupTestCase(t) defer tearDown(t) @@ -964,6 +994,8 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) { } func TestApplyUpdates(t *testing.T) { + t.Parallel() + initParams := makeConsensusParams(1, 2, 3, 3, 4) cases := [...]struct { diff --git a/tm2/pkg/bft/state/store_test.go b/tm2/pkg/bft/state/store_test.go index 79282e6a311..ed3b8e63311 100644 --- a/tm2/pkg/bft/state/store_test.go +++ b/tm2/pkg/bft/state/store_test.go @@ -15,6 +15,8 @@ import ( ) func TestStoreLoadValidators(t *testing.T) { + t.Parallel() + stateDB := dbm.NewMemDB() val, _ := types.RandValidator(true, 10) vals := types.NewValidatorSet([]*types.Validator{val}) diff --git a/tm2/pkg/bft/state/txindex/indexer.go b/tm2/pkg/bft/state/txindex/indexer.go deleted file mode 100644 index 2b5b4aae220..00000000000 --- a/tm2/pkg/bft/state/txindex/indexer.go +++ /dev/null @@ -1,18 +0,0 @@ -package txindex - -// TxIndexer interface defines methods to index and search transactions. -type TxIndexer interface { /* - // AddBatch analyzes, indexes and stores a batch of transactions. - AddBatch(b *Batch) error - - // Index analyzes, indexes and stores a single transaction. - Index(result *types.TxResult) error - - // Get returns the transaction specified by hash or nil if the transaction is not indexed - // or stored. - Get(hash []byte) (*types.TxResult, error) - - // Search allows you to query for transactions. - Search(q *query.Query) ([]*types.TxResult, error) - */ -} diff --git a/tm2/pkg/bft/state/txindex/indexer_service.go b/tm2/pkg/bft/state/txindex/indexer_service.go deleted file mode 100644 index fb5c3068ae4..00000000000 --- a/tm2/pkg/bft/state/txindex/indexer_service.go +++ /dev/null @@ -1,31 +0,0 @@ -package txindex - -import ( - "github.com/gnolang/gno/tm2/pkg/events" - "github.com/gnolang/gno/tm2/pkg/service" -) - -// IndexerService connects event bus and transaction indexer together in order -// to index transactions coming from event bus. -type IndexerService struct { - service.BaseService - - idr TxIndexer - evsw events.EventSwitch -} - -// NewIndexerService returns a new service instance. -func NewIndexerService(idr TxIndexer, evsw events.EventSwitch) *IndexerService { - is := &IndexerService{idr: idr, evsw: evsw} - is.BaseService = *service.NewBaseService(nil, "IndexerService", is) - return is -} - -func (is *IndexerService) OnStart() error { - // TODO - return nil -} - -func (is *IndexerService) OnStop() { - // TODO -} diff --git a/tm2/pkg/bft/state/txindex/null/null.go b/tm2/pkg/bft/state/txindex/null/null.go deleted file mode 100644 index ed90013b9a9..00000000000 --- a/tm2/pkg/bft/state/txindex/null/null.go +++ /dev/null @@ -1,31 +0,0 @@ -package null - -import ( - "github.com/gnolang/gno/tm2/pkg/bft/state/txindex" -) - -var _ txindex.TxIndexer = (*TxIndex)(nil) - -// TxIndex acts as a /dev/null. -type TxIndex struct{} - -/* -// Get on a TxIndex is disabled and panics when invoked. -func (txi *TxIndex) Get(hash []byte) (*types.TxResult, error) { - return nil, errors.New(`Indexing is disabled (set 'tx_index = "kv"' in config)`) -} - -// AddBatch is a noop and always returns nil. -func (txi *TxIndex) AddBatch(batch *txindex.Batch) error { - return nil -} - -// Index is a noop and always returns nil. -func (txi *TxIndex) Index(result *types.TxResult) error { - return nil -} - -func (txi *TxIndex) Search(q *query.Query) ([]*types.TxResult, error) { - return []*types.TxResult{}, nil -} -*/ diff --git a/tm2/pkg/bft/state/validation_test.go b/tm2/pkg/bft/state/validation_test.go index 94aafe92694..7ab9d1035ee 100644 --- a/tm2/pkg/bft/state/validation_test.go +++ b/tm2/pkg/bft/state/validation_test.go @@ -19,12 +19,14 @@ import ( const validationTestsStopHeight int64 = 10 func TestValidateBlockHeader(t *testing.T) { + t.Parallel() + proxyApp := newTestApp() require.NoError(t, proxyApp.Start()) defer proxyApp.Stop() state, stateDB, privVals := makeState(3, 1) - blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}) + blockExec := sm.NewBlockExecutor(stateDB, log.NewTestingLogger(t), proxyApp.Consensus(), mock.Mempool{}) lastCommit := types.NewCommit(types.BlockID{}, nil) // some bad values @@ -80,12 +82,14 @@ func TestValidateBlockHeader(t *testing.T) { } func TestValidateBlockCommit(t *testing.T) { + t.Parallel() + proxyApp := newTestApp() require.NoError(t, proxyApp.Start()) defer proxyApp.Stop() state, stateDB, privVals := makeState(1, 1) - blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}) + blockExec := sm.NewBlockExecutor(stateDB, log.NewTestingLogger(t), proxyApp.Consensus(), mock.Mempool{}) lastCommit := types.NewCommit(types.BlockID{}, nil) wrongPrecommitsCommit := types.NewCommit(types.BlockID{}, nil) badPrivVal := types.NewMockPV() diff --git a/tm2/pkg/bft/store/store_test.go b/tm2/pkg/bft/store/store_test.go index 1dc0bda833c..6b5dd8a96bb 100644 --- a/tm2/pkg/bft/store/store_test.go +++ b/tm2/pkg/bft/store/store_test.go @@ -1,7 +1,6 @@ package store import ( - "bytes" "fmt" "os" "runtime/debug" @@ -9,6 +8,8 @@ import ( "testing" "time" + "golang.org/x/exp/slog" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -44,7 +45,7 @@ func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Bl return block } -func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore, cleanupFunc) { +func makeStateAndBlockStore(logger *slog.Logger) (sm.State, *BlockStore, cleanupFunc) { config := cfg.ResetTestRoot("blockchain_reactor_test") // blockDB := dbm.NewDebugDB("blockDB", dbm.NewMemDB()) // stateDB := dbm.NewDebugDB("stateDB", dbm.NewMemDB()) @@ -58,6 +59,8 @@ func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore, cleanupFu } func TestLoadBlockStoreStateJSON(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() bsj := &BlockStoreStateJSON{Height: 1000} @@ -69,6 +72,8 @@ func TestLoadBlockStoreStateJSON(t *testing.T) { } func TestNewBlockStore(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() db.Set(blockStoreKey, []byte(`{"height": "10000"}`)) bs := NewBlockStore(db) @@ -115,7 +120,8 @@ var ( func TestMain(m *testing.M) { var cleanup cleanupFunc - state, _, cleanup = makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer))) + + state, _, cleanup = makeStateAndBlockStore(log.NewNoopLogger()) block = makeBlock(1, state, new(types.Commit)) partSet = block.MakePartSet(2) part1 = partSet.GetPart(0) @@ -129,7 +135,9 @@ func TestMain(m *testing.M) { // TODO: This test should be simplified ... func TestBlockStoreSaveLoadBlock(t *testing.T) { - state, bs, cleanup := makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer))) + t.Parallel() + + state, bs, cleanup := makeStateAndBlockStore(log.NewNoopLogger()) defer cleanup() require.Equal(t, bs.Height(), int64(0), "initially the height should be zero") @@ -325,6 +333,8 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) { } func TestLoadBlockPart(t *testing.T) { + t.Parallel() + bs, db := freshBlockStore() height, index := int64(10), 1 loadPart := func() (interface{}, error) { @@ -354,6 +364,8 @@ func TestLoadBlockPart(t *testing.T) { } func TestLoadBlockMeta(t *testing.T) { + t.Parallel() + bs, db := freshBlockStore() height := int64(10) loadMeta := func() (interface{}, error) { @@ -384,7 +396,9 @@ func TestLoadBlockMeta(t *testing.T) { } func TestBlockFetchAtHeight(t *testing.T) { - state, bs, cleanup := makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer))) + t.Parallel() + + state, bs, cleanup := makeStateAndBlockStore(log.NewNoopLogger()) defer cleanup() require.Equal(t, bs.Height(), int64(0), "initially the height should be zero") block := makeBlock(bs.Height()+1, state, new(types.Commit)) diff --git a/tm2/pkg/bft/types/block_test.go b/tm2/pkg/bft/types/block_test.go index 5b49e0c392e..3963378436b 100644 --- a/tm2/pkg/bft/types/block_test.go +++ b/tm2/pkg/bft/types/block_test.go @@ -21,6 +21,8 @@ import ( ) func TestBlockValidateBasic(t *testing.T) { + t.Parallel() + require.Error(t, (*Block)(nil).ValidateBasic()) txs := []Tx{Tx("foo"), Tx("bar")} @@ -57,6 +59,8 @@ func TestBlockValidateBasic(t *testing.T) { tc := tc i := i t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + block := MakeBlock(h, txs, commit) block.ProposerAddress = valSet.GetProposer().Address tc.malleateBlock(block) @@ -67,11 +71,15 @@ func TestBlockValidateBasic(t *testing.T) { } func TestBlockHash(t *testing.T) { + t.Parallel() + assert.Nil(t, (*Block)(nil).Hash()) assert.Nil(t, MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil).Hash()) } func TestBlockMakePartSet(t *testing.T) { + t.Parallel() + assert.Nil(t, (*Block)(nil).MakePartSet(2)) partSet := MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil).MakePartSet(1024) @@ -80,6 +88,8 @@ func TestBlockMakePartSet(t *testing.T) { } func TestBlockHashesTo(t *testing.T) { + t.Parallel() + assert.False(t, (*Block)(nil).HashesTo(nil)) lastID := makeBlockIDRandom() @@ -96,6 +106,8 @@ func TestBlockHashesTo(t *testing.T) { } func TestBlockSize(t *testing.T) { + t.Parallel() + size := MakeBlock(int64(3), []Tx{Tx("Hello World")}, nil).Size() if size <= 0 { t.Fatal("Size of the block is zero or negative") @@ -103,6 +115,8 @@ func TestBlockSize(t *testing.T) { } func TestBlockString(t *testing.T) { + t.Parallel() + assert.Equal(t, "nil-Block", (*Block)(nil).String()) assert.Equal(t, "nil-Block", (*Block)(nil).StringIndented("")) assert.Equal(t, "nil-Block", (*Block)(nil).StringShort()) @@ -135,16 +149,22 @@ func makeBlockID(hash []byte, partSetSize int, partSetHash []byte) BlockID { var nilBytes []byte func TestNilHeaderHashDoesntCrash(t *testing.T) { + t.Parallel() + assert.Equal(t, (*Header)(nil).Hash(), nilBytes) assert.Equal(t, (new(Header)).Hash(), nilBytes) } func TestNilDataHashDoesntCrash(t *testing.T) { + t.Parallel() + assert.Equal(t, (*Data)(nil).Hash(), nilBytes) assert.Equal(t, new(Data).Hash(), nilBytes) } func TestCommit(t *testing.T) { + t.Parallel() + lastID := makeBlockIDRandom() h := int64(3) voteSet, _, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1) @@ -166,6 +186,8 @@ func TestCommit(t *testing.T) { } func TestCommitValidateBasic(t *testing.T) { + t.Parallel() + testCases := []struct { testName string malleateCommit func(*Commit) @@ -181,6 +203,8 @@ func TestCommitValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + com := randCommit() tc.malleateCommit(com) assert.Equal(t, tc.expectErr, com.ValidateBasic() != nil, "Validate Basic had an unexpected result") @@ -189,6 +213,8 @@ func TestCommitValidateBasic(t *testing.T) { } func TestHeaderByteSize(t *testing.T) { + t.Parallel() + // Construct a UTF-8 string of MaxChainIDLen length using the supplementary // characters. // Each supplementary character takes 4 bytes. @@ -239,6 +265,8 @@ func randCommit() *Commit { } func TestCommitToVoteSet(t *testing.T) { + t.Parallel() + lastID := makeBlockIDRandom() h := int64(3) @@ -263,6 +291,8 @@ func TestCommitToVoteSet(t *testing.T) { } func TestCommitToVoteSetWithVotesForAnotherBlockOrNilBlock(t *testing.T) { + t.Parallel() + blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) blockID3 := makeBlockID([]byte("blockhash3"), 10000, []byte("partshash")) @@ -320,6 +350,8 @@ func TestCommitToVoteSetWithVotesForAnotherBlockOrNilBlock(t *testing.T) { } func TestSignedHeaderValidateBasic(t *testing.T) { + t.Parallel() + commit := randCommit() chainID := "𠜎" timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) @@ -360,6 +392,8 @@ func TestSignedHeaderValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + sh := SignedHeader{ Header: tc.shHeader, Commit: tc.shCommit, @@ -370,6 +404,8 @@ func TestSignedHeaderValidateBasic(t *testing.T) { } func TestBlockIDValidateBasic(t *testing.T) { + t.Parallel() + validBlockID := BlockID{ Hash: []byte{}, PartsHeader: PartSetHeader{ @@ -400,6 +436,8 @@ func TestBlockIDValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + blockID := BlockID{ Hash: tc.blockIDHash, PartsHeader: tc.blockIDPartsHeader, diff --git a/tm2/pkg/bft/types/evidence.go b/tm2/pkg/bft/types/evidence.go index 26cf1aaa8ca..c11021e3976 100644 --- a/tm2/pkg/bft/types/evidence.go +++ b/tm2/pkg/bft/types/evidence.go @@ -65,7 +65,7 @@ const ( ) // MaxEvidencePerBlock returns the maximum number of evidences -// allowed in the block and their maximum total size (limitted to 1/10th +// allowed in the block and their maximum total size (limited to 1/10th // of the maximum block size). // TODO: change to a constant, or to a fraction of the validator set size. // See https://github.com/tendermint/classic/issues/2590 diff --git a/tm2/pkg/bft/types/evidence_test.go b/tm2/pkg/bft/types/evidence_test.go index 4efc61f8be7..5c43c547b74 100644 --- a/tm2/pkg/bft/types/evidence_test.go +++ b/tm2/pkg/bft/types/evidence_test.go @@ -37,6 +37,8 @@ func makeVote(val PrivValidator, chainID string, valIndex int, height int64, rou } func TestEvidence(t *testing.T) { + t.Parallel() + val := NewMockPV() val2 := NewMockPV() @@ -83,6 +85,8 @@ func TestEvidence(t *testing.T) { } func TestDuplicatedVoteEvidence(t *testing.T) { + t.Parallel() + ev := randomDuplicatedVoteEvidence() assert.True(t, ev.Equal(ev)) @@ -90,6 +94,8 @@ func TestDuplicatedVoteEvidence(t *testing.T) { } func TestEvidenceList(t *testing.T) { + t.Parallel() + ev := randomDuplicatedVoteEvidence() evl := EvidenceList([]Evidence{ev}) @@ -99,6 +105,8 @@ func TestEvidenceList(t *testing.T) { } func TestEvidenceByteSize(t *testing.T) { + t.Parallel() + val := NewMockPV() blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) @@ -127,6 +135,8 @@ func randomDuplicatedVoteEvidence() *DuplicateVoteEvidence { } func TestDuplicateVoteEvidenceValidation(t *testing.T) { + t.Parallel() + val := NewMockPV() blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt64, tmhash.Sum([]byte("partshash"))) @@ -151,6 +161,8 @@ func TestDuplicateVoteEvidenceValidation(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + ev := &DuplicateVoteEvidence{ PubKey: secp256k1.GenPrivKey().PubKey(), VoteA: makeVote(val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, 0x02, blockID), @@ -163,11 +175,15 @@ func TestDuplicateVoteEvidenceValidation(t *testing.T) { } func TestMockGoodEvidenceValidateBasic(t *testing.T) { + t.Parallel() + goodEvidence := NewMockGoodEvidence(int64(1), 1, crypto.AddressFromPreimage([]byte{1})) assert.Nil(t, goodEvidence.ValidateBasic()) } func TestMockBadEvidenceValidateBasic(t *testing.T) { + t.Parallel() + badEvidence := MockBadEvidence{MockGoodEvidence: NewMockGoodEvidence(int64(1), 1, crypto.AddressFromPreimage([]byte{1}))} assert.Nil(t, badEvidence.ValidateBasic()) } diff --git a/tm2/pkg/bft/types/genesis.go b/tm2/pkg/bft/types/genesis.go index f881e068558..c03f7acc09e 100644 --- a/tm2/pkg/bft/types/genesis.go +++ b/tm2/pkg/bft/types/genesis.go @@ -18,7 +18,17 @@ const ( MaxChainIDLen = 50 ) -//------------------------------------------------------------ +var ( + ErrEmptyChainID = errors.New("chain ID is empty") + ErrLongChainID = fmt.Errorf("chain ID cannot be longer than %d chars", MaxChainIDLen) + ErrInvalidGenesisTime = errors.New("invalid genesis time") + ErrNoValidators = errors.New("no validators in set") + ErrInvalidValidatorVotingPower = errors.New("validator has no voting power") + ErrInvalidValidatorAddress = errors.New("invalid validator address") + ErrValidatorPubKeyMismatch = errors.New("validator public key and address mismatch") +) + +// ------------------------------------------------------------ // core types for a genesis definition // NOTE: any changes to the genesis definition should // be reflected in the documentation: @@ -61,6 +71,54 @@ func (genDoc *GenesisDoc) ValidatorHash() []byte { return vset.Hash() } +// Validate validates the genesis doc +func (genDoc *GenesisDoc) Validate() error { + // Make sure the chain ID is not empty + if genDoc.ChainID == "" { + return ErrEmptyChainID + } + + // Make sure the chain ID is < max chain ID length + if len(genDoc.ChainID) > MaxChainIDLen { + return ErrLongChainID + } + + // Make sure the genesis time is valid + if genDoc.GenesisTime.IsZero() { + return ErrInvalidGenesisTime + } + + // Validate the consensus params + if consensusParamsErr := ValidateConsensusParams(genDoc.ConsensusParams); consensusParamsErr != nil { + return consensusParamsErr + } + + // Make sure there are validators in the set + if len(genDoc.Validators) == 0 { + return ErrNoValidators + } + + // Make sure the validators are valid + for _, v := range genDoc.Validators { + // Check the voting power + if v.Power == 0 { + return fmt.Errorf("%w, %s", ErrInvalidValidatorVotingPower, v.Name) + } + + // Check the address + if v.Address.IsZero() { + return fmt.Errorf("%w, %s", ErrInvalidValidatorAddress, v.Name) + } + + // Check the pub key -> address matching + if v.PubKey.Address() != v.Address { + return fmt.Errorf("%w, %s", ErrValidatorPubKeyMismatch, v.Name) + } + } + + return nil +} + // ValidateAndComplete checks that all necessary fields are present // and fills in defaults for optional fields left empty func (genDoc *GenesisDoc) ValidateAndComplete() error { @@ -95,7 +153,7 @@ func (genDoc *GenesisDoc) ValidateAndComplete() error { return nil } -//------------------------------------------------------------ +// ------------------------------------------------------------ // Make genesis state from file // GenesisDocFromJSON unmarshalls JSON data into a GenesisDoc. @@ -126,7 +184,7 @@ func GenesisDocFromFile(genDocFile string) (*GenesisDoc, error) { return genDoc, nil } -//---------------------------------------- +// ---------------------------------------- // Mock AppState (for testing) type MockAppState struct { diff --git a/tm2/pkg/bft/types/genesis_test.go b/tm2/pkg/bft/types/genesis_test.go index c8886f9bf0a..24c69c6a28e 100644 --- a/tm2/pkg/bft/types/genesis_test.go +++ b/tm2/pkg/bft/types/genesis_test.go @@ -1,9 +1,9 @@ package types import ( - "io/ioutil" "os" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -14,6 +14,8 @@ import ( ) func TestGenesisBad(t *testing.T) { + t.Parallel() + // test some bad ones from raw json testCases := [][]byte{ {}, // empty @@ -37,6 +39,8 @@ func TestGenesisBad(t *testing.T) { } func TestGenesisGood(t *testing.T) { + t.Parallel() + // test a good one by raw json genDocBytes := []byte(`{"genesis_time":"0001-01-01T00:00:00Z","chain_id":"test-chain-QDKdJr","consensus_params":null,"validators":[{"pub_key":{"@type":"/tm.PubKeyEd25519","value":"AT/+aaL1eB0477Mud9JMm8Sh8BIvOYlPGC9KkIUmFaE="},"power":"10","name":""}],"app_hash":"","app_state":{"@type":"/tm.MockAppState","account_owner":"Bob"}}`) _, err := GenesisDocFromJSON(genDocBytes) @@ -87,7 +91,9 @@ func TestGenesisGood(t *testing.T) { } func TestGenesisSaveAs(t *testing.T) { - tmpfile, err := ioutil.TempFile("", "genesis") + t.Parallel() + + tmpfile, err := os.CreateTemp("", "genesis") require.NoError(t, err) defer os.Remove(tmpfile.Name()) @@ -114,6 +120,8 @@ func TestGenesisSaveAs(t *testing.T) { } func TestGenesisValidatorHash(t *testing.T) { + t.Parallel() + genDoc := randomGenesisDoc() assert.NotEmpty(t, genDoc.ValidatorHash()) } @@ -127,3 +135,120 @@ func randomGenesisDoc() *GenesisDoc { ConsensusParams: DefaultConsensusParams(), } } + +func TestGenesis_Validate(t *testing.T) { + t.Parallel() + + getValidTestGenesis := func() *GenesisDoc { + key := randPubKey() + + return &GenesisDoc{ + GenesisTime: time.Now(), + ChainID: "valid-chain-id", + ConsensusParams: DefaultConsensusParams(), + Validators: []GenesisValidator{ + { + Address: key.Address(), + PubKey: key, + Power: 1, + Name: "valid validator", + }, + }, + } + } + + t.Run("valid genesis", func(t *testing.T) { + t.Parallel() + + g := getValidTestGenesis() + + require.NoError(t, g.Validate()) + }) + + t.Run("invalid chain ID (zero)", func(t *testing.T) { + t.Parallel() + + g := getValidTestGenesis() + g.ChainID = "" + + assert.ErrorIs(t, g.Validate(), ErrEmptyChainID) + }) + + t.Run("invalid chain ID (long)", func(t *testing.T) { + t.Parallel() + + g := getValidTestGenesis() + g.ChainID = "thischainidisunusuallylongsoitwillcausethetesttofail" + + assert.ErrorIs(t, g.Validate(), ErrLongChainID) + }) + + t.Run("invalid genesis time", func(t *testing.T) { + t.Parallel() + + g := getValidTestGenesis() + g.GenesisTime = time.Time{} + + assert.ErrorIs(t, g.Validate(), ErrInvalidGenesisTime) + }) + + t.Run("invalid consensus params", func(t *testing.T) { + t.Parallel() + + g := getValidTestGenesis() + g.ConsensusParams.Block.MaxTxBytes = -1 // invalid value + + assert.ErrorContains(t, g.Validate(), "MaxTxBytes") + }) + + t.Run("no validators", func(t *testing.T) { + t.Parallel() + + g := getValidTestGenesis() + g.Validators = []GenesisValidator{} + + assert.ErrorIs(t, g.Validate(), ErrNoValidators) + }) + + t.Run("invalid validator, no voting power", func(t *testing.T) { + t.Parallel() + + g := getValidTestGenesis() + g.Validators = []GenesisValidator{ + { + Power: 0, // no voting power + }, + } + + assert.ErrorIs(t, g.Validate(), ErrInvalidValidatorVotingPower) + }) + + t.Run("invalid validator, zero address", func(t *testing.T) { + t.Parallel() + + g := getValidTestGenesis() + g.Validators = []GenesisValidator{ + { + Power: 1, + Address: Address{}, // zero address + }, + } + + assert.ErrorIs(t, g.Validate(), ErrInvalidValidatorAddress) + }) + + t.Run("invalid validator, public key mismatch", func(t *testing.T) { + t.Parallel() + + g := getValidTestGenesis() + g.Validators = []GenesisValidator{ + { + Power: 1, + Address: Address{1}, + PubKey: randPubKey(), + }, + } + + assert.ErrorIs(t, g.Validate(), ErrValidatorPubKeyMismatch) + }) +} diff --git a/tm2/pkg/bft/types/params.go b/tm2/pkg/bft/types/params.go index e50f5c05b88..461d62a17b3 100644 --- a/tm2/pkg/bft/types/params.go +++ b/tm2/pkg/bft/types/params.go @@ -16,6 +16,18 @@ const ( // MaxBlockPartsCount is the maximum count of block parts. MaxBlockPartsCount = (MaxBlockSizeBytes / BlockPartSizeBytes) + 1 + + // MaxBlockTxBytes is the max size of the block transaction + MaxBlockTxBytes int64 = 1000000 // 1MB + + // MaxBlockDataBytes is the max size of the block data + MaxBlockDataBytes int64 = 2000000 // 2MB + + // MaxBlockMaxGas is the max gas limit for the block + MaxBlockMaxGas int64 = 10000000 // 10M gas + + // BlockTimeIotaMS is the block time iota (in ms) + BlockTimeIotaMS int64 = 100 // ms ) var validatorPubKeyTypeURLs = map[string]struct{}{ @@ -31,10 +43,10 @@ func DefaultConsensusParams() abci.ConsensusParams { func DefaultBlockParams() *abci.BlockParams { return &abci.BlockParams{ - MaxTxBytes: 1024 * 1024, // 1MB - MaxDataBytes: 22020096, // 21MB - MaxGas: -1, - TimeIotaMS: 1000, // 1s + MaxTxBytes: MaxBlockTxBytes, + MaxDataBytes: MaxBlockDataBytes, + MaxGas: MaxBlockMaxGas, + TimeIotaMS: BlockTimeIotaMS, } } diff --git a/tm2/pkg/bft/types/params_test.go b/tm2/pkg/bft/types/params_test.go index cb5f3244e8d..141a67b903c 100644 --- a/tm2/pkg/bft/types/params_test.go +++ b/tm2/pkg/bft/types/params_test.go @@ -16,6 +16,8 @@ var ( ) func TestConsensusParamsValidation(t *testing.T) { + t.Parallel() + testCases := []struct { params abci.ConsensusParams valid bool @@ -63,6 +65,8 @@ func makeParams( } func TestConsensusParamsHash(t *testing.T) { + t.Parallel() + params := []abci.ConsensusParams{ makeParams(4, 1024, 2, 10, valEd25519), makeParams(1, 1024, 4, 10, valEd25519), @@ -90,6 +94,8 @@ func TestConsensusParamsHash(t *testing.T) { } func TestConsensusParamsUpdate(t *testing.T) { + t.Parallel() + testCases := []struct { params abci.ConsensusParams updates abci.ConsensusParams diff --git a/tm2/pkg/bft/types/part_set_test.go b/tm2/pkg/bft/types/part_set_test.go index 2e05daed849..13ebb2f0bba 100644 --- a/tm2/pkg/bft/types/part_set_test.go +++ b/tm2/pkg/bft/types/part_set_test.go @@ -1,7 +1,7 @@ package types import ( - "io/ioutil" + "io" "testing" "github.com/stretchr/testify/assert" @@ -16,6 +16,8 @@ const ( ) func TestBasicPartSet(t *testing.T) { + t.Parallel() + // Construct random data of size partSize * 100 data := random.RandBytes(testPartSize * 100) partSet := NewPartSetFromData(data, testPartSize) @@ -54,13 +56,15 @@ func TestBasicPartSet(t *testing.T) { // Reconstruct data, assert that they are equal. data2Reader := partSet2.GetReader() - data2, err := ioutil.ReadAll(data2Reader) + data2, err := io.ReadAll(data2Reader) require.NoError(t, err) assert.Equal(t, data, data2) } func TestWrongProof(t *testing.T) { + t.Parallel() + // Construct random data of size partSize * 100 data := random.RandBytes(testPartSize * 100) partSet := NewPartSetFromData(data, testPartSize) @@ -86,6 +90,8 @@ func TestWrongProof(t *testing.T) { } func TestPartSetHeaderValidateBasic(t *testing.T) { + t.Parallel() + testCases := []struct { testName string malleatePartSetHeader func(*PartSetHeader) @@ -98,6 +104,8 @@ func TestPartSetHeaderValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + data := random.RandBytes(testPartSize * 100) ps := NewPartSetFromData(data, testPartSize) psHeader := ps.Header() @@ -108,6 +116,8 @@ func TestPartSetHeaderValidateBasic(t *testing.T) { } func TestPartValidateBasic(t *testing.T) { + t.Parallel() + testCases := []struct { testName string malleatePart func(*Part) @@ -128,6 +138,8 @@ func TestPartValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + data := random.RandBytes(testPartSize * 100) ps := NewPartSetFromData(data, testPartSize) part := ps.GetPart(0) diff --git a/tm2/pkg/bft/types/proposal_test.go b/tm2/pkg/bft/types/proposal_test.go index ebda33298d1..0692e9bdd6f 100644 --- a/tm2/pkg/bft/types/proposal_test.go +++ b/tm2/pkg/bft/types/proposal_test.go @@ -29,6 +29,8 @@ func init() { } func TestProposalSignable(t *testing.T) { + t.Parallel() + chainID := "test_chain_id" signBytes := testProposal.SignBytes(chainID) @@ -38,6 +40,8 @@ func TestProposalSignable(t *testing.T) { } func TestProposalString(t *testing.T) { + t.Parallel() + str := testProposal.String() expected := `Proposal{12345/23456 (010203:111:626C6F636B70, -1) 000000000000 @ 2018-02-11T07:09:22.765Z}` if str != expected { @@ -46,6 +50,8 @@ func TestProposalString(t *testing.T) { } func TestProposalVerifySignature(t *testing.T) { + t.Parallel() + privVal := NewMockPV() pubKey := privVal.GetPubKey() @@ -104,6 +110,8 @@ func BenchmarkProposalVerifySignature(b *testing.B) { } func TestProposalValidateBasic(t *testing.T) { + t.Parallel() + privVal := NewMockPV() testCases := []struct { testName string @@ -130,6 +138,8 @@ func TestProposalValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + prop := NewProposal( 4, 2, 2, blockID) diff --git a/tm2/pkg/bft/types/results_test.go b/tm2/pkg/bft/types/results_test.go index 6db7044f6ad..6b375413ac6 100644 --- a/tm2/pkg/bft/types/results_test.go +++ b/tm2/pkg/bft/types/results_test.go @@ -10,6 +10,8 @@ import ( ) func TestABCIResults(t *testing.T) { + t.Parallel() + a := ABCIResult{Error: nil, Data: nil} b := ABCIResult{Error: nil, Data: []byte{}} c := ABCIResult{Error: nil, Data: []byte("one")} @@ -46,6 +48,8 @@ func TestABCIResults(t *testing.T) { } func TestABCIResultsBytes(t *testing.T) { + t.Parallel() + results := NewResults([]abci.ResponseDeliverTx{ {ResponseBase: abci.ResponseBase{Error: nil, Data: []byte{}}}, {ResponseBase: abci.ResponseBase{Error: nil, Data: []byte("one")}}, diff --git a/tm2/pkg/bft/types/time/time_test.go b/tm2/pkg/bft/types/time/time_test.go index 1b1a30e5058..bf53e313fef 100644 --- a/tm2/pkg/bft/types/time/time_test.go +++ b/tm2/pkg/bft/types/time/time_test.go @@ -8,6 +8,8 @@ import ( ) func TestWeightedMedian(t *testing.T) { + t.Parallel() + m := make([]*WeightedTime, 3) t1 := Now() diff --git a/tm2/pkg/bft/types/tx_test.go b/tm2/pkg/bft/types/tx_test.go index 1470fab024d..375783f874f 100644 --- a/tm2/pkg/bft/types/tx_test.go +++ b/tm2/pkg/bft/types/tx_test.go @@ -25,6 +25,8 @@ func randInt(low, high int) int { } func TestTxIndex(t *testing.T) { + t.Parallel() + for i := 0; i < 20; i++ { txs := makeTxs(15, 60) for j := 0; j < len(txs); j++ { @@ -38,6 +40,8 @@ func TestTxIndex(t *testing.T) { } func TestTxIndexByHash(t *testing.T) { + t.Parallel() + for i := 0; i < 20; i++ { txs := makeTxs(15, 60) for j := 0; j < len(txs); j++ { @@ -51,6 +55,8 @@ func TestTxIndexByHash(t *testing.T) { } func TestValidTxProof(t *testing.T) { + t.Parallel() + cases := []struct { txs Txs }{ @@ -90,6 +96,8 @@ func TestValidTxProof(t *testing.T) { } func TestTxProofUnchangable(t *testing.T) { + t.Parallel() + // run the other test a bunch... for i := 0; i < 40; i++ { testTxProofUnchangable(t) diff --git a/tm2/pkg/bft/types/types.proto b/tm2/pkg/bft/types/types.proto index 7514078f26b..ca97ff59781 100644 --- a/tm2/pkg/bft/types/types.proto +++ b/tm2/pkg/bft/types/types.proto @@ -12,158 +12,158 @@ import "google/protobuf/any.proto"; // messages message Proposal { - uint32 Type = 1; - sint64 Height = 2; - sint64 Round = 3; - sint64 POLRound = 4; - BlockID BlockID = 5; - google.protobuf.Timestamp Timestamp = 6; - bytes Signature = 7; + uint32 type = 1 [json_name = "Type"]; + sint64 height = 2; + sint64 round = 3; + sint64 pol_round = 4; + BlockID block_id = 5; + google.protobuf.Timestamp timestamp = 6; + bytes signature = 7; } message Block { - Header Header = 1; - Data Data = 2; - Commit LastCommit = 3; + Header header = 1; + Data data = 2; + Commit last_commit = 3; } message Header { - string Version = 1; - string ChainID = 2; - sint64 Height = 3; - google.protobuf.Timestamp Time = 4; - sint64 NumTxs = 5; - sint64 TotalTxs = 6; - string AppVersion = 7; - BlockID LastBlockID = 8; - bytes LastCommitHash = 9; - bytes DataHash = 10; - bytes ValidatorsHash = 11; - bytes NextValidatorsHash = 12; - bytes ConsensusHash = 13; - bytes AppHash = 14; - bytes LastResultsHash = 15; - string ProposerAddress = 16; + string version = 1; + string chain_id = 2; + sint64 height = 3; + google.protobuf.Timestamp time = 4; + sint64 num_txs = 5; + sint64 total_txs = 6; + string app_version = 7; + BlockID last_block_id = 8; + bytes last_commit_hash = 9; + bytes data_hash = 10; + bytes validators_hash = 11; + bytes next_validators_hash = 12; + bytes consensus_hash = 13; + bytes app_hash = 14; + bytes last_results_hash = 15; + string proposer_address = 16; } message Data { - repeated bytes Txs = 1; + repeated bytes txs = 1; } message Commit { - BlockID BlockID = 1; - repeated CommitSig Precommits = 2; + BlockID block_id = 1; + repeated CommitSig precommits = 2; } message BlockID { - bytes Hash = 1; - PartSetHeader PartsHeader = 2; + bytes hash = 1; + PartSetHeader parts_header = 2 [json_name = "parts"]; } message CommitSig { - uint32 Type = 1; - sint64 Height = 2; - sint64 Round = 3; - BlockID BlockID = 4; - google.protobuf.Timestamp Timestamp = 5; - string ValidatorAddress = 6; - sint64 ValidatorIndex = 7; - bytes Signature = 8; + uint32 type = 1; + sint64 height = 2; + sint64 round = 3; + BlockID block_id = 4; + google.protobuf.Timestamp timestamp = 5; + string validator_address = 6; + sint64 validator_index = 7; + bytes signature = 8; } message Vote { - uint32 Type = 1; - sint64 Height = 2; - sint64 Round = 3; - BlockID BlockID = 4; - google.protobuf.Timestamp Timestamp = 5; - string ValidatorAddress = 6; - sint64 ValidatorIndex = 7; - bytes Signature = 8; + uint32 type = 1; + sint64 height = 2; + sint64 round = 3; + BlockID block_id = 4; + google.protobuf.Timestamp timestamp = 5; + string validator_address = 6; + sint64 validator_index = 7; + bytes signature = 8; } message Part { - sint64 Index = 1; - bytes Bytes = 2; - SimpleProof Proof = 3; + sint64 index = 1; + bytes bytes = 2; + SimpleProof proof = 3; } message PartSet { } message PartSetHeader { - sint64 Total = 1; - bytes Hash = 2; + sint64 total = 1; + bytes hash = 2; } message Validator { - string Address = 1; - google.protobuf.Any PubKey = 2; - sint64 VotingPower = 3; - sint64 ProposerPriority = 4; + string address = 1; + google.protobuf.Any pub_key = 2; + sint64 voting_power = 3; + sint64 proposer_priority = 4; } message ValidatorSet { - repeated Validator Validators = 1; - Validator Proposer = 2; + repeated Validator validators = 1; + Validator proposer = 2; } message EventNewBlock { - Block Block = 1; - abci.ResponseBeginBlock ResultBeginBlock = 2; - abci.ResponseEndBlock ResultEndBlock = 3; + Block block = 1; + abci.ResponseBeginBlock result_begin_block = 2; + abci.ResponseEndBlock result_end_block = 3; } message EventNewBlockHeader { - Header Header = 1; - abci.ResponseBeginBlock ResultBeginBlock = 2; - abci.ResponseEndBlock ResultEndBlock = 3; + Header header = 1; + abci.ResponseBeginBlock result_begin_block = 2; + abci.ResponseEndBlock result_end_block = 3; } message EventTx { - TxResult Result = 1; + TxResult result = 1; } message EventVote { - Vote Vote = 1; + Vote vote = 1; } message EventString { - string Value = 1; + string value = 1; } message EventValidatorSetUpdates { - repeated abci.ValidatorUpdate ValidatorUpdates = 1; + repeated abci.ValidatorUpdate validator_updates = 1; } message DuplicateVoteEvidence { - google.protobuf.Any PubKey = 1; - Vote VoteA = 2; - Vote VoteB = 3; + google.protobuf.Any pub_key = 1 [json_name = "PubKey"]; + Vote vote_a = 2 [json_name = "VoteA"]; + Vote vote_b = 3 [json_name = "VoteB"]; } message MockGoodEvidence { - sint64 Height = 1; - string Address = 2; + sint64 height = 1 [json_name = "Height"]; + string address = 2 [json_name = "Address"]; } message MockRandomGoodEvidence { - MockGoodEvidence MockGoodEvidence = 1; + MockGoodEvidence mock_good_evidence = 1 [json_name = "MockGoodEvidence"]; } message MockBadEvidence { - MockGoodEvidence MockGoodEvidence = 1; + MockGoodEvidence mock_good_evidence = 1 [json_name = "MockGoodEvidence"]; } message TxResult { - sint64 Height = 1; - uint32 Index = 2; - bytes Tx = 3; - abci.ResponseDeliverTx Response = 4; + sint64 height = 1; + uint32 index = 2; + bytes tx = 3; + abci.ResponseDeliverTx response = 4; } message MockAppState { - string AccountOwner = 1; + string account_owner = 1; } message VoteSet { diff --git a/tm2/pkg/bft/types/validator_set.go b/tm2/pkg/bft/types/validator_set.go index 617ceecaae5..80ed994ca39 100644 --- a/tm2/pkg/bft/types/validator_set.go +++ b/tm2/pkg/bft/types/validator_set.go @@ -758,7 +758,7 @@ func (vals *ValidatorSet) VerifyFutureCommit(newSet *ValidatorSet, chainID strin return nil } -//----------------- +// ----------------- // ErrTooMuchChange func IsErrTooMuchChange(err error) bool { @@ -775,7 +775,7 @@ func (e tooMuchChangeError) Error() string { return fmt.Sprintf("Invalid commit -- insufficient old voting power: got %v, needed %v", e.got, e.needed) } -//---------------- +// ---------------- func (vals *ValidatorSet) String() string { return vals.StringIndented("") @@ -802,7 +802,7 @@ func (vals *ValidatorSet) StringIndented(indent string) string { indent) } -//------------------------------------- +// ------------------------------------- // Implements sort for sorting validators by address. // Sort validators by address. @@ -822,7 +822,7 @@ func (valz ValidatorsByAddress) Swap(i, j int) { valz[j] = it } -//---------------------------------------- +// ---------------------------------------- // for testing // RandValidatorSet returns a randomized validator set, useful for testing. @@ -841,7 +841,7 @@ func RandValidatorSet(numValidators int, votingPower int64) (*ValidatorSet, []Pr return vals, privValidators } -/////////////////////////////////////////////////////////////////////////////// +// ----------- // safe addition/subtraction func safeAdd(a, b int64) (int64, bool) { diff --git a/tm2/pkg/bft/types/validator_set_test.go b/tm2/pkg/bft/types/validator_set_test.go index a2c38026ed8..0fafb6fca9e 100644 --- a/tm2/pkg/bft/types/validator_set_test.go +++ b/tm2/pkg/bft/types/validator_set_test.go @@ -20,6 +20,8 @@ import ( ) func TestValidatorSetBasic(t *testing.T) { + t.Parallel() + // empty or nil validator lists are allowed, // but attempting to IncrementProposerPriority on them will panic. vset := NewValidatorSet([]*Validator{}) @@ -76,6 +78,8 @@ func TestValidatorSetBasic(t *testing.T) { } func TestCopy(t *testing.T) { + t.Parallel() + vset := randValidatorSet(10) vsetHash := vset.Hash() if len(vsetHash) == 0 { @@ -92,6 +96,8 @@ func TestCopy(t *testing.T) { // Test that IncrementProposerPriority requires positive times. func TestIncrementProposerPriorityPositiveTimes(t *testing.T) { + t.Parallel() + vset := NewValidatorSet([]*Validator{ newValidator([]byte("foo"), 1000), newValidator([]byte("bar"), 300), @@ -122,9 +128,11 @@ func BenchmarkValidatorSetCopy(b *testing.B) { } } -//------------------------------------------------------------------- +// ------------------------------------------------------------------- func TestProposerSelection1(t *testing.T) { + t.Parallel() + vset := NewValidatorSet([]*Validator{ newValidator([]byte("foo"), 1000), newValidator([]byte("bar"), 300), @@ -143,6 +151,8 @@ func TestProposerSelection1(t *testing.T) { } func TestProposerSelection2(t *testing.T) { + t.Parallel() + addr0 := []byte{1} addr1 := []byte{2} addr2 := []byte{3} @@ -217,6 +227,8 @@ func TestProposerSelection2(t *testing.T) { } func TestProposerSelection3(t *testing.T) { + t.Parallel() + vset := NewValidatorSet([]*Validator{ newValidator([]byte("a"), 1), newValidator([]byte("b"), 1), @@ -321,9 +333,11 @@ func (vals *ValidatorSet) fromBytes(b []byte) { } } -//------------------------------------------------------------------- +// ------------------------------------------------------------------- func TestValidatorSetTotalVotingPowerPanicsOnOverflow(t *testing.T) { + t.Parallel() + // NewValidatorSet calls IncrementProposerPriority which calls TotalVotingPower() // which should panic on overflows: shouldPanic := func() { @@ -338,6 +352,8 @@ func TestValidatorSetTotalVotingPowerPanicsOnOverflow(t *testing.T) { } func TestAvgProposerPriority(t *testing.T) { + t.Parallel() + // Create Validator set without calling IncrementProposerPriority: tcs := []struct { vs ValidatorSet @@ -356,6 +372,8 @@ func TestAvgProposerPriority(t *testing.T) { } func TestAveragingInIncrementProposerPriority(t *testing.T) { + t.Parallel() + // Test that the averaging works as expected inside of IncrementProposerPriority. // Each validator comes with zero voting power which simplifies reasoning about // the expected ProposerPriority. @@ -408,6 +426,8 @@ func TestAveragingInIncrementProposerPriority(t *testing.T) { } func TestAveragingInIncrementProposerPriorityWithVotingPower(t *testing.T) { + t.Parallel() + // Other than TestAveragingInIncrementProposerPriority this is a more complete test showing // how each ProposerPriority changes in relation to the validator's voting power respectively. // average is zero in each round: @@ -558,6 +578,8 @@ func TestAveragingInIncrementProposerPriorityWithVotingPower(t *testing.T) { } func TestSafeAdd(t *testing.T) { + t.Parallel() + f := func(a, b int64) bool { c, overflow := safeAdd(a, b) return overflow || (!overflow && c == a+b) @@ -568,21 +590,27 @@ func TestSafeAdd(t *testing.T) { } func TestSafeAddClip(t *testing.T) { + t.Parallel() + assert.EqualValues(t, math.MaxInt64, safeAddClip(math.MaxInt64, 10)) assert.EqualValues(t, math.MaxInt64, safeAddClip(math.MaxInt64, math.MaxInt64)) assert.EqualValues(t, math.MinInt64, safeAddClip(math.MinInt64, -10)) } func TestSafeSubClip(t *testing.T) { + t.Parallel() + assert.EqualValues(t, math.MinInt64, safeSubClip(math.MinInt64, 10)) assert.EqualValues(t, 0, safeSubClip(math.MinInt64, math.MinInt64)) assert.EqualValues(t, math.MinInt64, safeSubClip(math.MinInt64, math.MaxInt64)) assert.EqualValues(t, math.MaxInt64, safeSubClip(math.MaxInt64, -10)) } -//------------------------------------------------------------------- +// ------------------------------------------------------------------- func TestValidatorSetVerifyCommit(t *testing.T) { + t.Parallel() + privKey := mock.GenPrivKey() pubKey := privKey.PubKey() v1 := NewValidator(pubKey, 1000) @@ -635,6 +663,8 @@ func TestValidatorSetVerifyCommit(t *testing.T) { } func TestEmptySet(t *testing.T) { + t.Parallel() + var valList []*Validator valSet := NewValidatorSet(valList) assert.Panics(t, func() { valSet.IncrementProposerPriority(1) }) @@ -661,6 +691,8 @@ func TestEmptySet(t *testing.T) { } func TestUpdatesForNewValidatorSet(t *testing.T) { + t.Parallel() + v1 := newValidator([]byte("v1"), 100) v2 := newValidator([]byte("v2"), 100) valList := []*Validator{v1, v2} @@ -793,6 +825,8 @@ func executeValSetErrTestCase(t *testing.T, idx int, tt valSetErrTestCase) { } func TestValSetUpdatesDuplicateEntries(t *testing.T) { + t.Parallel() + testCases := []valSetErrTestCase{ // Duplicate entries in changes { // first entry is duplicated change @@ -850,6 +884,8 @@ func TestValSetUpdatesDuplicateEntries(t *testing.T) { } func TestValSetUpdatesOverflows(t *testing.T) { + t.Parallel() + maxVP := MaxTotalVotingPower testCases := []valSetErrTestCase{ { // single update leading to overflow @@ -884,6 +920,8 @@ func TestValSetUpdatesOverflows(t *testing.T) { } func TestValSetUpdatesOtherErrors(t *testing.T) { + t.Parallel() + testCases := []valSetErrTestCase{ { // update with negative voting power testValSet(2, 10), @@ -909,6 +947,8 @@ func TestValSetUpdatesOtherErrors(t *testing.T) { } func TestValSetUpdatesBasicTestsExecute(t *testing.T) { + t.Parallel() + valSetUpdatesBasicTests := []struct { startVals []testVal updateVals []testVal @@ -970,6 +1010,8 @@ func TestValSetUpdatesBasicTestsExecute(t *testing.T) { // Test that different permutations of an update give the same result. func TestValSetUpdatesOrderIndependenceTestsExecute(t *testing.T) { + t.Parallel() + // startVals - initial validators to create the set with // updateVals - a sequence of updates to be applied to the set. // updateVals is shuffled a number of times during testing to check for same resulting validator set. @@ -1031,6 +1073,8 @@ func TestValSetUpdatesOrderIndependenceTestsExecute(t *testing.T) { // This tests the private function validator_set.go:applyUpdates() function, used only for additions and changes. // Should perform a proper merge of updatedVals and startVals func TestValSetApplyUpdatesTestsExecute(t *testing.T) { + t.Parallel() + valSetUpdatesBasicTests := []struct { startVals []testVal updateVals []testVal @@ -1174,6 +1218,8 @@ func applyChangesToValSet(t *testing.T, valSet *ValidatorSet, valsLists ...[]tes } func TestValSetUpdatePriorityOrderTests(t *testing.T) { + t.Parallel() + const nMaxElections = 5000 testCases := []testVSetCfg{ diff --git a/tm2/pkg/bft/types/vote_set_test.go b/tm2/pkg/bft/types/vote_set_test.go index e3199f83edc..ca5639f2406 100644 --- a/tm2/pkg/bft/types/vote_set_test.go +++ b/tm2/pkg/bft/types/vote_set_test.go @@ -61,6 +61,8 @@ func withBlockPartsHeader(vote *Vote, blockPartsHeader PartSetHeader) *Vote { } func TestAddVote(t *testing.T) { + t.Parallel() + height, round := int64(1), 0 voteSet, _, privValidators := randVoteSet(height, round, PrevoteType, 10, 1) val0 := privValidators[0] @@ -106,6 +108,8 @@ func TestAddVote(t *testing.T) { } func Test2_3Majority(t *testing.T) { + t.Parallel() + height, round := int64(1), 0 voteSet, _, privValidators := randVoteSet(height, round, PrevoteType, 10, 1) @@ -162,6 +166,8 @@ func Test2_3Majority(t *testing.T) { } func Test2_3MajorityRedux(t *testing.T) { + t.Parallel() + height, round := int64(1), 0 voteSet, _, privValidators := randVoteSet(height, round, PrevoteType, 100, 1) @@ -267,6 +273,8 @@ func Test2_3MajorityRedux(t *testing.T) { } func TestBadVotes(t *testing.T) { + t.Parallel() + height, round := int64(1), 0 voteSet, _, privValidators := randVoteSet(height, round, PrevoteType, 10, 1) @@ -332,6 +340,8 @@ func TestBadVotes(t *testing.T) { } func TestConflicts(t *testing.T) { + t.Parallel() + height, round := int64(1), 0 voteSet, _, privValidators := randVoteSet(height, round, PrevoteType, 4, 1) blockHash1 := random.RandBytes(32) @@ -465,6 +475,8 @@ func TestConflicts(t *testing.T) { } func TestMakeCommit(t *testing.T) { + t.Parallel() + height, round := int64(1), 0 voteSet, _, privValidators := randVoteSet(height, round, PrecommitType, 10, 1) blockHash, blockPartsHeader := crypto.CRandBytes(32), PartSetHeader{123, crypto.CRandBytes(32)} diff --git a/tm2/pkg/bft/types/vote_test.go b/tm2/pkg/bft/types/vote_test.go index 1028694ebc5..8b34e474fbd 100644 --- a/tm2/pkg/bft/types/vote_test.go +++ b/tm2/pkg/bft/types/vote_test.go @@ -50,6 +50,8 @@ func exampleVote(t byte) *Vote { // This test will fail and can be removed once CommitSig contains only sigs and // timestamps. func TestVoteEncoding(t *testing.T) { + t.Parallel() + vote := examplePrecommit() commitSig := vote.CommitSig() bz1 := amino.MustMarshal(vote) @@ -58,6 +60,8 @@ func TestVoteEncoding(t *testing.T) { } func TestVoteSignable(t *testing.T) { + t.Parallel() + vote := examplePrecommit() signBytes := vote.SignBytes("test_chain_id") @@ -68,6 +72,8 @@ func TestVoteSignable(t *testing.T) { } func TestVoteSignBytesTestVectors(t *testing.T) { + t.Parallel() + tests := []struct { chainID string vote *Vote @@ -147,6 +153,8 @@ func TestVoteSignBytesTestVectors(t *testing.T) { } func TestVoteProposalNotEq(t *testing.T) { + t.Parallel() + cv := CanonicalizeVote("", &Vote{Height: 1, Round: 1}) p := CanonicalizeProposal("", &Proposal{Height: 1, Round: 1}) vb, err := amino.MarshalSized(cv) @@ -157,6 +165,8 @@ func TestVoteProposalNotEq(t *testing.T) { } func TestVoteVerifySignature(t *testing.T) { + t.Parallel() + privVal := NewMockPV() pubkey := privVal.GetPubKey() @@ -186,6 +196,8 @@ func TestVoteVerifySignature(t *testing.T) { } func TestIsVoteTypeValid(t *testing.T) { + t.Parallel() + tc := []struct { name string in SignedMsgType @@ -199,6 +211,8 @@ func TestIsVoteTypeValid(t *testing.T) { for _, tt := range tc { tt := tt t.Run(tt.name, func(st *testing.T) { + st.Parallel() + if rs := IsVoteTypeValid(tt.in); rs != tt.out { t.Errorf("Got unexpected Vote type. Expected:\n%v\nGot:\n%v", rs, tt.out) } @@ -207,6 +221,8 @@ func TestIsVoteTypeValid(t *testing.T) { } func TestVoteVerify(t *testing.T) { + t.Parallel() + privVal := NewMockPV() pubkey := privVal.GetPubKey() @@ -225,6 +241,8 @@ func TestVoteVerify(t *testing.T) { } func TestMaxVoteBytes(t *testing.T) { + t.Parallel() + // time is varint encoded so need to pick the max. // year int, month Month, day, hour, min, sec, nsec int, loc *Location timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) @@ -256,6 +274,8 @@ func TestMaxVoteBytes(t *testing.T) { } func TestVoteString(t *testing.T) { + t.Parallel() + str := examplePrecommit().String() expected := `Vote{56789:6AF1F4111082 12345/02/2(Precommit) 8B01023386C3 000000000000 @ 2017-12-25T03:00:01.234Z}` if str != expected { @@ -270,6 +290,8 @@ func TestVoteString(t *testing.T) { } func TestVoteValidateBasic(t *testing.T) { + t.Parallel() + privVal := NewMockPV() testCases := []struct { @@ -289,6 +311,8 @@ func TestVoteValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + vote := examplePrecommit() err := privVal.SignVote("test_chain_id", vote) require.NoError(t, err) diff --git a/tm2/pkg/bft/wal/wal.go b/tm2/pkg/bft/wal/wal.go index b66a34df222..26f3deeb49c 100644 --- a/tm2/pkg/bft/wal/wal.go +++ b/tm2/pkg/bft/wal/wal.go @@ -11,11 +11,12 @@ import ( "path/filepath" "time" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/amino" auto "github.com/gnolang/gno/tm2/pkg/autofile" tmtime "github.com/gnolang/gno/tm2/pkg/bft/types/time" "github.com/gnolang/gno/tm2/pkg/errors" - "github.com/gnolang/gno/tm2/pkg/log" osm "github.com/gnolang/gno/tm2/pkg/os" "github.com/gnolang/gno/tm2/pkg/service" ) @@ -59,7 +60,7 @@ type MetaMessage struct { // WAL is an interface for any write-ahead logger. type WAL interface { // config methods - SetLogger(l log.Logger) + SetLogger(l *slog.Logger) // write methods Write(WALMessage) error @@ -128,7 +129,7 @@ func (wal *baseWAL) Group() *auto.Group { return wal.group } -func (wal *baseWAL) SetLogger(l log.Logger) { +func (wal *baseWAL) SetLogger(l *slog.Logger) { wal.BaseService.Logger = l wal.group.SetLogger(l) } @@ -436,7 +437,7 @@ OUTER_LOOP: return nil, false, nil } -// ///////////////////////////////////////////////////////////////////////////// +// ----------- // A WALWriter writes custom-encoded WAL messages to an output stream. // Each binary WAL entry is length encoded, then crc encoded, @@ -512,7 +513,7 @@ func (enc *WALWriter) WriteMeta(meta MetaMessage) error { return err } -// ///////////////////////////////////////////////////////////////////////////// +// ----------- // IsDataCorruptionError returns true if data has been corrupted inside WAL. func IsDataCorruptionError(err error) bool { @@ -644,7 +645,7 @@ type NopWAL struct{} var _ WAL = NopWAL{} -func (NopWAL) SetLogger(l log.Logger) {} +func (NopWAL) SetLogger(l *slog.Logger) {} func (NopWAL) Write(m WALMessage) error { return nil } func (NopWAL) WriteSync(m WALMessage) error { return nil } func (NopWAL) WriteMetaSync(m MetaMessage) error { return nil } diff --git a/tm2/pkg/bft/wal/wal_test.go b/tm2/pkg/bft/wal/wal_test.go index 4cb94766389..57386a701e1 100644 --- a/tm2/pkg/bft/wal/wal_test.go +++ b/tm2/pkg/bft/wal/wal_test.go @@ -29,7 +29,7 @@ type TestMessage struct { func (TestMessage) AssertWALMessage() {} -var testPackage = amino.RegisterPackage(amino.NewPackage( +var _ = amino.RegisterPackage(amino.NewPackage( "github.com/gnolang/gno/tm2/pkg/bft/wal", "wal", amino.GetCallersDirname(), @@ -39,6 +39,8 @@ var testPackage = amino.RegisterPackage(amino.NewPackage( )) func TestWALWriterReader(t *testing.T) { + t.Parallel() + now := tmtime.Now() msgs := []TimedWALMessage{ {Time: now, Msg: TestMessage{Duration: time.Second, Height: 1, Round: 1}}, @@ -96,6 +98,8 @@ func makeTempWAL(t *testing.T, maxMsgSize int64, walChunkSize int64) (wal *baseW } func TestWALWrite(t *testing.T) { + t.Parallel() + // Create WAL const walChunkSize = 100000 wal := makeTempWAL(t, maxTestMsgSize, walChunkSize) @@ -119,6 +123,8 @@ func TestWALWrite(t *testing.T) { } func TestWALSearchForHeight(t *testing.T) { + t.Parallel() + // Create WAL const numHeight, numRounds, dataSize = 100, 10000, 10 const walChunkSize = 100000 @@ -161,6 +167,8 @@ func TestWALSearchForHeight(t *testing.T) { } func TestWALPeriodicSync(t *testing.T) { + t.Parallel() + // Create WAL const numHeight, numRounds, dataSize = 100, 10000, 10 const walChunkSize = 100000 @@ -172,7 +180,7 @@ func TestWALPeriodicSync(t *testing.T) { // Is this needed? wal.SetFlushInterval(walTestFlushInterval) - wal.SetLogger(log.TestingLogger()) + wal.SetLogger(log.NewNoopLogger()) // Take snapshot of starting state. startInfo := wal.Group().ReadGroupInfo() diff --git a/tm2/pkg/bitarray/bit_array_test.go b/tm2/pkg/bitarray/bit_array_test.go index a70de110790..614d56d22cc 100644 --- a/tm2/pkg/bitarray/bit_array_test.go +++ b/tm2/pkg/bitarray/bit_array_test.go @@ -28,6 +28,8 @@ func randBitArray(bits int) (*BitArray, []byte) { } func TestAnd(t *testing.T) { + t.Parallel() + bA1, _ := randBitArray(51) bA2, _ := randBitArray(31) bA3 := bA1.And(bA2) @@ -52,6 +54,8 @@ func TestAnd(t *testing.T) { } func TestOr(t *testing.T) { + t.Parallel() + bA1, _ := randBitArray(51) bA2, _ := randBitArray(31) bA3 := bA1.Or(bA2) @@ -76,6 +80,8 @@ func TestOr(t *testing.T) { } func TestSub(t *testing.T) { + t.Parallel() + testCases := []struct { initBA string subtractingBA string @@ -107,6 +113,8 @@ func TestSub(t *testing.T) { } func TestPickRandom(t *testing.T) { + t.Parallel() + empty16Bits := "________________" empty64Bits := empty16Bits + empty16Bits + empty16Bits + empty16Bits testCases := []struct { @@ -134,6 +142,8 @@ func TestPickRandom(t *testing.T) { } func TestBytes(t *testing.T) { + t.Parallel() + bA := NewBitArray(4) bA.SetIndex(0, true) check := func(bA *BitArray, bz []byte) { @@ -163,6 +173,8 @@ func TestBytes(t *testing.T) { } func TestEmptyFull(t *testing.T) { + t.Parallel() + ns := []int{47, 123} for _, n := range ns { bA := NewBitArray(n) @@ -179,6 +191,8 @@ func TestEmptyFull(t *testing.T) { } func TestUpdateNeverPanics(t *testing.T) { + t.Parallel() + newRandBitArray := func(n int) *BitArray { ba, _ := randBitArray(n) return ba @@ -201,6 +215,8 @@ func TestUpdateNeverPanics(t *testing.T) { } func TestNewBitArrayNeverCrashesOnNegatives(t *testing.T) { + t.Parallel() + bitList := []int{-127, -128, -1 << 31} for _, bits := range bitList { _ = NewBitArray(bits) @@ -208,6 +224,8 @@ func TestNewBitArrayNeverCrashesOnNegatives(t *testing.T) { } func TestJSONMarshalUnmarshal(t *testing.T) { + t.Parallel() + bA1 := NewBitArray(0) bA2 := NewBitArray(1) @@ -233,6 +251,8 @@ func TestJSONMarshalUnmarshal(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.bA.String(), func(t *testing.T) { + t.Parallel() + bz, err := json.Marshal(tc.bA) require.NoError(t, err) diff --git a/tm2/pkg/bitarray/bitarray.proto b/tm2/pkg/bitarray/bitarray.proto index b7ff61c7e23..3e6bd03a0ae 100644 --- a/tm2/pkg/bitarray/bitarray.proto +++ b/tm2/pkg/bitarray/bitarray.proto @@ -5,6 +5,6 @@ option go_package = "github.com/gnolang/gno/tm2/pkg/bitarray/pb"; // messages message BitArray { - sint64 Bits = 1; - repeated uint64 Elems = 2; + sint64 bits = 1; + repeated uint64 elems = 2; } \ No newline at end of file diff --git a/tm2/pkg/clist/clist_test.go b/tm2/pkg/clist/clist_test.go index 496966d2d61..2cef3ef847f 100644 --- a/tm2/pkg/clist/clist_test.go +++ b/tm2/pkg/clist/clist_test.go @@ -2,8 +2,6 @@ package clist import ( "fmt" - "runtime" - "sync/atomic" "testing" "time" @@ -12,6 +10,8 @@ import ( ) func TestPanicOnMaxLength(t *testing.T) { + t.Parallel() + maxLength := 1000 l := newWithMax(maxLength) @@ -24,6 +24,8 @@ func TestPanicOnMaxLength(t *testing.T) { } func TestSmall(t *testing.T) { + t.Parallel() + l := New() el1 := l.PushBack(1) el2 := l.PushBack(2) @@ -64,110 +66,9 @@ func TestSmall(t *testing.T) { } } -// This test is quite hacky because it relies on SetFinalizer -// which isn't guaranteed to run at all. -// -//nolint:unused,deadcode -func _TestGCFifo(t *testing.T) { - t.Helper() - - if runtime.GOARCH != "amd64" { - t.Skipf("Skipping on non-amd64 machine") - } - - const numElements = 1000000 - l := New() - gcCount := new(uint64) - - // SetFinalizer doesn't work well with circular structures, - // so we construct a trivial non-circular structure to - // track. - type value struct { - Int int - } - done := make(chan struct{}) - - for i := 0; i < numElements; i++ { - v := new(value) - v.Int = i - l.PushBack(v) - runtime.SetFinalizer(v, func(v *value) { - atomic.AddUint64(gcCount, 1) - }) - } - - for el := l.Front(); el != nil; { - l.Remove(el) - // oldEl := el - el = el.Next() - // oldEl.DetachPrev() - // oldEl.DetachNext() - } - - runtime.GC() - time.Sleep(time.Second * 3) - runtime.GC() - time.Sleep(time.Second * 3) - _ = done - - if *gcCount != numElements { - t.Errorf("Expected gcCount to be %v, got %v", numElements, - *gcCount) - } -} - -// This test is quite hacky because it relies on SetFinalizer -// which isn't guaranteed to run at all. -// -//nolint:unused,deadcode -func _TestGCRandom(t *testing.T) { - t.Helper() - - if runtime.GOARCH != "amd64" { - t.Skipf("Skipping on non-amd64 machine") - } - - const numElements = 1000000 - l := New() - gcCount := 0 - - // SetFinalizer doesn't work well with circular structures, - // so we construct a trivial non-circular structure to - // track. - type value struct { - Int int - } - - for i := 0; i < numElements; i++ { - v := new(value) - v.Int = i - l.PushBack(v) - runtime.SetFinalizer(v, func(v *value) { - gcCount++ - }) - } - - els := make([]*CElement, 0, numElements) - for el := l.Front(); el != nil; el = el.Next() { - els = append(els, el) - } - - for _, i := range random.RandPerm(numElements) { - el := els[i] - l.Remove(el) - _ = el.Next() - } - - runtime.GC() - time.Sleep(time.Second * 3) - - if gcCount != numElements { - t.Errorf("Expected gcCount to be %v, got %v", numElements, - gcCount) - } -} - func TestScanRightDeleteRandom(t *testing.T) { + t.Parallel() + const numElements = 1000 const numTimes = 100 const numScanners = 10 @@ -239,6 +140,8 @@ func TestScanRightDeleteRandom(t *testing.T) { } func TestWaitChan(t *testing.T) { + t.Parallel() + l := New() ch := l.WaitChan() diff --git a/tm2/pkg/cmap/cmap_test.go b/tm2/pkg/cmap/cmap_test.go index e0fc5a5dc4e..d9051ea18d6 100644 --- a/tm2/pkg/cmap/cmap_test.go +++ b/tm2/pkg/cmap/cmap_test.go @@ -9,6 +9,8 @@ import ( ) func TestIterateKeysWithValues(t *testing.T) { + t.Parallel() + cmap := NewCMap() for i := 1; i <= 10; i++ { @@ -39,6 +41,8 @@ func TestIterateKeysWithValues(t *testing.T) { } func TestContains(t *testing.T) { + t.Parallel() + cmap := NewCMap() cmap.Set("key1", "value1") diff --git a/tm2/pkg/commands/command.go b/tm2/pkg/commands/command.go index e6fe36e3d6c..bc5f6f36cc5 100644 --- a/tm2/pkg/commands/command.go +++ b/tm2/pkg/commands/command.go @@ -5,6 +5,7 @@ import ( "errors" "flag" "fmt" + "os" "strings" "text/tabwriter" @@ -105,6 +106,19 @@ func (c *Command) AddSubCommands(cmds ...*Command) { } } +// Execute is a helper function for command entry. It wraps ParseAndRun and +// handles the flag.ErrHelp error, ensuring that every command with -h or +// --help won't show an error message: +// 'error parsing commandline arguments: flag: help requested' +func (c *Command) Execute(ctx context.Context, args []string) { + if err := c.ParseAndRun(ctx, args); err != nil { + if !errors.Is(err, flag.ErrHelp) { + _, _ = fmt.Fprintf(os.Stderr, "%+v\n", err) + } + os.Exit(1) + } +} + // ParseAndRun is a helper function that calls Parse and then Run in a single // invocation. It's useful for simple command trees that don't need two-phase // setup. diff --git a/tm2/pkg/commands/commands_test.go b/tm2/pkg/commands/commands_test.go index 96802b9d720..4879e667cf5 100644 --- a/tm2/pkg/commands/commands_test.go +++ b/tm2/pkg/commands/commands_test.go @@ -26,6 +26,8 @@ func (c *mockConfig) RegisterFlags(fs *flag.FlagSet) { } func TestCommandParseAndRun(t *testing.T) { + t.Parallel() + type flags struct { b bool s string @@ -215,7 +217,10 @@ func TestCommandParseAndRun(t *testing.T) { }, } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var ( invokedCmd string args []string @@ -416,9 +421,7 @@ func TestCommand_AddSubCommands(t *testing.T) { // Forked from peterbourgon/ff/ffcli func TestHelpUsage(t *testing.T) { - fs, _ := fftest.Pair() - var buf bytes.Buffer - fs.SetOutput(&buf) + t.Parallel() tests := []struct { name string @@ -432,7 +435,6 @@ func TestHelpUsage(t *testing.T) { shortUsage: "TestHelpUsage [flags] ", shortHelp: "Some short help", longHelp: "Some long help.", - flagSet: fs, }, expectedOutput: strings.TrimSpace(` USAGE @@ -455,7 +457,6 @@ FLAGS name: "TestHelpUsage", shortUsage: "TestHelpUsage [flags] ", shortHelp: "Some short help", - flagSet: fs, }, expectedOutput: strings.TrimSpace(` USAGE @@ -477,7 +478,6 @@ FLAGS command: &Command{ name: "TestHelpUsage", shortUsage: "TestHelpUsage [flags] ", - flagSet: fs, }, expectedOutput: strings.TrimSpace(` USAGE @@ -494,8 +494,15 @@ FLAGS }, } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { - buf.Reset() + t.Parallel() + + fs, _ := fftest.Pair() + var buf bytes.Buffer + fs.SetOutput(&buf) + + tt.command.flagSet = fs err := tt.command.ParseAndRun(context.Background(), []string{"-h"}) @@ -507,6 +514,8 @@ FLAGS // Forked from peterbourgon/ff/ffcli func TestNestedOutput(t *testing.T) { + t.Parallel() + var ( rootHelpOutput = "USAGE\n \n\nSUBCOMMANDS\n foo\n\n" fooHelpOutput = "USAGE\n foo\n\nSUBCOMMANDS\n bar\n\n" @@ -572,6 +581,8 @@ func TestNestedOutput(t *testing.T) { }, } { t.Run(testcase.name, func(t *testing.T) { + t.Parallel() + var ( rootfs = flag.NewFlagSet("root", flag.ContinueOnError) foofs = flag.NewFlagSet("foo", flag.ContinueOnError) diff --git a/tm2/pkg/commands/io.go b/tm2/pkg/commands/io.go index b23455e4602..fc0d3378a88 100644 --- a/tm2/pkg/commands/io.go +++ b/tm2/pkg/commands/io.go @@ -9,21 +9,42 @@ import ( // IO holds settable command // input, output and error buffers -type IO struct { - In io.Reader +type IO interface { + // getters + In() io.Reader + Out() io.WriteCloser + Err() io.WriteCloser + + // setters and helpers + SetIn(in io.Reader) + SetOut(out io.WriteCloser) + SetErr(err io.WriteCloser) + Println(args ...interface{}) + Printf(format string, args ...interface{}) + Printfln(format string, args ...interface{}) + ErrPrintln(args ...interface{}) + ErrPrintfln(format string, args ...interface{}) + GetCheckPassword(prompts [2]string, insecure bool) (string, error) + GetConfirmation(prompt string) (bool, error) + GetPassword(prompt string, insecure bool) (string, error) + GetString(prompt string) (string, error) +} + +type IOImpl struct { + in io.Reader inBuf *bufio.Reader - Out io.WriteCloser + out io.WriteCloser outBuf *bufio.Writer - Err io.WriteCloser + err io.WriteCloser errBuf *bufio.Writer } // NewDefaultIO returns a default command io // that utilizes standard input / output / error -func NewDefaultIO() *IO { - c := &IO{} +func NewDefaultIO() IO { + c := &IOImpl{} c.SetIn(os.Stdin) c.SetOut(os.Stdout) @@ -34,17 +55,21 @@ func NewDefaultIO() *IO { // NewTestIO returns a test command io // that only sets standard input (to avoid panics) -func NewTestIO() *IO { - c := &IO{} +func NewTestIO() IO { + c := &IOImpl{} c.SetIn(os.Stdin) return c } +func (io *IOImpl) In() io.Reader { return io.in } +func (io *IOImpl) Out() io.WriteCloser { return io.out } +func (io *IOImpl) Err() io.WriteCloser { return io.err } + // SetIn sets the input reader for the command io -func (io *IO) SetIn(in io.Reader) { - io.In = in - if inbuf, ok := io.In.(*bufio.Reader); ok { +func (io *IOImpl) SetIn(in io.Reader) { + io.in = in + if inbuf, ok := io.in.(*bufio.Reader); ok { io.inBuf = inbuf return @@ -54,19 +79,19 @@ func (io *IO) SetIn(in io.Reader) { } // SetOut sets the output writer for the command io -func (io *IO) SetOut(out io.WriteCloser) { - io.Out = out - io.outBuf = bufio.NewWriter(io.Out) +func (io *IOImpl) SetOut(out io.WriteCloser) { + io.out = out + io.outBuf = bufio.NewWriter(io.out) } // SetErr sets the error writer for the command io -func (io *IO) SetErr(err io.WriteCloser) { - io.Err = err - io.errBuf = bufio.NewWriter(io.Err) +func (io *IOImpl) SetErr(err io.WriteCloser) { + io.err = err + io.errBuf = bufio.NewWriter(io.err) } // Println prints a line terminated by a newline -func (io *IO) Println(args ...interface{}) { +func (io *IOImpl) Println(args ...interface{}) { if io.outBuf == nil { return } @@ -76,7 +101,7 @@ func (io *IO) Println(args ...interface{}) { } // Printf prints a formatted string without trailing newline -func (io *IO) Printf(format string, args ...interface{}) { +func (io *IOImpl) Printf(format string, args ...interface{}) { if io.outBuf == nil { return } @@ -86,7 +111,7 @@ func (io *IO) Printf(format string, args ...interface{}) { } // Printfln prints a formatted string terminated by a newline -func (io *IO) Printfln(format string, args ...interface{}) { +func (io *IOImpl) Printfln(format string, args ...interface{}) { if io.outBuf == nil { return } @@ -97,7 +122,7 @@ func (io *IO) Printfln(format string, args ...interface{}) { // ErrPrintln prints a line terminated by a newline to // cmd.Err(Buf) -func (io *IO) ErrPrintln(args ...interface{}) { +func (io *IOImpl) ErrPrintln(args ...interface{}) { if io.errBuf == nil { return } @@ -107,7 +132,7 @@ func (io *IO) ErrPrintln(args ...interface{}) { } // ErrPrintfln prints a formatted string terminated by a newline to cmd.Err(Buf) -func (io *IO) ErrPrintfln(format string, args ...interface{}) { +func (io *IOImpl) ErrPrintfln(format string, args ...interface{}) { if io.errBuf == nil { return } diff --git a/tm2/pkg/commands/utils.go b/tm2/pkg/commands/utils.go index c7ce19e8138..90b6fa12b64 100644 --- a/tm2/pkg/commands/utils.go +++ b/tm2/pkg/commands/utils.go @@ -9,7 +9,7 @@ import ( ) // GetPassword fetches the password using the provided prompt, if any -func (io *IO) GetPassword( +func (io *IOImpl) GetPassword( prompt string, insecure bool, ) (string, error) { @@ -27,7 +27,7 @@ func (io *IO) GetPassword( } // readLine reads a new line from standard input -func (io *IO) readLine() (string, error) { +func (io *IOImpl) readLine() (string, error) { input, err := io.inBuf.ReadString('\n') if err != nil { return "", err @@ -52,7 +52,7 @@ func readPassword() (string, error) { // GetConfirmation will request user give the confirmation from stdin. // "y", "Y", "yes", "YES", and "Yes" all count as confirmations. // If the input is not recognized, it returns false and a nil error. -func (io *IO) GetConfirmation(prompt string) (bool, error) { +func (io *IOImpl) GetConfirmation(prompt string) (bool, error) { // On stderr so it isn't part of bash output. io.ErrPrintfln("%s [y/n]:", prompt) @@ -78,7 +78,7 @@ func (io *IO) GetConfirmation(prompt string) (bool, error) { // match (for creating a new password). // It enforces the password length. Only parses password once if // input is piped in. -func (io *IO) GetCheckPassword( +func (io *IOImpl) GetCheckPassword( prompts [2]string, insecure bool, ) (string, error) { @@ -100,7 +100,7 @@ func (io *IO) GetCheckPassword( } // GetString simply returns the trimmed string output of a given reader. -func (io *IO) GetString(prompt string) (string, error) { +func (io *IOImpl) GetString(prompt string) (string, error) { if prompt != "" { // On stderr so it isn't part of bash output. io.ErrPrintln(prompt) diff --git a/tm2/pkg/crypto/bcrypt/bcrypt_test.go b/tm2/pkg/crypto/bcrypt/bcrypt_test.go index 96634260d65..92b296bfe52 100644 --- a/tm2/pkg/crypto/bcrypt/bcrypt_test.go +++ b/tm2/pkg/crypto/bcrypt/bcrypt_test.go @@ -12,6 +12,8 @@ import ( ) func TestBcryptingIsEasy(t *testing.T) { + t.Parallel() + pass := []byte("mypassword") salt := []byte("1234567890123456") hp, err := GenerateFromPassword(salt, pass, 0) @@ -31,6 +33,8 @@ func TestBcryptingIsEasy(t *testing.T) { } func TestBcryptingIsCorrect(t *testing.T) { + t.Parallel() + pass := []byte("allmine") salt := []byte("XajjQvNhvvRt5GSeFk1xFe") expectedHash := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga") @@ -56,6 +60,8 @@ func TestBcryptingIsCorrect(t *testing.T) { } func TestVeryShortPasswords(t *testing.T) { + t.Parallel() + key := []byte("k") salt := []byte("XajjQvNhvvRt5GSeFk1xFe") _, err := bcrypt(key, 10, salt) @@ -65,6 +71,8 @@ func TestVeryShortPasswords(t *testing.T) { } func TestTooLongPasswordsWork(t *testing.T) { + t.Parallel() + salt := []byte("XajjQvNhvvRt5GSeFk1xFe") // One byte over the usual 56 byte limit that blowfish has tooLongPass := []byte("012345678901234567890123456789012345678901234567890123456") @@ -92,6 +100,8 @@ var invalidTests = []InvalidHashTest{ } func TestInvalidHashErrors(t *testing.T) { + t.Parallel() + check := func(name string, expected, err error) { if err == nil { t.Errorf("%s: Should have returned an error", name) @@ -109,6 +119,8 @@ func TestInvalidHashErrors(t *testing.T) { } func TestUnpaddedBase64Encoding(t *testing.T) { + t.Parallel() + original := []byte{101, 201, 101, 75, 19, 227, 199, 20, 239, 236, 133, 32, 30, 109, 243, 30} encodedOriginal := []byte("XajjQvNhvvRt5GSeFk1xFe") @@ -129,6 +141,8 @@ func TestUnpaddedBase64Encoding(t *testing.T) { } func TestCost(t *testing.T) { + t.Parallel() + suffix := "XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C" for _, vers := range []string{"2a", "2"} { for _, cost := range []int{4, 10} { @@ -151,6 +165,8 @@ func TestCost(t *testing.T) { } func TestCostValidationInHash(t *testing.T) { + t.Parallel() + if testing.Short() { return } @@ -185,6 +201,8 @@ func TestCostValidationInHash(t *testing.T) { } func TestCostReturnsWithLeadingZeroes(t *testing.T) { + t.Parallel() + salt := []byte("1234567890123456") hp, _ := newFromPassword(salt, []byte("abcdefgh"), 7) cost := hp.Hash()[4:7] @@ -196,6 +214,8 @@ func TestCostReturnsWithLeadingZeroes(t *testing.T) { } func TestMinorNotRequired(t *testing.T) { + t.Parallel() + noMinorHash := []byte("$2$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga") h, err := newFromHash(noMinorHash) if err != nil { @@ -233,6 +253,8 @@ func BenchmarkDefaultCost(b *testing.B) { // See Issue https://github.com/golang/go/issues/20425. func TestNoSideEffectsFromCompare(t *testing.T) { + t.Parallel() + source := []byte("passw0rd123456") password := source[:len(source)-6] token := source[len(source)-6:] diff --git a/tm2/pkg/crypto/bech32_test.go b/tm2/pkg/crypto/bech32_test.go index 13bdaa8e816..f5bc3e9ed7c 100644 --- a/tm2/pkg/crypto/bech32_test.go +++ b/tm2/pkg/crypto/bech32_test.go @@ -21,6 +21,8 @@ var invalidStrs = []string{ } func TestEmptyAddresses(t *testing.T) { + t.Parallel() + require.Equal(t, (crypto.Address{}).String(), "g1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqluuxe") addr := crypto.AddressFromBytes(make([]byte, 20)) @@ -47,6 +49,8 @@ func testMarshal(t *testing.T, addr crypto.Address, marshal func(orig interface{ } func TestRandBech32AddrConsistency(t *testing.T) { + t.Parallel() + var pub ed25519.PubKeyEd25519 for i := 0; i < 1000; i++ { diff --git a/tm2/pkg/crypto/bip39/bip39_test.go b/tm2/pkg/crypto/bip39/bip39_test.go index cbe15be6c3f..7854db76f53 100644 --- a/tm2/pkg/crypto/bip39/bip39_test.go +++ b/tm2/pkg/crypto/bip39/bip39_test.go @@ -14,6 +14,8 @@ type Vector struct { } func TestBip39(t *testing.T) { + t.Parallel() + for _, vector := range testVectors() { entropy, err := hex.DecodeString(vector.entropy) assert.NoError(t, err) @@ -31,6 +33,8 @@ func TestBip39(t *testing.T) { } func TestIsMnemonicValid(t *testing.T) { + t.Parallel() + for _, vector := range badMnemonicSentences() { assert.Equal(t, IsMnemonicValid(vector.mnemonic), false) } @@ -41,6 +45,8 @@ func TestIsMnemonicValid(t *testing.T) { } func TestInvalidMnemonicFails(t *testing.T) { + t.Parallel() + for _, vector := range badMnemonicSentences() { _, err := MnemonicToByteArray(vector.mnemonic) assert.NotNil(t, err) @@ -48,6 +54,8 @@ func TestInvalidMnemonicFails(t *testing.T) { } func TestValidateEntropyWithChecksumBitSize(t *testing.T) { + t.Parallel() + // Good tests. for i := 1; i <= (12*32 + 12); i++ { err := validateEntropyWithChecksumBitSize(i) @@ -74,6 +82,8 @@ func TestValidateEntropyWithChecksumBitSize(t *testing.T) { } func TestNewEntropy(t *testing.T) { + t.Parallel() + // Good tests. for i := 128; i <= 256; i += 32 { _, err := NewEntropy(i) diff --git a/tm2/pkg/crypto/ed25519/ed25519.proto b/tm2/pkg/crypto/ed25519/ed25519.proto index 35a6b4e443e..407de42b668 100644 --- a/tm2/pkg/crypto/ed25519/ed25519.proto +++ b/tm2/pkg/crypto/ed25519/ed25519.proto @@ -5,9 +5,9 @@ option go_package = "github.com/gnolang/gno/tm2/pkg/crypto/ed25519/pb"; // messages message PubKeyEd25519 { - bytes Value = 1; + bytes value = 1; } message PrivKeyEd25519 { - bytes Value = 1; + bytes value = 1; } \ No newline at end of file diff --git a/tm2/pkg/crypto/ed25519/ed25519_test.go b/tm2/pkg/crypto/ed25519/ed25519_test.go index c07b7ae2298..67daacaaff5 100644 --- a/tm2/pkg/crypto/ed25519/ed25519_test.go +++ b/tm2/pkg/crypto/ed25519/ed25519_test.go @@ -1,15 +1,19 @@ package ed25519_test import ( + "encoding/hex" "testing" - "github.com/gnolang/gno/tm2/pkg/crypto" - "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" ) func TestSignAndValidateEd25519(t *testing.T) { + t.Parallel() + privKey := ed25519.GenPrivKey() pubKey := privKey.PubKey() @@ -26,3 +30,19 @@ func TestSignAndValidateEd25519(t *testing.T) { assert.False(t, pubKey.VerifyBytes(msg, sig)) } + +const ( + privKeySecretGolden = "secret_golden" + msgGolden = "msg_golden" + signedGolden = "f9d4e6a665dfb6cd7e2fedf0d46a1725472e640a5e93d654ce4caa986e5defd23c8b3af76aa6e39c24c582f0ebee860f66254b29cf6d034ce461ae2773133703" +) + +func TestSignAndVerifyGolden(t *testing.T) { + privKey := ed25519.GenPrivKeyFromSecret([]byte(privKeySecretGolden)) + // pubKey := privKey.PubKey() + out, err := privKey.Sign([]byte(msgGolden)) + require.NoError(t, err) + + hexOut := hex.EncodeToString(out) + require.Equal(t, signedGolden, hexOut) +} diff --git a/tm2/pkg/crypto/hd/fundraiser_test.go b/tm2/pkg/crypto/hd/fundraiser_test.go index dabfd670c37..884425c6c39 100644 --- a/tm2/pkg/crypto/hd/fundraiser_test.go +++ b/tm2/pkg/crypto/hd/fundraiser_test.go @@ -43,6 +43,8 @@ func initFundraiserTestVectors(t *testing.T) []addrData { } func TestFundraiserCompatibility(t *testing.T) { + t.Parallel() + hdToAddrTable := initFundraiserTestVectors(t) for i, d := range hdToAddrTable { diff --git a/tm2/pkg/crypto/hd/hd.proto b/tm2/pkg/crypto/hd/hd.proto index 949408bb00c..48e2524c28f 100644 --- a/tm2/pkg/crypto/hd/hd.proto +++ b/tm2/pkg/crypto/hd/hd.proto @@ -5,9 +5,9 @@ option go_package = "github.com/gnolang/gno/tm2/pkg/crypto/hd/pb"; // messages message Bip44Params { - uint32 Purpose = 1; - uint32 CoinType = 2; - uint32 Account = 3; - bool Change = 4; - uint32 AddressIndex = 5; + uint32 purpose = 1; + uint32 coin_type = 2 [json_name = "coinType"]; + uint32 account = 3; + bool change = 4; + uint32 address_index = 5 [json_name = "addressIndex"]; } \ No newline at end of file diff --git a/tm2/pkg/crypto/hd/hdpath.go b/tm2/pkg/crypto/hd/hdpath.go index a03c34fb0f3..32de2c5bff6 100644 --- a/tm2/pkg/crypto/hd/hdpath.go +++ b/tm2/pkg/crypto/hd/hdpath.go @@ -24,7 +24,7 @@ import ( "strconv" "strings" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" ) // BIP44Params wraps BIP 44 params (5 level BIP 32 path). @@ -212,7 +212,7 @@ func derivePrivateKey(privKeyBytes [32]byte, chainCode [32]byte, index uint32, h data = append([]byte{byte(0)}, privKeyBytes[:]...) } else { // this can't return an error: - _, ecPub := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes[:]) + _, ecPub := btcec.PrivKeyFromBytes(privKeyBytes[:]) pubkeyBytes := ecPub.SerializeCompressed() data = pubkeyBytes diff --git a/tm2/pkg/crypto/hd/hdpath_test.go b/tm2/pkg/crypto/hd/hdpath_test.go index 1e66a9fba2c..31e806b2b1a 100644 --- a/tm2/pkg/crypto/hd/hdpath_test.go +++ b/tm2/pkg/crypto/hd/hdpath_test.go @@ -34,6 +34,8 @@ const ( ) func TestStringifyFundraiserPathParams(t *testing.T) { + t.Parallel() + path := NewFundraiserParams(4, testCoinType, 22) require.Equal(t, "44'/118'/4'/0/22", path.String()) @@ -45,6 +47,8 @@ func TestStringifyFundraiserPathParams(t *testing.T) { } func TestPathToArray(t *testing.T) { + t.Parallel() + path := NewParams(44, 118, 1, false, 4) require.Equal(t, "[44 118 1 0 4]", fmt.Sprintf("%v", path.DerivationPath())) @@ -53,6 +57,8 @@ func TestPathToArray(t *testing.T) { } func TestParamsFromPath(t *testing.T) { + t.Parallel() + goodCases := []struct { params *BIP44Params path string diff --git a/tm2/pkg/crypto/keys/armor/armor.go b/tm2/pkg/crypto/keys/armor/armor.go index 7233b27123a..22315f8c521 100644 --- a/tm2/pkg/crypto/keys/armor/armor.go +++ b/tm2/pkg/crypto/keys/armor/armor.go @@ -128,7 +128,7 @@ func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (privK } key = crypto.Sha256(key) // Get 32 bytes privKeyBytes, err := xsalsa20symmetric.DecryptSymmetric(encBytes, key) - if err != nil && err.Error() == "Ciphertext decryption failed" { + if err != nil && err.Error() == "ciphertext decryption failed" { return privKey, keyerror.NewErrWrongPassword() } else if err != nil { return privKey, err diff --git a/tm2/pkg/crypto/keys/client/add.go b/tm2/pkg/crypto/keys/client/add.go index 5f90a9f874e..49436220974 100644 --- a/tm2/pkg/crypto/keys/client/add.go +++ b/tm2/pkg/crypto/keys/client/add.go @@ -14,24 +14,24 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto/multisig" ) -type addCfg struct { - rootCfg *baseCfg - - multisig commands.StringArr - multisigThreshold int - noSort bool - publicKey string - useLedger bool - recover bool - noBackup bool - dryRun bool - account uint64 - index uint64 +type AddCfg struct { + RootCfg *BaseCfg + + Multisig commands.StringArr + MultisigThreshold int + NoSort bool + PublicKey string + UseLedger bool + Recover bool + NoBackup bool + DryRun bool + Account uint64 + Index uint64 } -func newAddCmd(rootCfg *baseCfg) *commands.Command { - cfg := &addCfg{ - rootCfg: rootCfg, +func NewAddCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &AddCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -42,76 +42,76 @@ func newAddCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execAdd(cfg, args, commands.NewDefaultIO()) + return execAdd(cfg, args, io) }, ) } -func (c *addCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *AddCfg) RegisterFlags(fs *flag.FlagSet) { fs.Var( - &c.multisig, + &c.Multisig, "multisig", "Construct and store a multisig public key (implies --pubkey)", ) fs.IntVar( - &c.multisigThreshold, + &c.MultisigThreshold, "threshold", 1, "K out of N required signatures. For use in conjunction with --multisig", ) fs.BoolVar( - &c.noSort, + &c.NoSort, "nosort", false, "Keys passed to --multisig are taken in the order they're supplied", ) fs.StringVar( - &c.publicKey, + &c.PublicKey, "pubkey", "", "Parse a public key in bech32 format and save it to disk", ) fs.BoolVar( - &c.useLedger, + &c.UseLedger, "ledger", false, "Store a local reference to a private key on a Ledger device", ) fs.BoolVar( - &c.recover, + &c.Recover, "recover", false, "Provide seed phrase to recover existing key instead of creating", ) fs.BoolVar( - &c.noBackup, + &c.NoBackup, "nobackup", false, "Don't print out seed phrase (if others are watching the terminal)", ) fs.BoolVar( - &c.dryRun, + &c.DryRun, "dryrun", false, "Perform action, but don't add key to local keystore", ) fs.Uint64Var( - &c.account, + &c.Account, "account", 0, "Account number for HD derivation", ) fs.Uint64Var( - &c.index, + &c.Index, "index", 0, "Address index number for HD derivation", @@ -131,7 +131,7 @@ input output - armor encrypted private key (saved to file) */ -func execAdd(cfg *addCfg, args []string, io *commands.IO) error { +func execAdd(cfg *AddCfg, args []string, io commands.IO) error { var ( kb keys.Keybase err error @@ -143,21 +143,20 @@ func execAdd(cfg *addCfg, args []string, io *commands.IO) error { } name := args[0] - showMnemonic := !cfg.noBackup + showMnemonic := !cfg.NoBackup - if cfg.dryRun { + if cfg.DryRun { // we throw this away, so don't enforce args, // we want to get a new random seed phrase quickly kb = keys.NewInMemory() encryptPassword = DryRunKeyPass } else { - kb, err = keys.NewKeyBaseFromDir(cfg.rootCfg.Home) + kb, err = keys.NewKeyBaseFromDir(cfg.RootCfg.Home) if err != nil { return err } - _, err = kb.GetByName(name) - if err == nil { + if has, err := kb.HasByName(name); err == nil && has { // account exists, ask for user confirmation response, err2 := io.GetConfirmation(fmt.Sprintf("Override the existing name %s", name)) if err2 != nil { @@ -168,11 +167,11 @@ func execAdd(cfg *addCfg, args []string, io *commands.IO) error { } } - multisigKeys := cfg.multisig + multisigKeys := cfg.Multisig if len(multisigKeys) != 0 { var pks []crypto.PubKey - multisigThreshold := cfg.multisigThreshold + multisigThreshold := cfg.MultisigThreshold if err := keys.ValidateMultisigThreshold(multisigThreshold, len(multisigKeys)); err != nil { return err } @@ -186,7 +185,7 @@ func execAdd(cfg *addCfg, args []string, io *commands.IO) error { } // Handle --nosort - if !cfg.noSort { + if !cfg.NoSort { sort.Slice(pks, func(i, j int) bool { return pks[i].Address().Compare(pks[j].Address()) < 0 }) @@ -202,13 +201,13 @@ func execAdd(cfg *addCfg, args []string, io *commands.IO) error { } // ask for a password when generating a local key - if cfg.publicKey == "" && !cfg.useLedger { + if cfg.PublicKey == "" && !cfg.UseLedger { encryptPassword, err = io.GetCheckPassword( [2]string{ "Enter a passphrase to encrypt your key to disk:", "Repeat the passphrase:", }, - cfg.rootCfg.InsecurePasswordStdin, + cfg.RootCfg.InsecurePasswordStdin, ) if err != nil { return err @@ -216,8 +215,8 @@ func execAdd(cfg *addCfg, args []string, io *commands.IO) error { } } - if cfg.publicKey != "" { - pk, err := crypto.PubKeyFromBech32(cfg.publicKey) + if cfg.PublicKey != "" { + pk, err := crypto.PubKeyFromBech32(cfg.PublicKey) if err != nil { return err } @@ -228,11 +227,11 @@ func execAdd(cfg *addCfg, args []string, io *commands.IO) error { return nil } - account := cfg.account - index := cfg.index + account := cfg.Account + index := cfg.Index // If we're using ledger, only thing we need is the path and the bech32 prefix. - if cfg.useLedger { + if cfg.UseLedger { bech32PrefixAddr := crypto.Bech32AddrPrefix info, err := kb.CreateLedger(name, keys.Secp256k1, bech32PrefixAddr, uint32(account), uint32(index)) if err != nil { @@ -246,7 +245,7 @@ func execAdd(cfg *addCfg, args []string, io *commands.IO) error { var mnemonic string const bip39Passphrase string = "" // XXX research. - if cfg.recover { + if cfg.Recover { bip39Message := "Enter your bip39 mnemonic" mnemonic, err = io.GetString(bip39Message) if err != nil { @@ -259,7 +258,7 @@ func execAdd(cfg *addCfg, args []string, io *commands.IO) error { } if len(mnemonic) == 0 { - mnemonic, err = generateMnemonic(mnemonicEntropySize) + mnemonic, err = GenerateMnemonic(mnemonicEntropySize) if err != nil { return err } @@ -271,7 +270,7 @@ func execAdd(cfg *addCfg, args []string, io *commands.IO) error { } // Recover key from seed passphrase - if cfg.recover { + if cfg.Recover { // Hide mnemonic from output showMnemonic = false mnemonic = "" @@ -280,7 +279,7 @@ func execAdd(cfg *addCfg, args []string, io *commands.IO) error { return printCreate(info, showMnemonic, mnemonic, io) } -func printCreate(info keys.Info, showMnemonic bool, mnemonic string, io *commands.IO) error { +func printCreate(info keys.Info, showMnemonic bool, mnemonic string, io commands.IO) error { io.Println("") printNewInfo(info, io) @@ -296,7 +295,7 @@ It is the only way to recover your account if you ever forget your password. return nil } -func printNewInfo(info keys.Info, io *commands.IO) { +func printNewInfo(info keys.Info, io commands.IO) { keyname := info.GetName() keytype := info.GetType() keypub := info.GetPubKey() diff --git a/tm2/pkg/crypto/keys/client/add_test.go b/tm2/pkg/crypto/keys/client/add_test.go index 94cb945f113..4110ea32c9a 100644 --- a/tm2/pkg/crypto/keys/client/add_test.go +++ b/tm2/pkg/crypto/keys/client/add_test.go @@ -20,8 +20,8 @@ func Test_execAddBasic(t *testing.T) { assert.NotNil(t, kbHome) defer kbCleanUp() - cfg := &addCfg{ - rootCfg: &baseCfg{ + cfg := &AddCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ InsecurePasswordStdin: true, Home: kbHome, @@ -59,13 +59,13 @@ func Test_execAddPublicKey(t *testing.T) { assert.NotNil(t, kbHome) defer kbCleanUp() - cfg := &addCfg{ - rootCfg: &baseCfg{ + cfg := &AddCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ Home: kbHome, }, }, - publicKey: test2PubkeyBech32, // test2 account + PublicKey: test2PubkeyBech32, // test2 account } if err := execAdd(cfg, []string{"test2"}, nil); err != nil { @@ -80,14 +80,14 @@ func Test_execAddRecover(t *testing.T) { assert.NotNil(t, kbHome) defer kbCleanUp() - cfg := &addCfg{ - rootCfg: &baseCfg{ + cfg := &AddCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ InsecurePasswordStdin: true, Home: kbHome, }, }, - recover: true, // init test2 account + Recover: true, // init test2 account } test2Name := "test2" diff --git a/tm2/pkg/crypto/keys/client/addpkg.go b/tm2/pkg/crypto/keys/client/addpkg.go deleted file mode 100644 index 885e1f123d7..00000000000 --- a/tm2/pkg/crypto/keys/client/addpkg.go +++ /dev/null @@ -1,222 +0,0 @@ -package client - -// TODO: move most of the logic in ROOT/gno.land/... - -import ( - "context" - "flag" - "fmt" - - "github.com/gnolang/gno/gno.land/pkg/sdk/vm" - gno "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/tm2/pkg/amino" - "github.com/gnolang/gno/tm2/pkg/commands" - "github.com/gnolang/gno/tm2/pkg/crypto/keys" - "github.com/gnolang/gno/tm2/pkg/errors" - "github.com/gnolang/gno/tm2/pkg/std" -) - -type addPkgCfg struct { - rootCfg *makeTxCfg - - pkgPath string - pkgDir string - deposit string -} - -func newAddPkgCmd(rootCfg *makeTxCfg) *commands.Command { - cfg := &addPkgCfg{ - rootCfg: rootCfg, - } - - return commands.NewCommand( - commands.Metadata{ - Name: "addpkg", - ShortUsage: "addpkg [flags] ", - ShortHelp: "Uploads a new package", - }, - cfg, - func(_ context.Context, args []string) error { - return execAddPkg(cfg, args, commands.NewDefaultIO()) - }, - ) -} - -func (c *addPkgCfg) RegisterFlags(fs *flag.FlagSet) { - fs.StringVar( - &c.pkgPath, - "pkgpath", - "", - "package path (required)", - ) - - fs.StringVar( - &c.pkgDir, - "pkgdir", - "", - "path to package files (required)", - ) - - fs.StringVar( - &c.deposit, - "deposit", - "", - "deposit coins", - ) -} - -func execAddPkg(cfg *addPkgCfg, args []string, io *commands.IO) error { - if cfg.pkgPath == "" { - return errors.New("pkgpath not specified") - } - if cfg.pkgDir == "" { - return errors.New("pkgdir not specified") - } - - if len(args) != 1 { - return flag.ErrHelp - } - - // read account pubkey. - nameOrBech32 := args[0] - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.rootCfg.Home) - if err != nil { - return err - } - info, err := kb.GetByNameOrAddress(nameOrBech32) - if err != nil { - return err - } - creator := info.GetAddress() - // info.GetPubKey() - - // parse deposit. - deposit, err := std.ParseCoins(cfg.deposit) - if err != nil { - panic(err) - } - - // open files in directory as MemPackage. - memPkg := gno.ReadMemPackage(cfg.pkgDir, cfg.pkgPath) - if memPkg.IsEmpty() { - panic(fmt.Sprintf("found an empty package %q", cfg.pkgPath)) - } - - // precompile and validate syntax - err = gno.PrecompileAndCheckMempkg(memPkg) - if err != nil { - panic(err) - } - - // parse gas wanted & fee. - gaswanted := cfg.rootCfg.gasWanted - gasfee, err := std.ParseCoin(cfg.rootCfg.gasFee) - if err != nil { - panic(err) - } - // construct msg & tx and marshal. - msg := vm.MsgAddPackage{ - Creator: creator, - Package: memPkg, - Deposit: deposit, - } - tx := std.Tx{ - Msgs: []std.Msg{msg}, - Fee: std.NewFee(gaswanted, gasfee), - Signatures: nil, - Memo: cfg.rootCfg.memo, - } - - if cfg.rootCfg.broadcast { - err := signAndBroadcast(cfg.rootCfg, args, tx, io) - if err != nil { - return err - } - } else { - fmt.Println(string(amino.MustMarshalJSON(tx))) - } - return nil -} - -func signAndBroadcast( - cfg *makeTxCfg, - args []string, - tx std.Tx, - io *commands.IO, -) error { - baseopts := cfg.rootCfg - txopts := cfg - - // query account - nameOrBech32 := args[0] - kb, err := keys.NewKeyBaseFromDir(baseopts.Home) - if err != nil { - return err - } - info, err := kb.GetByNameOrAddress(nameOrBech32) - if err != nil { - return err - } - accountAddr := info.GetAddress() - - qopts := &queryCfg{ - rootCfg: baseopts, - path: fmt.Sprintf("auth/accounts/%s", accountAddr), - } - qres, err := queryHandler(qopts) - if err != nil { - return errors.Wrap(err, "query account") - } - var qret struct{ BaseAccount std.BaseAccount } - err = amino.UnmarshalJSON(qres.Response.Data, &qret) - if err != nil { - return err - } - - // sign tx - accountNumber := qret.BaseAccount.AccountNumber - sequence := qret.BaseAccount.Sequence - sopts := &signCfg{ - rootCfg: baseopts, - sequence: sequence, - accountNumber: accountNumber, - chainID: txopts.chainID, - nameOrBech32: nameOrBech32, - txJSON: amino.MustMarshalJSON(tx), - } - if baseopts.Quiet { - sopts.pass, err = io.GetPassword("", baseopts.InsecurePasswordStdin) - } else { - sopts.pass, err = io.GetPassword("Enter password.", baseopts.InsecurePasswordStdin) - } - if err != nil { - return err - } - - signedTx, err := SignHandler(sopts) - if err != nil { - return errors.Wrap(err, "sign tx") - } - - // broadcast signed tx - bopts := &broadcastCfg{ - rootCfg: baseopts, - tx: signedTx, - } - bres, err := broadcastHandler(bopts) - if err != nil { - return errors.Wrap(err, "broadcast tx") - } - if bres.CheckTx.IsErr() { - return errors.Wrap(bres.CheckTx.Error, "check transaction failed: log:%s", bres.CheckTx.Log) - } - if bres.DeliverTx.IsErr() { - return errors.Wrap(bres.DeliverTx.Error, "deliver transaction failed: log:%s", bres.DeliverTx.Log) - } - io.Println(string(bres.DeliverTx.Data)) - io.Println("OK!") - io.Println("GAS WANTED:", bres.DeliverTx.GasWanted) - io.Println("GAS USED: ", bres.DeliverTx.GasUsed) - - return nil -} diff --git a/tm2/pkg/crypto/keys/client/broadcast.go b/tm2/pkg/crypto/keys/client/broadcast.go index 039a9557c38..3443f4678c6 100644 --- a/tm2/pkg/crypto/keys/client/broadcast.go +++ b/tm2/pkg/crypto/keys/client/broadcast.go @@ -14,18 +14,18 @@ import ( "github.com/gnolang/gno/tm2/pkg/std" ) -type broadcastCfg struct { - rootCfg *baseCfg +type BroadcastCfg struct { + RootCfg *BaseCfg - dryRun bool + DryRun bool // internal tx *std.Tx } -func newBroadcastCmd(rootCfg *baseCfg) *commands.Command { - cfg := &broadcastCfg{ - rootCfg: rootCfg, +func NewBroadcastCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &BroadcastCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -41,16 +41,16 @@ func newBroadcastCmd(rootCfg *baseCfg) *commands.Command { ) } -func (c *broadcastCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *BroadcastCfg) RegisterFlags(fs *flag.FlagSet) { fs.BoolVar( - &c.dryRun, + &c.DryRun, "dry-run", false, "perform a dry-run broadcast", ) } -func execBroadcast(cfg *broadcastCfg, args []string, io *commands.IO) error { +func execBroadcast(cfg *BroadcastCfg, args []string, io commands.IO) error { if len(args) != 1 { return flag.ErrHelp } @@ -67,7 +67,7 @@ func execBroadcast(cfg *broadcastCfg, args []string, io *commands.IO) error { } cfg.tx = &tx - res, err := broadcastHandler(cfg) + res, err := BroadcastHandler(cfg) if err != nil { return err } @@ -85,12 +85,12 @@ func execBroadcast(cfg *broadcastCfg, args []string, io *commands.IO) error { return nil } -func broadcastHandler(cfg *broadcastCfg) (*ctypes.ResultBroadcastTxCommit, error) { +func BroadcastHandler(cfg *BroadcastCfg) (*ctypes.ResultBroadcastTxCommit, error) { if cfg.tx == nil { return nil, errors.New("invalid tx") } - remote := cfg.rootCfg.Remote + remote := cfg.RootCfg.Remote if remote == "" || remote == "y" { return nil, errors.New("missing remote url") } @@ -102,8 +102,8 @@ func broadcastHandler(cfg *broadcastCfg) (*ctypes.ResultBroadcastTxCommit, error cli := client.NewHTTP(remote, "/websocket") - if cfg.dryRun { - return simulateTx(cli, bz) + if cfg.DryRun { + return SimulateTx(cli, bz) } bres, err := cli.BroadcastTxCommit(bz) @@ -114,7 +114,7 @@ func broadcastHandler(cfg *broadcastCfg) (*ctypes.ResultBroadcastTxCommit, error return bres, nil } -func simulateTx(cli client.ABCIClient, tx []byte) (*ctypes.ResultBroadcastTxCommit, error) { +func SimulateTx(cli client.ABCIClient, tx []byte) (*ctypes.ResultBroadcastTxCommit, error) { bres, err := cli.ABCIQuery(".app/simulate", tx) if err != nil { return nil, errors.Wrap(err, "simulate tx") diff --git a/tm2/pkg/crypto/keys/client/common.go b/tm2/pkg/crypto/keys/client/common.go index cfadd05120b..a6b52b6cad3 100644 --- a/tm2/pkg/crypto/keys/client/common.go +++ b/tm2/pkg/crypto/keys/client/common.go @@ -1,11 +1,5 @@ package client -import ( - "fmt" - "os" - "path/filepath" -) - type BaseOptions struct { Home string Remote string @@ -15,27 +9,9 @@ type BaseOptions struct { } var DefaultBaseOptions = BaseOptions{ - Home: HomeDir(), + Home: "", Remote: "127.0.0.1:26657", Quiet: false, InsecurePasswordStdin: false, Config: "", } - -func HomeDir() string { - // if environment variable is set, always use that. - // otherwise, use config dir (varies depending on OS) + "gno" - var err error - dir := os.Getenv("GNO_HOME") - if dir != "" { - return dir - } - dir, err = os.UserConfigDir() - if err != nil { - panic(fmt.Errorf("couldn't get user config dir: %w", err)) - } - gnoHome := filepath.Join(dir, "gno") - // XXX: added april 2023 as a transitory measure - remove after test4 - fixOldDefaultGnoHome(gnoHome) - return gnoHome -} diff --git a/tm2/pkg/crypto/keys/client/delete.go b/tm2/pkg/crypto/keys/client/delete.go index 0f216d3467c..69c185c3e75 100644 --- a/tm2/pkg/crypto/keys/client/delete.go +++ b/tm2/pkg/crypto/keys/client/delete.go @@ -9,16 +9,16 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto/keys" ) -type deleteCfg struct { - rootCfg *baseCfg +type DeleteCfg struct { + RootCfg *BaseCfg - yes bool - force bool + Yes bool + Force bool } -func newDeleteCmd(rootCfg *baseCfg) *commands.Command { - cfg := &deleteCfg{ - rootCfg: rootCfg, +func NewDeleteCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &DeleteCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -29,35 +29,35 @@ func newDeleteCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execDelete(cfg, args, commands.NewDefaultIO()) + return execDelete(cfg, args, io) }, ) } -func (c *deleteCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *DeleteCfg) RegisterFlags(fs *flag.FlagSet) { fs.BoolVar( - &c.yes, + &c.Yes, "yes", false, "skip confirmation prompt", ) fs.BoolVar( - &c.force, + &c.Force, "force", false, "remove key unconditionally", ) } -func execDelete(cfg *deleteCfg, args []string, io *commands.IO) error { +func execDelete(cfg *DeleteCfg, args []string, io commands.IO) error { if len(args) != 1 { return flag.ErrHelp } nameOrBech32 := args[0] - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.Home) + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.Home) if err != nil { return err } @@ -68,7 +68,7 @@ func execDelete(cfg *deleteCfg, args []string, io *commands.IO) error { } if info.GetType() == keys.TypeLedger || info.GetType() == keys.TypeOffline { - if !cfg.yes { + if !cfg.Yes { if err := confirmDeletion(io); err != nil { return err } @@ -83,11 +83,11 @@ func execDelete(cfg *deleteCfg, args []string, io *commands.IO) error { } // skip passphrase check if run with --force - skipPass := cfg.force + skipPass := cfg.Force var oldpass string if !skipPass { msg := "DANGER - enter password to permanently delete key:" - if oldpass, err = io.GetPassword(msg, cfg.rootCfg.InsecurePasswordStdin); err != nil { + if oldpass, err = io.GetPassword(msg, cfg.RootCfg.InsecurePasswordStdin); err != nil { return err } } @@ -101,7 +101,7 @@ func execDelete(cfg *deleteCfg, args []string, io *commands.IO) error { return nil } -func confirmDeletion(io *commands.IO) error { +func confirmDeletion(io commands.IO) error { answer, err := io.GetConfirmation("Key reference will be deleted. Continue?") if err != nil { return err diff --git a/tm2/pkg/crypto/keys/client/delete_test.go b/tm2/pkg/crypto/keys/client/delete_test.go index b3f83f1d9a2..a9724ac483b 100644 --- a/tm2/pkg/crypto/keys/client/delete_test.go +++ b/tm2/pkg/crypto/keys/client/delete_test.go @@ -23,8 +23,8 @@ func Test_execDelete(t *testing.T) { defer kbCleanUp() // initialize test options - cfg := &deleteCfg{ - rootCfg: &baseCfg{ + cfg := &DeleteCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ Home: kbHome, InsecurePasswordStdin: true, @@ -75,14 +75,14 @@ func Test_execDelete(t *testing.T) { } // Set config yes = true - cfg = &deleteCfg{ - rootCfg: &baseCfg{ + cfg = &DeleteCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ Home: kbHome, InsecurePasswordStdin: true, }, }, - yes: true, + Yes: true, } _, err = kb.GetByName(fakeKeyName2) diff --git a/tm2/pkg/crypto/keys/client/export.go b/tm2/pkg/crypto/keys/client/export.go index eda04a5c92f..58f533bd3be 100644 --- a/tm2/pkg/crypto/keys/client/export.go +++ b/tm2/pkg/crypto/keys/client/export.go @@ -2,6 +2,7 @@ package client import ( "context" + "errors" "flag" "fmt" "os" @@ -10,17 +11,17 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto/keys" ) -type exportCfg struct { - rootCfg *baseCfg +type ExportCfg struct { + RootCfg *BaseCfg - nameOrBech32 string - outputPath string - unsafe bool + NameOrBech32 string + OutputPath string + Unsafe bool } -func newExportCmd(rootCfg *baseCfg) *commands.Command { - cfg := &exportCfg{ - rootCfg: rootCfg, +func NewExportCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &ExportCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -31,41 +32,46 @@ func newExportCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execExport(cfg, commands.NewDefaultIO()) + return execExport(cfg, io) }, ) } -func (c *exportCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *ExportCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( - &c.nameOrBech32, + &c.NameOrBech32, "key", "", "Name or Bech32 address of the private key", ) fs.StringVar( - &c.outputPath, + &c.OutputPath, "output-path", "", "The desired output path for the armor file", ) fs.BoolVar( - &c.unsafe, + &c.Unsafe, "unsafe", false, "Export the private key armor as unencrypted", ) } -func execExport(cfg *exportCfg, io *commands.IO) error { +func execExport(cfg *ExportCfg, io commands.IO) error { + // check keyname + if cfg.NameOrBech32 == "" { + return errors.New("key to be exported shouldn't be empty") + } + // Create a new instance of the key-base - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.Home) + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.Home) if err != nil { return fmt.Errorf( "unable to create a key base from directory %s, %w", - cfg.rootCfg.Home, + cfg.RootCfg.Home, err, ) } @@ -73,7 +79,7 @@ func execExport(cfg *exportCfg, io *commands.IO) error { // Get the key-base decrypt password decryptPassword, err := io.GetPassword( "Enter a passphrase to decrypt your private key from disk:", - cfg.rootCfg.InsecurePasswordStdin, + cfg.RootCfg.InsecurePasswordStdin, ) if err != nil { return fmt.Errorf( @@ -87,10 +93,10 @@ func execExport(cfg *exportCfg, io *commands.IO) error { exportErr error ) - if cfg.unsafe { + if cfg.Unsafe { // Generate the unencrypted armor armor, exportErr = kb.ExportPrivKeyUnsafe( - cfg.nameOrBech32, + cfg.NameOrBech32, decryptPassword, ) } else { @@ -100,7 +106,7 @@ func execExport(cfg *exportCfg, io *commands.IO) error { "Enter a passphrase to encrypt your private key armor:", "Repeat the passphrase:", }, - cfg.rootCfg.InsecurePasswordStdin, + cfg.RootCfg.InsecurePasswordStdin, ) if err != nil { return fmt.Errorf( @@ -111,7 +117,7 @@ func execExport(cfg *exportCfg, io *commands.IO) error { // Generate the encrypted armor armor, exportErr = kb.ExportPrivKey( - cfg.nameOrBech32, + cfg.NameOrBech32, decryptPassword, encryptPassword, ) @@ -126,7 +132,7 @@ func execExport(cfg *exportCfg, io *commands.IO) error { // Write the armor to disk if err := os.WriteFile( - cfg.outputPath, + cfg.OutputPath, []byte(armor), 0o644, ); err != nil { @@ -136,7 +142,7 @@ func execExport(cfg *exportCfg, io *commands.IO) error { ) } - io.Printfln("Private key armor successfully outputted to %s", cfg.outputPath) + io.Printfln("Private key armor successfully outputted to %s", cfg.OutputPath) return nil } diff --git a/tm2/pkg/crypto/keys/client/export_test.go b/tm2/pkg/crypto/keys/client/export_test.go index a5f1ec8f48e..dfd7c74ce38 100644 --- a/tm2/pkg/crypto/keys/client/export_test.go +++ b/tm2/pkg/crypto/keys/client/export_test.go @@ -44,7 +44,7 @@ func addRandomKeyToKeybase( encryptPassword string, ) (keys.Info, error) { // Generate a random mnemonic - mnemonic, err := generateMnemonic(mnemonicEntropySize) + mnemonic, err := GenerateMnemonic(mnemonicEntropySize) if err != nil { return nil, fmt.Errorf( "unable to generate a mnemonic phrase, %w", @@ -64,11 +64,9 @@ func addRandomKeyToKeybase( } type testCmdKeyOptsBase struct { - kbHome string - keyName string - decryptPassword string - encryptPassword string - unsafe bool + kbHome string + keyName string + unsafe bool } type testExportKeyOpts struct { @@ -83,16 +81,16 @@ func exportKey( exportOpts testExportKeyOpts, input io.Reader, ) error { - cfg := &exportCfg{ - rootCfg: &baseCfg{ + cfg := &ExportCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ Home: exportOpts.kbHome, InsecurePasswordStdin: true, }, }, - nameOrBech32: exportOpts.keyName, - outputPath: exportOpts.outputPath, - unsafe: exportOpts.unsafe, + NameOrBech32: exportOpts.keyName, + OutputPath: exportOpts.outputPath, + Unsafe: exportOpts.unsafe, } cmdIO := commands.NewTestIO() @@ -204,3 +202,19 @@ func TestExport_ExportKey(t *testing.T) { }) } } + +func TestExport_ExportKeyWithEmptyName(t *testing.T) { + // Generate a temporary key-base directory + _, kbHome := newTestKeybase(t) + err := exportKey( + testExportKeyOpts{ + testCmdKeyOptsBase: testCmdKeyOptsBase{ + kbHome: kbHome, + keyName: "", + }, + }, + nil, + ) + assert.Error(t, err) + assert.EqualError(t, err, "key to be exported shouldn't be empty") +} diff --git a/tm2/pkg/crypto/keys/client/generate.go b/tm2/pkg/crypto/keys/client/generate.go index b721e6704ce..bae3b6b42a3 100644 --- a/tm2/pkg/crypto/keys/client/generate.go +++ b/tm2/pkg/crypto/keys/client/generate.go @@ -10,15 +10,15 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto/bip39" ) -type generateCfg struct { - rootCfg *baseCfg +type GenerateCfg struct { + RootCfg *BaseCfg - customEntropy bool + CustomEntropy bool } -func newGenerateCmd(rootCfg *baseCfg) *commands.Command { - cfg := &generateCfg{ - rootCfg: rootCfg, +func NewGenerateCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &GenerateCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -29,22 +29,22 @@ func newGenerateCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execGenerate(cfg, args, commands.NewDefaultIO()) + return execGenerate(cfg, args, io) }, ) } -func (c *generateCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *GenerateCfg) RegisterFlags(fs *flag.FlagSet) { fs.BoolVar( - &c.customEntropy, + &c.CustomEntropy, "entropy", false, "supply custom entropy", ) } -func execGenerate(cfg *generateCfg, args []string, io *commands.IO) error { - customEntropy := cfg.customEntropy +func execGenerate(cfg *GenerateCfg, args []string, io commands.IO) error { + customEntropy := cfg.CustomEntropy if len(args) != 0 { return flag.ErrHelp diff --git a/tm2/pkg/crypto/keys/client/generate_test.go b/tm2/pkg/crypto/keys/client/generate_test.go index 516912046b6..25eca1ed628 100644 --- a/tm2/pkg/crypto/keys/client/generate_test.go +++ b/tm2/pkg/crypto/keys/client/generate_test.go @@ -11,8 +11,8 @@ import ( func Test_execGenerateNormal(t *testing.T) { t.Parallel() - cfg := &generateCfg{ - customEntropy: false, + cfg := &GenerateCfg{ + CustomEntropy: false, } err := execGenerate(cfg, []string{}, commands.NewTestIO()) @@ -22,8 +22,8 @@ func Test_execGenerateNormal(t *testing.T) { func Test_execGenerateUser(t *testing.T) { t.Parallel() - cfg := &generateCfg{ - customEntropy: true, + cfg := &GenerateCfg{ + CustomEntropy: true, } io := commands.NewTestIO() diff --git a/tm2/pkg/crypto/keys/client/helper.go b/tm2/pkg/crypto/keys/client/helper.go index 42a936910f7..525ad9071f8 100644 --- a/tm2/pkg/crypto/keys/client/helper.go +++ b/tm2/pkg/crypto/keys/client/helper.go @@ -2,9 +2,9 @@ package client import "github.com/gnolang/gno/tm2/pkg/crypto/bip39" -// generateMnemonic generates a new BIP39 mnemonic using the +// GenerateMnemonic generates a new BIP39 mnemonic using the // provided entropy size -func generateMnemonic(entropySize int) (string, error) { +func GenerateMnemonic(entropySize int) (string, error) { // Generate the entropy seed entropySeed, err := bip39.NewEntropy(entropySize) if err != nil { diff --git a/tm2/pkg/crypto/keys/client/import.go b/tm2/pkg/crypto/keys/client/import.go index 5e0eeecabb5..cace9bc0964 100644 --- a/tm2/pkg/crypto/keys/client/import.go +++ b/tm2/pkg/crypto/keys/client/import.go @@ -2,6 +2,7 @@ package client import ( "context" + "errors" "flag" "fmt" "os" @@ -10,17 +11,17 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto/keys" ) -type importCfg struct { - rootCfg *baseCfg +type ImportCfg struct { + RootCfg *BaseCfg - keyName string - armorPath string - unsafe bool + KeyName string + ArmorPath string + Unsafe bool } -func newImportCmd(rootCfg *baseCfg) *commands.Command { - cfg := &importCfg{ - rootCfg: rootCfg, +func NewImportCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &ImportCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -31,51 +32,56 @@ func newImportCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, _ []string) error { - return execImport(cfg, commands.NewDefaultIO()) + return execImport(cfg, io) }, ) } -func (c *importCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *ImportCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( - &c.keyName, + &c.KeyName, "name", "", "The name of the private key", ) fs.StringVar( - &c.armorPath, + &c.ArmorPath, "armor-path", "", "The path to the encrypted armor file", ) fs.BoolVar( - &c.unsafe, + &c.Unsafe, "unsafe", false, "Import the private key armor as unencrypted", ) } -func execImport(cfg *importCfg, io *commands.IO) error { +func execImport(cfg *ImportCfg, io commands.IO) error { + // check keyname + if cfg.KeyName == "" { + return errors.New("name shouldn't be empty") + } + // Create a new instance of the key-base - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.Home) + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.Home) if err != nil { return fmt.Errorf( "unable to create a key base from directory %s, %w", - cfg.rootCfg.Home, + cfg.RootCfg.Home, err, ) } // Read the raw encrypted armor - armor, err := os.ReadFile(cfg.armorPath) + armor, err := os.ReadFile(cfg.ArmorPath) if err != nil { return fmt.Errorf( "unable to read armor from path %s, %w", - cfg.armorPath, + cfg.ArmorPath, err, ) } @@ -85,11 +91,11 @@ func execImport(cfg *importCfg, io *commands.IO) error { encryptPassword string ) - if !cfg.unsafe { + if !cfg.Unsafe { // Get the armor decrypt password decryptPassword, err = io.GetPassword( - "Enter a passphrase to decrypt your private key armor:", - cfg.rootCfg.InsecurePasswordStdin, + "Enter the passphrase to decrypt your private key armor:", + cfg.RootCfg.InsecurePasswordStdin, ) if err != nil { return fmt.Errorf( @@ -105,7 +111,7 @@ func execImport(cfg *importCfg, io *commands.IO) error { "Enter a passphrase to encrypt your private key:", "Repeat the passphrase:", }, - cfg.rootCfg.InsecurePasswordStdin, + cfg.RootCfg.InsecurePasswordStdin, ) if err != nil { return fmt.Errorf( @@ -114,10 +120,10 @@ func execImport(cfg *importCfg, io *commands.IO) error { ) } - if cfg.unsafe { + if cfg.Unsafe { // Import the unencrypted private key if err := kb.ImportPrivKeyUnsafe( - cfg.keyName, + cfg.KeyName, string(armor), encryptPassword, ); err != nil { @@ -129,7 +135,7 @@ func execImport(cfg *importCfg, io *commands.IO) error { } else { // Import the encrypted private key if err := kb.ImportPrivKey( - cfg.keyName, + cfg.KeyName, string(armor), decryptPassword, encryptPassword, @@ -141,7 +147,7 @@ func execImport(cfg *importCfg, io *commands.IO) error { } } - io.Printfln("Successfully imported private key %s", cfg.keyName) + io.Printfln("Successfully imported private key %s", cfg.KeyName) return nil } diff --git a/tm2/pkg/crypto/keys/client/import_test.go b/tm2/pkg/crypto/keys/client/import_test.go index 7bc00e06ec9..9788f3d700c 100644 --- a/tm2/pkg/crypto/keys/client/import_test.go +++ b/tm2/pkg/crypto/keys/client/import_test.go @@ -22,16 +22,16 @@ func importKey( importOpts testImportKeyOpts, input io.Reader, ) error { - cfg := &importCfg{ - rootCfg: &baseCfg{ + cfg := &ImportCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ Home: importOpts.kbHome, InsecurePasswordStdin: true, }, }, - keyName: importOpts.keyName, - armorPath: importOpts.armorPath, - unsafe: importOpts.unsafe, + KeyName: importOpts.keyName, + ArmorPath: importOpts.armorPath, + Unsafe: importOpts.unsafe, } cmdIO := commands.NewTestIO() @@ -152,3 +152,19 @@ func TestImport_ImportKey(t *testing.T) { }) } } + +func TestImport_ImportKeyWithEmptyName(t *testing.T) { + // Generate a temporary key-base directory + _, kbHome := newTestKeybase(t) + err := importKey( + testImportKeyOpts{ + testCmdKeyOptsBase: testCmdKeyOptsBase{ + kbHome: kbHome, + keyName: "", + }, + }, + nil, + ) + assert.Error(t, err) + assert.EqualError(t, err, "name shouldn't be empty") +} diff --git a/tm2/pkg/crypto/keys/client/list.go b/tm2/pkg/crypto/keys/client/list.go index 50be35cef43..5a2c0dfb28a 100644 --- a/tm2/pkg/crypto/keys/client/list.go +++ b/tm2/pkg/crypto/keys/client/list.go @@ -8,7 +8,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto/keys" ) -func newListCmd(rootCfg *baseCfg) *commands.Command { +func NewListCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { return commands.NewCommand( commands.Metadata{ Name: "list", @@ -17,12 +17,12 @@ func newListCmd(rootCfg *baseCfg) *commands.Command { }, nil, func(_ context.Context, args []string) error { - return execList(rootCfg, args, commands.NewDefaultIO()) + return execList(rootCfg, args, io) }, ) } -func execList(cfg *baseCfg, args []string, io *commands.IO) error { +func execList(cfg *BaseCfg, args []string, io commands.IO) error { if len(args) != 0 { return flag.ErrHelp } @@ -40,7 +40,7 @@ func execList(cfg *baseCfg, args []string, io *commands.IO) error { return err } -func printInfos(infos []keys.Info, io *commands.IO) { +func printInfos(infos []keys.Info, io commands.IO) { for i, info := range infos { keyname := info.GetName() keytype := info.GetType() diff --git a/tm2/pkg/crypto/keys/client/list_test.go b/tm2/pkg/crypto/keys/client/list_test.go index ee0a147f3d3..69e96f1cfb1 100644 --- a/tm2/pkg/crypto/keys/client/list_test.go +++ b/tm2/pkg/crypto/keys/client/list_test.go @@ -36,7 +36,7 @@ func Test_execList(t *testing.T) { for _, tt := range testData { t.Run(tt.name, func(t *testing.T) { // Set current home - cfg := &baseCfg{ + cfg := &BaseCfg{ BaseOptions: BaseOptions{ Home: tt.kbDir, }, diff --git a/tm2/pkg/crypto/keys/client/maketx.go b/tm2/pkg/crypto/keys/client/maketx.go index cbcc6def0de..c78c5de25c8 100644 --- a/tm2/pkg/crypto/keys/client/maketx.go +++ b/tm2/pkg/crypto/keys/client/maketx.go @@ -2,24 +2,30 @@ package client import ( "flag" + "fmt" + "github.com/gnolang/gno/tm2/pkg/amino" + types "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" "github.com/gnolang/gno/tm2/pkg/commands" + "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/errors" + "github.com/gnolang/gno/tm2/pkg/std" ) -type makeTxCfg struct { - rootCfg *baseCfg +type MakeTxCfg struct { + RootCfg *BaseCfg - gasWanted int64 - gasFee string - memo string + GasWanted int64 + GasFee string + Memo string - broadcast bool - chainID string + Broadcast bool + ChainID string } -func newMakeTxCmd(rootCfg *baseCfg) *commands.Command { - cfg := &makeTxCfg{ - rootCfg: rootCfg, +func NewMakeTxCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &MakeTxCfg{ + RootCfg: rootCfg, } cmd := commands.NewCommand( @@ -33,47 +39,148 @@ func newMakeTxCmd(rootCfg *baseCfg) *commands.Command { ) cmd.AddSubCommands( - newAddPkgCmd(cfg), - newSendCmd(cfg), - newCallCmd(cfg), + NewMakeSendCmd(cfg, io), ) return cmd } -func (c *makeTxCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *MakeTxCfg) RegisterFlags(fs *flag.FlagSet) { fs.Int64Var( - &c.gasWanted, + &c.GasWanted, "gas-wanted", 0, "gas requested for tx", ) fs.StringVar( - &c.gasFee, + &c.GasFee, "gas-fee", "", "gas payment fee", ) fs.StringVar( - &c.memo, + &c.Memo, "memo", "", "any descriptive text", ) fs.BoolVar( - &c.broadcast, + &c.Broadcast, "broadcast", false, "sign and broadcast", ) fs.StringVar( - &c.chainID, + &c.ChainID, "chainid", "dev", "chainid to sign for (only useful if --broadcast)", ) } + +func SignAndBroadcastHandler( + cfg *MakeTxCfg, + nameOrBech32 string, + tx std.Tx, + pass string, +) (*types.ResultBroadcastTxCommit, error) { + baseopts := cfg.RootCfg + txopts := cfg + + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.Home) + if err != nil { + return nil, err + } + + info, err := kb.GetByNameOrAddress(nameOrBech32) + if err != nil { + return nil, err + } + accountAddr := info.GetAddress() + + qopts := &QueryCfg{ + RootCfg: baseopts, + Path: fmt.Sprintf("auth/accounts/%s", accountAddr), + } + qres, err := QueryHandler(qopts) + if err != nil { + return nil, errors.Wrap(err, "query account") + } + var qret struct{ BaseAccount std.BaseAccount } + err = amino.UnmarshalJSON(qres.Response.Data, &qret) + if err != nil { + return nil, err + } + + // sign tx + accountNumber := qret.BaseAccount.AccountNumber + sequence := qret.BaseAccount.Sequence + sopts := &SignCfg{ + Pass: pass, + RootCfg: baseopts, + Sequence: sequence, + AccountNumber: accountNumber, + ChainID: txopts.ChainID, + NameOrBech32: nameOrBech32, + TxJSON: amino.MustMarshalJSON(tx), + } + + signedTx, err := SignHandler(sopts) + if err != nil { + return nil, errors.Wrap(err, "sign tx") + } + + // broadcast signed tx + bopts := &BroadcastCfg{ + RootCfg: baseopts, + tx: signedTx, + } + + return BroadcastHandler(bopts) +} + +func ExecSignAndBroadcast( + cfg *MakeTxCfg, + args []string, + tx std.Tx, + io commands.IO, +) error { + baseopts := cfg.RootCfg + + // query account + nameOrBech32 := args[0] + + var err error + var pass string + if baseopts.Quiet { + pass, err = io.GetPassword("", baseopts.InsecurePasswordStdin) + } else { + pass, err = io.GetPassword("Enter password.", baseopts.InsecurePasswordStdin) + } + + if err != nil { + return err + } + + bres, err := SignAndBroadcastHandler(cfg, nameOrBech32, tx, pass) + if err != nil { + return errors.Wrap(err, "broadcast tx") + } + if bres.CheckTx.IsErr() { + return errors.Wrap(bres.CheckTx.Error, "check transaction failed: log:%s", bres.CheckTx.Log) + } + if bres.DeliverTx.IsErr() { + return errors.Wrap(bres.DeliverTx.Error, "deliver transaction failed: log:%s", bres.DeliverTx.Log) + } + + io.Println(string(bres.DeliverTx.Data)) + io.Println("OK!") + io.Println("GAS WANTED:", bres.DeliverTx.GasWanted) + io.Println("GAS USED: ", bres.DeliverTx.GasUsed) + + return nil +} diff --git a/tm2/pkg/crypto/keys/client/query.go b/tm2/pkg/crypto/keys/client/query.go index 58923f8787c..4c37a125749 100644 --- a/tm2/pkg/crypto/keys/client/query.go +++ b/tm2/pkg/crypto/keys/client/query.go @@ -3,7 +3,6 @@ package client import ( "context" "flag" - "fmt" "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" @@ -11,20 +10,19 @@ import ( "github.com/gnolang/gno/tm2/pkg/errors" ) -type queryCfg struct { - rootCfg *baseCfg +type QueryCfg struct { + RootCfg *BaseCfg - data string - height int64 - prove bool + Data string + Height int64 + Prove bool - // internal - path string + Path string } -func newQueryCmd(rootCfg *baseCfg) *commands.Command { - cfg := &queryCfg{ - rootCfg: rootCfg, +func NewQueryCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &QueryCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -35,75 +33,76 @@ func newQueryCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execQuery(cfg, args) + return execQuery(cfg, args, io) }, ) } -func (c *queryCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *QueryCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( - &c.data, + &c.Data, "data", "", "query data bytes", ) fs.Int64Var( - &c.height, + &c.Height, "height", 0, "query height (not yet supported)", ) fs.BoolVar( - &c.prove, + &c.Prove, "prove", false, "prove query result (not yet supported)", ) } -func execQuery(cfg *queryCfg, args []string) error { +func execQuery(cfg *QueryCfg, args []string, io commands.IO) error { if len(args) != 1 { return flag.ErrHelp } - cfg.path = args[0] + cfg.Path = args[0] - qres, err := queryHandler(cfg) + qres, err := QueryHandler(cfg) if err != nil { return err } if qres.Response.Error != nil { - fmt.Printf("Log: %s\n", + io.Printf("Log: %s\n", qres.Response.Log) return qres.Response.Error } + resdata := qres.Response.Data // XXX in general, how do we know what to show? // proof := qres.Response.Proof height := qres.Response.Height - fmt.Printf("height: %d\ndata: %s\n", + io.Printf("height: %d\ndata: %s\n", height, string(resdata)) return nil } -func queryHandler(cfg *queryCfg) (*ctypes.ResultABCIQuery, error) { - remote := cfg.rootCfg.Remote +func QueryHandler(cfg *QueryCfg) (*ctypes.ResultABCIQuery, error) { + remote := cfg.RootCfg.Remote if remote == "" || remote == "y" { return nil, errors.New("missing remote url") } - data := []byte(cfg.data) + data := []byte(cfg.Data) opts2 := client.ABCIQueryOptions{ // Height: height, XXX // Prove: false, XXX } cli := client.NewHTTP(remote, "/websocket") qres, err := cli.ABCIQueryWithOptions( - cfg.path, data, opts2) + cfg.Path, data, opts2) if err != nil { return nil, errors.Wrap(err, "querying") } diff --git a/tm2/pkg/crypto/keys/client/root.go b/tm2/pkg/crypto/keys/client/root.go index ad8983f2bb9..bfccdc26bab 100644 --- a/tm2/pkg/crypto/keys/client/root.go +++ b/tm2/pkg/crypto/keys/client/root.go @@ -14,12 +14,18 @@ const ( mnemonicEntropySize = 256 ) -type baseCfg struct { +type BaseCfg struct { BaseOptions } -func NewRootCmd() *commands.Command { - cfg := &baseCfg{} +func NewRootCmd(io commands.IO) *commands.Command { + return NewRootCmdWithBaseConfig(io, DefaultBaseOptions) +} + +func NewRootCmdWithBaseConfig(io commands.IO, base BaseOptions) *commands.Command { + cfg := &BaseCfg{ + BaseOptions: base, + } cmd := commands.NewCommand( commands.Metadata{ @@ -35,56 +41,56 @@ func NewRootCmd() *commands.Command { ) cmd.AddSubCommands( - newAddCmd(cfg), - newDeleteCmd(cfg), - newGenerateCmd(cfg), - newExportCmd(cfg), - newImportCmd(cfg), - newListCmd(cfg), - newSignCmd(cfg), - newVerifyCmd(cfg), - newQueryCmd(cfg), - newBroadcastCmd(cfg), - newMakeTxCmd(cfg), + NewAddCmd(cfg, io), + NewDeleteCmd(cfg, io), + NewGenerateCmd(cfg, io), + NewExportCmd(cfg, io), + NewImportCmd(cfg, io), + NewListCmd(cfg, io), + NewSignCmd(cfg, io), + NewVerifyCmd(cfg, io), + NewQueryCmd(cfg, io), + NewBroadcastCmd(cfg, io), + NewMakeTxCmd(cfg, io), ) return cmd } -func (c *baseCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *BaseCfg) RegisterFlags(fs *flag.FlagSet) { // Base options fs.StringVar( &c.Home, "home", - DefaultBaseOptions.Home, + c.Home, "home directory", ) fs.StringVar( &c.Remote, "remote", - DefaultBaseOptions.Remote, + c.Remote, "remote node URL", ) fs.BoolVar( &c.Quiet, "quiet", - DefaultBaseOptions.Quiet, + c.Quiet, "suppress output during execution", ) fs.BoolVar( &c.InsecurePasswordStdin, "insecure-password-stdin", - DefaultBaseOptions.Quiet, + c.Quiet, "WARNING! take password from stdin", ) fs.StringVar( &c.Config, "config", - DefaultBaseOptions.Config, + c.Config, "config file (optional)", ) } diff --git a/tm2/pkg/crypto/keys/client/send.go b/tm2/pkg/crypto/keys/client/send.go index 8f82778b1e3..a253f9d9f4e 100644 --- a/tm2/pkg/crypto/keys/client/send.go +++ b/tm2/pkg/crypto/keys/client/send.go @@ -14,16 +14,16 @@ import ( "github.com/gnolang/gno/tm2/pkg/std" ) -type sendCfg struct { - rootCfg *makeTxCfg +type MakeSendCfg struct { + RootCfg *MakeTxCfg - send string - to string + Send string + To string } -func newSendCmd(rootCfg *makeTxCfg) *commands.Command { - cfg := &sendCfg{ - rootCfg: rootCfg, +func NewMakeSendCmd(rootCfg *MakeTxCfg, io commands.IO) *commands.Command { + cfg := &MakeSendCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -34,48 +34,48 @@ func newSendCmd(rootCfg *makeTxCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execSend(cfg, args, commands.NewDefaultIO()) + return execMakeSend(cfg, args, io) }, ) } -func (c *sendCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *MakeSendCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( - &c.send, + &c.Send, "send", "", "send amount", ) fs.StringVar( - &c.to, + &c.To, "to", "", "destination address", ) } -func execSend(cfg *sendCfg, args []string, io *commands.IO) error { +func execMakeSend(cfg *MakeSendCfg, args []string, io commands.IO) error { if len(args) != 1 { return flag.ErrHelp } - if cfg.rootCfg.gasWanted == 0 { + if cfg.RootCfg.GasWanted == 0 { return errors.New("gas-wanted not specified") } - if cfg.rootCfg.gasFee == "" { + if cfg.RootCfg.GasFee == "" { return errors.New("gas-fee not specified") } - if cfg.send == "" { + if cfg.Send == "" { return errors.New("send (amount) must be specified") } - if cfg.to == "" { + if cfg.To == "" { return errors.New("to (destination address) must be specified") } // read account pubkey. nameOrBech32 := args[0] - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.rootCfg.Home) + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.RootCfg.Home) if err != nil { return err } @@ -87,20 +87,20 @@ func execSend(cfg *sendCfg, args []string, io *commands.IO) error { // info.GetPubKey() // Parse to address. - toAddr, err := crypto.AddressFromBech32(cfg.to) + toAddr, err := crypto.AddressFromBech32(cfg.To) if err != nil { return err } // Parse send amount. - send, err := std.ParseCoins(cfg.send) + send, err := std.ParseCoins(cfg.Send) if err != nil { return errors.Wrap(err, "parsing send coins") } // parse gas wanted & fee. - gaswanted := cfg.rootCfg.gasWanted - gasfee, err := std.ParseCoin(cfg.rootCfg.gasFee) + gaswanted := cfg.RootCfg.GasWanted + gasfee, err := std.ParseCoin(cfg.RootCfg.GasFee) if err != nil { return errors.Wrap(err, "parsing gas fee coin") } @@ -115,11 +115,11 @@ func execSend(cfg *sendCfg, args []string, io *commands.IO) error { Msgs: []std.Msg{msg}, Fee: std.NewFee(gaswanted, gasfee), Signatures: nil, - Memo: cfg.rootCfg.memo, + Memo: cfg.RootCfg.Memo, } - if cfg.rootCfg.broadcast { - err := signAndBroadcast(cfg.rootCfg, args, tx, io) + if cfg.RootCfg.Broadcast { + err := ExecSignAndBroadcast(cfg.RootCfg, args, tx, io) if err != nil { return err } diff --git a/tm2/pkg/crypto/keys/client/sign.go b/tm2/pkg/crypto/keys/client/sign.go index bfc39647141..19022eaba81 100644 --- a/tm2/pkg/crypto/keys/client/sign.go +++ b/tm2/pkg/crypto/keys/client/sign.go @@ -2,7 +2,6 @@ package client import ( "context" - "errors" "flag" "fmt" "os" @@ -10,27 +9,26 @@ import ( "github.com/gnolang/gno/tm2/pkg/amino" "github.com/gnolang/gno/tm2/pkg/commands" "github.com/gnolang/gno/tm2/pkg/crypto/keys" + "github.com/gnolang/gno/tm2/pkg/errors" "github.com/gnolang/gno/tm2/pkg/std" ) -type signCfg struct { - rootCfg *baseCfg - - txPath string - chainID string - accountNumber uint64 - sequence uint64 - showSignBytes bool - - // internal flags, when called programmatically - nameOrBech32 string - txJSON []byte - pass string +type SignCfg struct { + RootCfg *BaseCfg + + TxPath string + ChainID string + AccountNumber uint64 + Sequence uint64 + ShowSignBytes bool + NameOrBech32 string + TxJSON []byte + Pass string } -func newSignCmd(rootCfg *baseCfg) *commands.Command { - cfg := &signCfg{ - rootCfg: rootCfg, +func NewSignCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &SignCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -41,59 +39,59 @@ func newSignCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execSign(cfg, args, commands.NewDefaultIO()) + return execSign(cfg, args, io) }, ) } -func (c *signCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *SignCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( - &c.txPath, + &c.TxPath, "txpath", "-", "path to file of tx to sign", ) fs.StringVar( - &c.chainID, + &c.ChainID, "chainid", "dev", "chainid to sign for", ) fs.Uint64Var( - &c.accountNumber, + &c.AccountNumber, "number", 0, "account number to sign with (required)", ) fs.Uint64Var( - &c.sequence, + &c.Sequence, "sequence", 0, "sequence to sign with (required)", ) fs.BoolVar( - &c.showSignBytes, + &c.ShowSignBytes, "show-signbytes", false, "show sign bytes and quit", ) } -func execSign(cfg *signCfg, args []string, io *commands.IO) error { +func execSign(cfg *SignCfg, args []string, io commands.IO) error { var err error if len(args) != 1 { return flag.ErrHelp } - cfg.nameOrBech32 = args[0] + cfg.NameOrBech32 = args[0] // read tx to sign - txpath := cfg.txPath + txpath := cfg.TxPath if txpath == "-" { // from stdin. txjsonstr, err := io.GetString( "Enter tx to sign, terminated by a newline.", @@ -101,23 +99,23 @@ func execSign(cfg *signCfg, args []string, io *commands.IO) error { if err != nil { return err } - cfg.txJSON = []byte(txjsonstr) + cfg.TxJSON = []byte(txjsonstr) } else { // from file - cfg.txJSON, err = os.ReadFile(txpath) + cfg.TxJSON, err = os.ReadFile(txpath) if err != nil { return err } } - if cfg.rootCfg.Quiet { - cfg.pass, err = io.GetPassword( + if cfg.RootCfg.Quiet { + cfg.Pass, err = io.GetPassword( "", - cfg.rootCfg.InsecurePasswordStdin, + cfg.RootCfg.InsecurePasswordStdin, ) } else { - cfg.pass, err = io.GetPassword( + cfg.Pass, err = io.GetPassword( "Enter password.", - cfg.rootCfg.InsecurePasswordStdin, + cfg.RootCfg.InsecurePasswordStdin, ) } if err != nil { @@ -138,20 +136,20 @@ func execSign(cfg *signCfg, args []string, io *commands.IO) error { return nil } -func SignHandler(cfg *signCfg) (*std.Tx, error) { +func SignHandler(cfg *SignCfg) (*std.Tx, error) { var err error var tx std.Tx - if cfg.txJSON == nil { + if cfg.TxJSON == nil { return nil, errors.New("invalid tx content") } - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.Home) + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.Home) if err != nil { return nil, err } - err = amino.UnmarshalJSON(cfg.txJSON, &tx) + err = amino.UnmarshalJSON(cfg.TxJSON, &tx) if err != nil { return nil, err } @@ -174,16 +172,16 @@ func SignHandler(cfg *signCfg) (*std.Tx, error) { } // derive sign doc bytes. - chainID := cfg.chainID - accountNumber := cfg.accountNumber - sequence := cfg.sequence + chainID := cfg.ChainID + accountNumber := cfg.AccountNumber + sequence := cfg.Sequence signbz := tx.GetSignBytes(chainID, accountNumber, sequence) - if cfg.showSignBytes { + if cfg.ShowSignBytes { fmt.Printf("sign bytes: %X\n", signbz) return nil, nil } - sig, pub, err := kb.Sign(cfg.nameOrBech32, cfg.pass, signbz) + sig, pub, err := kb.Sign(cfg.NameOrBech32, cfg.Pass, signbz) if err != nil { return nil, err } @@ -201,7 +199,7 @@ func SignHandler(cfg *signCfg) (*std.Tx, error) { } if !found { return nil, errors.New( - fmt.Sprintf("addr %v (%s) not in signer set", addr, cfg.nameOrBech32), + fmt.Sprintf("addr %v (%s) not in signer set", addr, cfg.NameOrBech32), ) } diff --git a/tm2/pkg/crypto/keys/client/sign_test.go b/tm2/pkg/crypto/keys/client/sign_test.go index 6e9b1da5946..0d73d247637 100644 --- a/tm2/pkg/crypto/keys/client/sign_test.go +++ b/tm2/pkg/crypto/keys/client/sign_test.go @@ -23,17 +23,17 @@ func Test_execSign(t *testing.T) { defer kbCleanUp() // initialize test options - cfg := &signCfg{ - rootCfg: &baseCfg{ + cfg := &SignCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ Home: kbHome, InsecurePasswordStdin: true, }, }, - txPath: "-", // stdin - chainID: "dev", - accountNumber: 0, - sequence: 0, + TxPath: "-", // stdin + ChainID: "dev", + AccountNumber: 0, + Sequence: 0, } fakeKeyName1 := "signApp_Key1" @@ -43,7 +43,7 @@ func Test_execSign(t *testing.T) { io := commands.NewTestIO() // add test account to keybase. - kb, err := keys.NewKeyBaseFromDir(cfg.rootCfg.Home) + kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.Home) assert.NoError(t, err) acc, err := kb.CreateAccount(fakeKeyName1, testMnemonic, "", encPassword, 0, 0) addr := acc.GetAddress() diff --git a/tm2/pkg/crypto/keys/client/verify.go b/tm2/pkg/crypto/keys/client/verify.go index 3dcc5f35dee..5a52ba76a3c 100644 --- a/tm2/pkg/crypto/keys/client/verify.go +++ b/tm2/pkg/crypto/keys/client/verify.go @@ -10,15 +10,15 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto/keys" ) -type verifyCfg struct { - rootCfg *baseCfg +type VerifyCfg struct { + RootCfg *BaseCfg - docPath string + DocPath string } -func newVerifyCmd(rootCfg *baseCfg) *commands.Command { - cfg := &verifyCfg{ - rootCfg: rootCfg, +func NewVerifyCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { + cfg := &VerifyCfg{ + RootCfg: rootCfg, } return commands.NewCommand( @@ -29,21 +29,21 @@ func newVerifyCmd(rootCfg *baseCfg) *commands.Command { }, cfg, func(_ context.Context, args []string) error { - return execVerify(cfg, args, commands.NewDefaultIO()) + return execVerify(cfg, args, io) }, ) } -func (c *verifyCfg) RegisterFlags(fs *flag.FlagSet) { +func (c *VerifyCfg) RegisterFlags(fs *flag.FlagSet) { fs.StringVar( - &c.docPath, + &c.DocPath, "docpath", "", "path of document file to verify", ) } -func execVerify(cfg *verifyCfg, args []string, io *commands.IO) error { +func execVerify(cfg *VerifyCfg, args []string, io commands.IO) error { var ( kb keys.Keybase err error @@ -58,8 +58,8 @@ func execVerify(cfg *verifyCfg, args []string, io *commands.IO) error { if err != nil { return err } - docpath := cfg.docPath - kb, err = keys.NewKeyBaseFromDir(cfg.rootCfg.Home) + docpath := cfg.DocPath + kb, err = keys.NewKeyBaseFromDir(cfg.RootCfg.Home) if err != nil { return err } diff --git a/tm2/pkg/crypto/keys/client/verify_test.go b/tm2/pkg/crypto/keys/client/verify_test.go index 206c14682fd..796f6344852 100644 --- a/tm2/pkg/crypto/keys/client/verify_test.go +++ b/tm2/pkg/crypto/keys/client/verify_test.go @@ -21,14 +21,14 @@ func Test_execVerify(t *testing.T) { defer kbCleanUp() // initialize test options - cfg := &verifyCfg{ - rootCfg: &baseCfg{ + cfg := &VerifyCfg{ + RootCfg: &BaseCfg{ BaseOptions: BaseOptions{ Home: kbHome, InsecurePasswordStdin: true, }, }, - docPath: "", + DocPath: "", } io := commands.NewTestIO() diff --git a/tm2/pkg/crypto/keys/keybase.go b/tm2/pkg/crypto/keys/keybase.go index 16b3631d188..5b69f36a22d 100644 --- a/tm2/pkg/crypto/keys/keybase.go +++ b/tm2/pkg/crypto/keys/keybase.go @@ -42,16 +42,11 @@ const ( French // Italian is currently not supported. Italian - addressSuffix = "address" - infoSuffix = "info" ) const ( - // used for deriving seed from mnemonic - DefaultBIP39Passphrase = "" - - // bits of entropy to draw when creating a mnemonic - defaultEntropySize = 256 + addressSuffix = "address" + infoSuffix = "info" ) var ( @@ -168,14 +163,32 @@ func (kb dbKeybase) List() ([]Info, error) { return res, nil } +// HasByNameOrAddress checks if a key with the name or bech32 string address is in the keybase. +func (kb dbKeybase) HasByNameOrAddress(nameOrBech32 string) (bool, error) { + address, err := crypto.AddressFromBech32(nameOrBech32) + if err != nil { + return kb.HasByName(nameOrBech32) + } + return kb.HasByAddress(address) +} + +// HasByName checks if a key with the name is in the keybase. +func (kb dbKeybase) HasByName(name string) (bool, error) { + return kb.db.Has(infoKey(name)), nil +} + +// HasByAddress checks if a key with the address is in the keybase. +func (kb dbKeybase) HasByAddress(address crypto.Address) (bool, error) { + return kb.db.Has(addrKey(address)), nil +} + // Get returns the public information about one key. func (kb dbKeybase) GetByNameOrAddress(nameOrBech32 string) (Info, error) { addr, err := crypto.AddressFromBech32(nameOrBech32) if err != nil { return kb.GetByName(nameOrBech32) - } else { - return kb.GetByAddress(addr) } + return kb.GetByAddress(addr) } func (kb dbKeybase) GetByName(name string) (Info, error) { @@ -189,7 +202,7 @@ func (kb dbKeybase) GetByName(name string) (Info, error) { func (kb dbKeybase) GetByAddress(address crypto.Address) (Info, error) { ik := kb.db.Get(addrKey(address)) if len(ik) == 0 { - return nil, fmt.Errorf("key with address %s not found", address) + return nil, keyerror.NewErrKeyNotFound(fmt.Sprintf("key with address %s not found", address)) } bs := kb.db.Get(ik) return readInfo(bs) diff --git a/tm2/pkg/crypto/keys/keybase_test.go b/tm2/pkg/crypto/keys/keybase_test.go index 987a271881b..0c43dbd8dc5 100644 --- a/tm2/pkg/crypto/keys/keybase_test.go +++ b/tm2/pkg/crypto/keys/keybase_test.go @@ -9,9 +9,12 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" + "github.com/gnolang/gno/tm2/pkg/crypto/keys/keyerror" ) func TestCreateAccountInvalidMnemonic(t *testing.T) { + t.Parallel() + kb := NewInMemory() _, err := kb.CreateAccount( "some_account", @@ -22,6 +25,8 @@ func TestCreateAccountInvalidMnemonic(t *testing.T) { } func TestCreateLedgerUnsupportedAlgo(t *testing.T) { + t.Parallel() + kb := NewInMemory() _, err := kb.CreateLedger("some_account", Ed25519, "cosmos", 0, 1) assert.Error(t, err) @@ -29,6 +34,8 @@ func TestCreateLedgerUnsupportedAlgo(t *testing.T) { } func TestCreateLedger(t *testing.T) { + t.Parallel() + kb := NewInMemory() // test_cover and test_unit will result in different answers @@ -38,7 +45,8 @@ func TestCreateLedger(t *testing.T) { ledger, err := kb.CreateLedger("some_account", Secp256k1, "cosmos", 3, 1) if err != nil { assert.Error(t, err) - assert.Equal(t, "no Ledger discovery function defined", err.Error()) + assert.Contains(t, err.Error(), "LedgerHID device (idx 0) not found.") + assert.Nil(t, ledger) t.Skip("ledger nano S: support for ledger devices is not available in this executable") return @@ -65,6 +73,8 @@ func TestCreateLedger(t *testing.T) { // TestKeyManagement makes sure we can manipulate these keys well func TestKeyManagement(t *testing.T) { + t.Parallel() + // make the storage with reasonable defaults cstore := NewInMemory() @@ -80,8 +90,9 @@ func TestKeyManagement(t *testing.T) { assert.Empty(t, l) // create some keys - _, err = cstore.GetByName(n1) - require.Error(t, err) + has, err := cstore.HasByName(n1) + require.NoError(t, err) + require.False(t, has) i, err := cstore.CreateAccount(n1, mn1, bip39Passphrase, p1, 0, 0) require.NoError(t, err) require.Equal(t, n1, i.GetName()) @@ -91,14 +102,21 @@ func TestKeyManagement(t *testing.T) { // we can get these keys i2, err := cstore.GetByName(n2) require.NoError(t, err) - _, err = cstore.GetByName(n3) - require.NotNil(t, err) - _, err = cstore.GetByAddress(toAddr(i2)) + has, err = cstore.HasByName(n3) + require.NoError(t, err) + require.False(t, has) + has, err = cstore.HasByAddress(toAddr(i2)) + require.NoError(t, err) + require.True(t, has) + // Also check with HasByNameOrAddress + has, err = cstore.HasByNameOrAddress(crypto.AddressToBech32(toAddr(i2))) require.NoError(t, err) + require.True(t, has) addr, err := crypto.AddressFromBech32("g1frtkxv37nq7arvyz5p0mtjqq7hwuvd4dnt892p") require.NoError(t, err) _, err = cstore.GetByAddress(addr) require.NotNil(t, err) + require.True(t, keyerror.IsErrKeyNotFound(err)) // list shows them in order keyS, err := cstore.List() @@ -117,8 +135,9 @@ func TestKeyManagement(t *testing.T) { keyS, err = cstore.List() require.NoError(t, err) require.Equal(t, 1, len(keyS)) - _, err = cstore.GetByName(n1) - require.Error(t, err) + has, err = cstore.HasByName(n1) + require.NoError(t, err) + require.False(t, has) // create an offline key o1 := "offline" @@ -147,6 +166,8 @@ func TestKeyManagement(t *testing.T) { // TestSignVerify does some detailed checks on how we sign and validate // signatures func TestSignVerify(t *testing.T) { + t.Parallel() + cstore := NewInMemory() n1, n2, n3 := "some dude", "a dudette", "dude-ish" @@ -233,6 +254,8 @@ func assertPassword(t *testing.T, cstore Keybase, name, pass, badpass string) { // TestExportImport tests exporting and importing func TestExportImport(t *testing.T) { + t.Parallel() + // make the storage with reasonable defaults cstore := NewInMemory() @@ -263,6 +286,8 @@ func TestExportImport(t *testing.T) { } func TestExportImportPubKey(t *testing.T) { + t.Parallel() + // make the storage with reasonable defaults cstore := NewInMemory() @@ -304,6 +329,8 @@ func TestExportImportPubKey(t *testing.T) { // TestAdvancedKeyManagement verifies update, import, export functionality func TestAdvancedKeyManagement(t *testing.T) { + t.Parallel() + // make the storage with reasonable defaults cstore := NewInMemory() @@ -352,6 +379,8 @@ func TestAdvancedKeyManagement(t *testing.T) { // TestSeedPhrase verifies restoring from a seed phrase func TestSeedPhrase(t *testing.T) { + t.Parallel() + // make the storage with reasonable defaults cstore := NewInMemory() @@ -368,8 +397,9 @@ func TestSeedPhrase(t *testing.T) { // now, let us delete this key err = cstore.Delete(n1, p1, false) require.Nil(t, err, "%+v", err) - _, err = cstore.GetByName(n1) - require.NotNil(t, err) + has, err := cstore.HasByName(n1) + require.NoError(t, err) + require.False(t, has) } func ExampleNew() { diff --git a/tm2/pkg/crypto/keys/keyerror/errors.go b/tm2/pkg/crypto/keys/keyerror/errors.go index 93eb63d2bf3..f7dc97e972d 100644 --- a/tm2/pkg/crypto/keys/keyerror/errors.go +++ b/tm2/pkg/crypto/keys/keyerror/errors.go @@ -1,6 +1,7 @@ package keyerror import ( + "errors" "fmt" ) @@ -40,7 +41,8 @@ func IsErrKeyNotFound(err error) bool { if err == nil { return false } - if keyErr, ok := err.(keybaseError); ok { + var keyErr keybaseError + if errors.As(err, &keyErr) { if keyErr.Code() == codeKeyNotFound { return true } @@ -72,7 +74,8 @@ func IsErrWrongPassword(err error) bool { if err == nil { return false } - if keyErr, ok := err.(keybaseError); ok { + var keyErr keybaseError + if errors.As(err, &keyErr) { if keyErr.Code() == codeWrongPassword { return true } diff --git a/tm2/pkg/crypto/keys/lazy_keybase.go b/tm2/pkg/crypto/keys/lazy_keybase.go index f7f9e229980..62e88d9a8e2 100644 --- a/tm2/pkg/crypto/keys/lazy_keybase.go +++ b/tm2/pkg/crypto/keys/lazy_keybase.go @@ -37,6 +37,36 @@ func (lkb lazyKeybase) List() ([]Info, error) { return NewDBKeybase(db).List() } +func (lkb lazyKeybase) HasByNameOrAddress(nameOrBech32 string) (bool, error) { + db, err := db.NewDB(lkb.name, dbBackend, lkb.dir) + if err != nil { + return false, err + } + defer db.Close() + + return NewDBKeybase(db).HasByNameOrAddress(nameOrBech32) +} + +func (lkb lazyKeybase) HasByName(name string) (bool, error) { + db, err := db.NewDB(lkb.name, dbBackend, lkb.dir) + if err != nil { + return false, err + } + defer db.Close() + + return NewDBKeybase(db).HasByName(name) +} + +func (lkb lazyKeybase) HasByAddress(address crypto.Address) (bool, error) { + db, err := db.NewDB(lkb.name, dbBackend, lkb.dir) + if err != nil { + return false, err + } + defer db.Close() + + return NewDBKeybase(db).HasByAddress(address) +} + func (lkb lazyKeybase) GetByNameOrAddress(nameOrBech32 string) (Info, error) { db, err := db.NewDB(lkb.name, dbBackend, lkb.dir) if err != nil { diff --git a/tm2/pkg/crypto/keys/types.go b/tm2/pkg/crypto/keys/types.go index bba3a917b69..c5d33023a0a 100644 --- a/tm2/pkg/crypto/keys/types.go +++ b/tm2/pkg/crypto/keys/types.go @@ -13,6 +13,9 @@ import ( type Keybase interface { // CRUD on the keystore List() ([]Info, error) + HasByNameOrAddress(nameOrBech32 string) (bool, error) + HasByName(name string) (bool, error) + HasByAddress(address crypto.Address) (bool, error) GetByNameOrAddress(nameOrBech32 string) (Info, error) GetByName(name string) (Info, error) GetByAddress(address crypto.Address) (Info, error) diff --git a/tm2/pkg/crypto/keys/types_test.go b/tm2/pkg/crypto/keys/types_test.go index a0591819a88..2a2dd2c82f6 100644 --- a/tm2/pkg/crypto/keys/types_test.go +++ b/tm2/pkg/crypto/keys/types_test.go @@ -11,6 +11,8 @@ import ( ) func Test_writeReadLedgerInfo(t *testing.T) { + t.Parallel() + var tmpKey secp256k1.PubKeySecp256k1 bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A") copy(tmpKey[:], bz) diff --git a/tm2/pkg/crypto/ledger/ledger_secp256k1.go b/tm2/pkg/crypto/ledger/ledger_secp256k1.go index f0b74cd48d4..f154dbf376c 100644 --- a/tm2/pkg/crypto/ledger/ledger_secp256k1.go +++ b/tm2/pkg/crypto/ledger/ledger_secp256k1.go @@ -2,10 +2,14 @@ package ledger import ( "fmt" + "math/big" "os" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" + secp "github.com/decred/dcrd/dcrec/secp256k1/v4" + ledger "github.com/cosmos/ledger-cosmos-go" "github.com/gnolang/gno/tm2/pkg/amino" "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/crypto/hd" @@ -13,10 +17,6 @@ import ( "github.com/gnolang/gno/tm2/pkg/errors" ) -// discoverLedger defines a function to be invoked at runtime for discovering -// a connected Ledger device. -var discoverLedger discoverLedgerFn - type ( // discoverLedgerFn defines a Ledger discovery function that returns a // connected device or an error upon failure. Its allows a method to avoid CGO @@ -31,7 +31,7 @@ type ( // Returns a compressed pubkey and bech32 address (requires user confirmation) GetAddressPubKeySECP256K1([]uint32, string) ([]byte, string, error) // Signs a message (requires user confirmation) - SignSECP256K1([]uint32, []byte) ([]byte, error) + SignSECP256K1([]uint32, []byte, byte) ([]byte, error) } // PrivKeyLedgerSecp256k1 implements PrivKey, calling the ledger nano we @@ -45,6 +45,17 @@ type ( } ) +// discoverLedger defines a function to be invoked at runtime for discovering +// a connected Ledger device. +var discoverLedger discoverLedgerFn = func() (LedgerSECP256K1, error) { + device, err := ledger.FindLedgerCosmosUserApp() + if err != nil { + return nil, err + } + + return device, nil +} + // NewPrivKeyLedgerSecp256k1Unsafe will generate a new key and store the public key for later use. // // This function is marked as unsafe as it will retrieve a pubkey without user verification. @@ -166,19 +177,33 @@ func warnIfErrors(f func() error) { } func convertDERtoBER(signatureDER []byte) ([]byte, error) { - sigDER, err := btcec.ParseDERSignature(signatureDER[:], btcec.S256()) + sigDER, err := ecdsa.ParseDERSignature(signatureDER) if err != nil { return nil, err } - sigBER := btcec.Signature{R: sigDER.R, S: sigDER.S} - return sigBER.Serialize(), nil -} -func getLedgerDevice() (LedgerSECP256K1, error) { - if discoverLedger == nil { - return nil, errors.New("no Ledger discovery function defined") + sigStr := sigDER.Serialize() + // The format of a DER encoded signature is as follows: + // 0x30 0x02 0x02 + r, s := new(big.Int), new(big.Int) + r.SetBytes(sigStr[4 : 4+sigStr[3]]) + s.SetBytes(sigStr[4+sigStr[3]+2:]) + + sModNScalar := new(secp.ModNScalar) + sModNScalar.SetByteSlice(s.Bytes()) + if sModNScalar.IsOverHalfOrder() { + s = new(big.Int).Sub(secp.S256().N, s) } + sigBytes := make([]byte, 64) + // 0 pad the byte arrays from the left if they aren't big enough. + copy(sigBytes[32-len(r.Bytes()):32], r.Bytes()) + copy(sigBytes[64-len(s.Bytes()):64], s.Bytes()) + + return sigBytes, nil +} + +func getLedgerDevice() (LedgerSECP256K1, error) { device, err := discoverLedger() if err != nil { return nil, errors.Wrap(err, "ledger nano S") @@ -211,8 +236,7 @@ func sign(device LedgerSECP256K1, pkl PrivKeyLedgerSecp256k1, msg []byte) ([]byt if err != nil { return nil, err } - - sig, err := device.SignSECP256K1(pkl.Path.DerivationPath(), msg) + sig, err := device.SignSECP256K1(pkl.Path.DerivationPath(), msg, 0) if err != nil { return nil, err } @@ -235,7 +259,7 @@ func getPubKeyUnsafe(device LedgerSECP256K1, path hd.BIP44Params) (crypto.PubKey } // re-serialize in the 33-byte compressed format - cmp, err := btcec.ParsePubKey(publicKey[:], btcec.S256()) + cmp, err := btcec.ParsePubKey(publicKey[:]) if err != nil { return nil, fmt.Errorf("error parsing public key: %w", err) } @@ -259,7 +283,7 @@ func getPubKeyAddrSafe(device LedgerSECP256K1, path hd.BIP44Params, hrp string) } // re-serialize in the 33-byte compressed format - cmp, err := btcec.ParsePubKey(publicKey[:], btcec.S256()) + cmp, err := btcec.ParsePubKey(publicKey[:]) if err != nil { return nil, "", fmt.Errorf("error parsing public key: %w", err) } diff --git a/tm2/pkg/crypto/merkle/merkle.proto b/tm2/pkg/crypto/merkle/merkle.proto index f415caaaa2f..74aed458a20 100644 --- a/tm2/pkg/crypto/merkle/merkle.proto +++ b/tm2/pkg/crypto/merkle/merkle.proto @@ -5,27 +5,27 @@ option go_package = "github.com/gnolang/gno/tm2/pkg/crypto/merkle/pb"; // messages message ProofOp { - string Type = 1; - bytes Key = 2; - bytes Data = 3; + string type = 1; + bytes key = 2; + bytes data = 3; } message Proof { - repeated ProofOp Ops = 1; + repeated ProofOp ops = 1; } message SimpleProof { - sint64 Total = 1; - sint64 Index = 2; - bytes LeafHash = 3; - repeated bytes Aunts = 4; + sint64 total = 1; + sint64 index = 2; + bytes leaf_hash = 3; + repeated bytes aunts = 4; } message SimpleProofNode { - bytes Hash = 1; - SimpleProofNode Parent = 2; - SimpleProofNode Left = 3; - SimpleProofNode Right = 4; + bytes hash = 1 [json_name = "Hash"]; + SimpleProofNode parent = 2 [json_name = "Parent"]; + SimpleProofNode left = 3 [json_name = "Left"]; + SimpleProofNode right = 4 [json_name = "Right"]; } message MERKLE_BytesList { diff --git a/tm2/pkg/crypto/merkle/proof_key_path_test.go b/tm2/pkg/crypto/merkle/proof_key_path_test.go index 22e3e21ca3d..669e8c5630c 100644 --- a/tm2/pkg/crypto/merkle/proof_key_path_test.go +++ b/tm2/pkg/crypto/merkle/proof_key_path_test.go @@ -10,6 +10,8 @@ import ( ) func TestKeyPath(t *testing.T) { + t.Parallel() + var path KeyPath keys := make([][]byte, 10) alphanum := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" diff --git a/tm2/pkg/crypto/merkle/proof_test.go b/tm2/pkg/crypto/merkle/proof_test.go index 676e281ac60..0424ea351dd 100644 --- a/tm2/pkg/crypto/merkle/proof_test.go +++ b/tm2/pkg/crypto/merkle/proof_test.go @@ -26,19 +26,6 @@ func NewDominoOp(key, input, output string) DominoOp { } } -//nolint:unused -func DominoOpDecoder(pop ProofOp) (ProofOperator, error) { - if pop.Type != ProofOpDomino { - panic("unexpected proof op type") - } - var op DominoOp // a bit strange as we'll discard this, but it works. - err := amino.UnmarshalSized(pop.Data, &op) - if err != nil { - return nil, errors.Wrap(err, "decoding ProofOp.Data into SimpleValueOp") - } - return NewDominoOp(string(pop.Key), op.Input, op.Output), nil -} - func (dop DominoOp) ProofOp() ProofOp { bz := amino.MustMarshalSized(dop) return ProofOp{ @@ -63,9 +50,11 @@ func (dop DominoOp) GetKey() []byte { return []byte(dop.key) } -//---------------------------------------- +// ---------------------------------------- func TestProofOperators(t *testing.T) { + t.Parallel() + var err error // ProofRuntime setup diff --git a/tm2/pkg/crypto/merkle/rfc6962_test.go b/tm2/pkg/crypto/merkle/rfc6962_test.go index 917b09015b0..57f72cea311 100644 --- a/tm2/pkg/crypto/merkle/rfc6962_test.go +++ b/tm2/pkg/crypto/merkle/rfc6962_test.go @@ -24,6 +24,8 @@ import ( ) func TestRFC6962Hasher(t *testing.T) { + t.Parallel() + _, leafHashTrail := trailsFromByteSlices([][]byte{[]byte("L123456")}) leafHash := leafHashTrail.Hash _, leafHashTrail = trailsFromByteSlices([][]byte{{}}) @@ -58,6 +60,8 @@ func TestRFC6962Hasher(t *testing.T) { } { tc := tc t.Run(tc.desc, func(t *testing.T) { + t.Parallel() + wantBytes, err := hex.DecodeString(tc.want) if err != nil { t.Fatalf("hex.DecodeString(%x): %v", tc.want, err) @@ -70,6 +74,8 @@ func TestRFC6962Hasher(t *testing.T) { } func TestRFC6962HasherCollisions(t *testing.T) { + t.Parallel() + // Check that different leaves have different hashes. leaf1, leaf2 := []byte("Hello"), []byte("World") _, leafHashTrail := trailsFromByteSlices([][]byte{leaf1}) diff --git a/tm2/pkg/crypto/merkle/simple_map_test.go b/tm2/pkg/crypto/merkle/simple_map_test.go index 366d9f39099..6df8e0b967e 100644 --- a/tm2/pkg/crypto/merkle/simple_map_test.go +++ b/tm2/pkg/crypto/merkle/simple_map_test.go @@ -8,6 +8,8 @@ import ( ) func TestSimpleMap(t *testing.T) { + t.Parallel() + tests := []struct { keys []string values []string // each string gets converted to []byte in test diff --git a/tm2/pkg/crypto/merkle/simple_proof_test.go b/tm2/pkg/crypto/merkle/simple_proof_test.go index 1a517905b5e..88677e3fb0e 100644 --- a/tm2/pkg/crypto/merkle/simple_proof_test.go +++ b/tm2/pkg/crypto/merkle/simple_proof_test.go @@ -7,6 +7,8 @@ import ( ) func TestSimpleProofValidateBasic(t *testing.T) { + t.Parallel() + testCases := []struct { testName string malleateProof func(*SimpleProof) @@ -23,6 +25,8 @@ func TestSimpleProofValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { + t.Parallel() + _, proofs := SimpleProofsFromByteSlices([][]byte{ []byte("apple"), []byte("watermelon"), diff --git a/tm2/pkg/crypto/merkle/simple_tree_test.go b/tm2/pkg/crypto/merkle/simple_tree_test.go index 8690bae6ac3..209df75b1b5 100644 --- a/tm2/pkg/crypto/merkle/simple_tree_test.go +++ b/tm2/pkg/crypto/merkle/simple_tree_test.go @@ -17,6 +17,8 @@ func (tI testItem) Hash() []byte { } func TestSimpleProof(t *testing.T) { + t.Parallel() + total := 100 items := make([][]byte, total) @@ -69,6 +71,8 @@ func TestSimpleProof(t *testing.T) { } func TestSimpleHashAlternatives(t *testing.T) { + t.Parallel() + total := 100 items := make([][]byte, total) @@ -104,6 +108,8 @@ func BenchmarkSimpleHashAlternatives(b *testing.B) { } func Test_getSplitPoint(t *testing.T) { + t.Parallel() + tests := []struct { length int want int diff --git a/tm2/pkg/crypto/mock/mock_test.go b/tm2/pkg/crypto/mock/mock_test.go index 2e5f1d776bd..3d7ac571faa 100644 --- a/tm2/pkg/crypto/mock/mock_test.go +++ b/tm2/pkg/crypto/mock/mock_test.go @@ -10,6 +10,8 @@ import ( ) func TestSignAndValidateMock(t *testing.T) { + t.Parallel() + privKey := mock.PrivKeyMock([]byte{0x01}) pubKey := privKey.PubKey() diff --git a/tm2/pkg/crypto/multisig/bitarray/compact_bit_array_test.go b/tm2/pkg/crypto/multisig/bitarray/compact_bit_array_test.go index ecbfb64005d..c41e8e3c87b 100644 --- a/tm2/pkg/crypto/multisig/bitarray/compact_bit_array_test.go +++ b/tm2/pkg/crypto/multisig/bitarray/compact_bit_array_test.go @@ -28,6 +28,8 @@ func randCompactBitArray(bits int) (*CompactBitArray, []byte) { } func TestNewBitArrayNeverCrashesOnNegatives(t *testing.T) { + t.Parallel() + bitList := []int{-127, -128, -1 << 31} for _, bits := range bitList { bA := NewCompactBitArray(bits) @@ -36,6 +38,8 @@ func TestNewBitArrayNeverCrashesOnNegatives(t *testing.T) { } func TestJSONMarshalUnmarshal(t *testing.T) { + t.Parallel() + bA1 := NewCompactBitArray(0) bA2 := NewCompactBitArray(1) @@ -73,6 +77,8 @@ func TestJSONMarshalUnmarshal(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.bA.String(), func(t *testing.T) { + t.Parallel() + bz, err := json.Marshal(tc.bA) require.NoError(t, err) @@ -96,6 +102,8 @@ func TestJSONMarshalUnmarshal(t *testing.T) { } func TestCompactMarshalUnmarshal(t *testing.T) { + t.Parallel() + bA1 := NewCompactBitArray(0) bA2 := NewCompactBitArray(1) @@ -133,6 +141,8 @@ func TestCompactMarshalUnmarshal(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.bA.String(), func(t *testing.T) { + t.Parallel() + bz := tc.bA.CompactMarshal() assert.Equal(t, tc.marshalledBA, bz) @@ -153,6 +163,8 @@ func TestCompactMarshalUnmarshal(t *testing.T) { } func TestCompactBitArrayNumOfTrueBitsBefore(t *testing.T) { + t.Parallel() + testCases := []struct { marshalledBA string bAIndex []int @@ -169,6 +181,8 @@ func TestCompactBitArrayNumOfTrueBitsBefore(t *testing.T) { tc := tc tcIndex := tcIndex t.Run(tc.marshalledBA, func(t *testing.T) { + t.Parallel() + var bA *CompactBitArray err := json.Unmarshal([]byte(tc.marshalledBA), &bA) require.NoError(t, err) @@ -181,6 +195,8 @@ func TestCompactBitArrayNumOfTrueBitsBefore(t *testing.T) { } func TestCompactBitArrayGetSetIndex(t *testing.T) { + t.Parallel() + r := rand.New(rand.NewSource(100)) numTests := 10 numBitsPerArr := 100 diff --git a/tm2/pkg/crypto/multisig/multisig.proto b/tm2/pkg/crypto/multisig/multisig.proto index 47667a02f02..4adf6b02a6f 100644 --- a/tm2/pkg/crypto/multisig/multisig.proto +++ b/tm2/pkg/crypto/multisig/multisig.proto @@ -8,6 +8,6 @@ import "google/protobuf/any.proto"; // messages message PubKeyMultisig { - uint64 K = 1; - repeated google.protobuf.Any PubKeys = 2; + uint64 k = 1 [json_name = "threshold"]; + repeated google.protobuf.Any pub_keys = 2 [json_name = "pubkeys"]; } \ No newline at end of file diff --git a/tm2/pkg/crypto/multisig/threshold_pubkey_test.go b/tm2/pkg/crypto/multisig/threshold_pubkey_test.go index 9195344f423..548e8df0a82 100644 --- a/tm2/pkg/crypto/multisig/threshold_pubkey_test.go +++ b/tm2/pkg/crypto/multisig/threshold_pubkey_test.go @@ -15,6 +15,8 @@ import ( // This tests multisig functionality, but it expects the first k signatures to be valid // TODO: Adapt it to give more flexibility about first k signatures being valid func TestThresholdMultisigValidCases(t *testing.T) { + t.Parallel() + pkSet1, sigSet1 := generatePubKeysAndSignatures(5, []byte{1, 2, 3, 4}) cases := []struct { msg []byte @@ -105,6 +107,8 @@ func TestThresholdMultisigValidCases(t *testing.T) { // TODO: Fully replace this test with table driven tests func TestThresholdMultisigDuplicateSignatures(t *testing.T) { + t.Parallel() + msg := []byte{1, 2, 3, 4, 5} pubkeys, sigs := generatePubKeysAndSignatures(5, msg) multisigKey := NewPubKeyMultisigThreshold(2, pubkeys) @@ -118,6 +122,8 @@ func TestThresholdMultisigDuplicateSignatures(t *testing.T) { // TODO: Fully replace this test with table driven tests func TestMultiSigPubKeyEquality(t *testing.T) { + t.Parallel() + msg := []byte{1, 2, 3, 4} pubkeys, _ := generatePubKeysAndSignatures(5, msg) multisigKey := NewPubKeyMultisigThreshold(2, pubkeys) @@ -135,6 +141,8 @@ func TestMultiSigPubKeyEquality(t *testing.T) { } func TestAddress(t *testing.T) { + t.Parallel() + msg := []byte{1, 2, 3, 4} pubkeys, _ := generatePubKeysAndSignatures(5, msg) multisigKey := NewPubKeyMultisigThreshold(2, pubkeys) @@ -142,6 +150,8 @@ func TestAddress(t *testing.T) { } func TestPubKeyMultisigThresholdAminoToIface(t *testing.T) { + t.Parallel() + msg := []byte{1, 2, 3, 4} pubkeys, _ := generatePubKeysAndSignatures(5, msg) multisigKey := NewPubKeyMultisigThreshold(2, pubkeys) diff --git a/tm2/pkg/crypto/random_test.go b/tm2/pkg/crypto/random_test.go index aab505cb61c..dd405c06859 100644 --- a/tm2/pkg/crypto/random_test.go +++ b/tm2/pkg/crypto/random_test.go @@ -11,6 +11,8 @@ import ( // the purpose of this test is primarily to ensure that the randomness // generation won't error. func TestRandomConsistency(t *testing.T) { + t.Parallel() + x1 := crypto.CRandBytes(256) x2 := crypto.CRandBytes(256) x3 := crypto.CRandBytes(256) diff --git a/tm2/pkg/crypto/secp256k1/secp256k1.go b/tm2/pkg/crypto/secp256k1/secp256k1.go index 32af38bcdd9..03f51f5ebf9 100644 --- a/tm2/pkg/crypto/secp256k1/secp256k1.go +++ b/tm2/pkg/crypto/secp256k1/secp256k1.go @@ -7,9 +7,9 @@ import ( "io" "math/big" + secp256k1 "github.com/btcsuite/btcd/btcec/v2" "golang.org/x/crypto/ripemd160" - secp256k1 "github.com/btcsuite/btcd/btcec" "github.com/gnolang/gno/tm2/pkg/amino" "github.com/gnolang/gno/tm2/pkg/crypto" ) @@ -29,7 +29,7 @@ func (privKey PrivKeySecp256k1) Bytes() []byte { // PubKey performs the point-scalar multiplication from the privKey on the // generator point to get the pubkey. func (privKey PrivKeySecp256k1) PubKey() crypto.PubKey { - _, pubkeyObject := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) + _, pubkeyObject := secp256k1.PrivKeyFromBytes(privKey[:]) var pubkeyBytes PubKeySecp256k1 copy(pubkeyBytes[:], pubkeyObject.SerializeCompressed()) return pubkeyBytes diff --git a/tm2/pkg/crypto/secp256k1/secp256k1_cgo_test.go b/tm2/pkg/crypto/secp256k1/secp256k1_cgo_test.go index e5b584707e5..d0070ad9bbe 100644 --- a/tm2/pkg/crypto/secp256k1/secp256k1_cgo_test.go +++ b/tm2/pkg/crypto/secp256k1/secp256k1_cgo_test.go @@ -10,6 +10,8 @@ import ( ) func TestPrivKeySecp256k1SignVerify(t *testing.T) { + t.Parallel() + msg := []byte("A.1.2 ECC Key Pair Generation by Testing Candidates") priv := GenPrivKey() tests := []struct { @@ -23,6 +25,8 @@ func TestPrivKeySecp256k1SignVerify(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got, err := tt.privKey.Sign(msg) if tt.wantSignErr { require.Error(t, err) diff --git a/tm2/pkg/crypto/secp256k1/secp256k1_internal_test.go b/tm2/pkg/crypto/secp256k1/secp256k1_internal_test.go index d680d24f6a9..65d657d98cb 100644 --- a/tm2/pkg/crypto/secp256k1/secp256k1_internal_test.go +++ b/tm2/pkg/crypto/secp256k1/secp256k1_internal_test.go @@ -5,12 +5,13 @@ import ( "math/big" "testing" + underlyingSecp256k1 "github.com/btcsuite/btcd/btcec/v2" "github.com/stretchr/testify/require" - - underlyingSecp256k1 "github.com/btcsuite/btcd/btcec" ) func Test_genPrivKey(t *testing.T) { + t.Parallel() + empty := make([]byte, 32) oneB := big.NewInt(1).Bytes() onePadded := make([]byte, 32) @@ -30,6 +31,8 @@ func Test_genPrivKey(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() + if tt.shouldPanic { require.Panics(t, func() { genPrivKey(bytes.NewReader(tt.notSoRand)) diff --git a/tm2/pkg/crypto/secp256k1/secp256k1_nocgo.go b/tm2/pkg/crypto/secp256k1/secp256k1_nocgo.go index 86da257c264..ccf2104a8d3 100644 --- a/tm2/pkg/crypto/secp256k1/secp256k1_nocgo.go +++ b/tm2/pkg/crypto/secp256k1/secp256k1_nocgo.go @@ -3,29 +3,25 @@ package secp256k1 import ( - "math/big" - - secp256k1 "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" + "github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/gnolang/gno/tm2/pkg/crypto" ) -// used to reject malleable signatures -// see: -// - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/signature_nocgo.go#L90-L93 -// - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/crypto.go#L39 -var secp256k1halfN = new(big.Int).Rsh(secp256k1.S256().N, 1) - // Sign creates an ECDSA signature on curve Secp256k1, using SHA256 on the msg. // The returned signature will be of the form R || S (in lower-S form). func (privKey PrivKeySecp256k1) Sign(msg []byte) ([]byte, error) { - priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) - sig, err := priv.Sign(crypto.Sha256(msg)) + priv, _ := btcec.PrivKeyFromBytes(privKey[:]) + + sig, err := ecdsa.SignCompact(priv, crypto.Sha256(msg), false) // ref uncompressed pubkey if err != nil { return nil, err } - sigBytes := serializeSig(sig) - return sigBytes, nil + + // remove compact sig recovery code byte at the beginning + return sig[1:], nil } // VerifyBytes verifies a signature of the form R || S. @@ -34,37 +30,36 @@ func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sigStr []byte) bool { if len(sigStr) != 64 { return false } - pub, err := secp256k1.ParsePubKey(pubKey[:], secp256k1.S256()) + + pub, err := secp256k1.ParsePubKey(pubKey[:]) if err != nil { return false } - // parse the signature: - signature := signatureFromBytes(sigStr) - // Reject malleable signatures. libsecp256k1 does this check but btcec doesn't. - // see: https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/signature_nocgo.go#L90-L93 - if signature.S.Cmp(secp256k1halfN) > 0 { + + psig, ok := signatureFromBytes(sigStr) + if !ok { return false } - return signature.Verify(crypto.Sha256(msg), pub) + + return psig.Verify(crypto.Sha256(msg), pub) } // Read Signature struct from R || S. Caller needs to ensure // that len(sigStr) == 64. -func signatureFromBytes(sigStr []byte) *secp256k1.Signature { - return &secp256k1.Signature{ - R: new(big.Int).SetBytes(sigStr[:32]), - S: new(big.Int).SetBytes(sigStr[32:64]), +func signatureFromBytes(sigStr []byte) (*ecdsa.Signature, bool) { + // parse the signature: + var r, s secp256k1.ModNScalar + if r.SetByteSlice(sigStr[:32]) { + return nil, false // overflow + } + if s.SetByteSlice(sigStr[32:]) { + return nil, false + } + + // Reject malleable signatures. libsecp256k1 does this check but btcec doesn't. + if s.IsOverHalfOrder() { + return nil, false } -} -// Serialize signature to R || S. -// R, S are padded to 32 bytes respectively. -func serializeSig(sig *secp256k1.Signature) []byte { - rBytes := sig.R.Bytes() - sBytes := sig.S.Bytes() - sigBytes := make([]byte, 64) - // 0 pad the byte arrays from the left if they aren't big enough. - copy(sigBytes[32-len(rBytes):32], rBytes) - copy(sigBytes[64-len(sBytes):64], sBytes) - return sigBytes + return ecdsa.NewSignature(&r, &s), true } diff --git a/tm2/pkg/crypto/secp256k1/secp256k1_nocgo_test.go b/tm2/pkg/crypto/secp256k1/secp256k1_nocgo_test.go index e75d5377670..ae8c47fdfed 100644 --- a/tm2/pkg/crypto/secp256k1/secp256k1_nocgo_test.go +++ b/tm2/pkg/crypto/secp256k1/secp256k1_nocgo_test.go @@ -5,7 +5,6 @@ package secp256k1 import ( "testing" - secp256k1 "github.com/btcsuite/btcd/btcec" "github.com/stretchr/testify/require" ) @@ -13,26 +12,32 @@ import ( // non-canonical signatures fail. // Note: run with CGO_ENABLED=0 or go test -tags !cgo. func TestSignatureVerificationAndRejectUpperS(t *testing.T) { + t.Parallel() + msg := []byte("We have lingered long enough on the shores of the cosmic ocean.") for i := 0; i < 500; i++ { priv := GenPrivKey() sigStr, err := priv.Sign(msg) require.NoError(t, err) - sig := signatureFromBytes(sigStr) - require.False(t, sig.S.Cmp(secp256k1halfN) > 0) + _, ok := signatureFromBytes(sigStr) + require.True(t, ok) pub := priv.PubKey() require.True(t, pub.VerifyBytes(msg, sigStr)) + } +} + +func BenchmarkVerify(b *testing.B) { + priv := GenPrivKey() + msg := []byte("We have lingered long enough on the shores of the cosmic ocean.") + sigStr, err := priv.Sign(msg) + require.NoError(b, err) - // malleate: - sig.S.Sub(secp256k1.S256().CurveParams.N, sig.S) - require.True(t, sig.S.Cmp(secp256k1halfN) > 0) - malSigStr := serializeSig(sig) + pub := priv.PubKey() + b.ResetTimer() - require.False(t, pub.VerifyBytes(msg, malSigStr), - "VerifyBytes incorrect with malleated & invalid S. sig=%v, key=%v", - sig, - priv, - ) + for i := 0; i < b.N; i++ { + ok := pub.VerifyBytes(msg, sigStr) + require.True(b, ok) } } diff --git a/tm2/pkg/crypto/secp256k1/secp256k1_test.go b/tm2/pkg/crypto/secp256k1/secp256k1_test.go index 86aa058f95a..bc0b472d50c 100644 --- a/tm2/pkg/crypto/secp256k1/secp256k1_test.go +++ b/tm2/pkg/crypto/secp256k1/secp256k1_test.go @@ -5,14 +5,13 @@ import ( "math/big" "testing" - "github.com/btcsuite/btcutil/base58" + underlyingSecp256k1 "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil/base58" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/crypto/secp256k1" - - underlyingSecp256k1 "github.com/btcsuite/btcd/btcec" ) type keyData struct { @@ -30,6 +29,8 @@ var secpDataTable = []keyData{ } func TestPubKeySecp256k1Address(t *testing.T) { + t.Parallel() + for _, d := range secpDataTable { privB, _ := hex.DecodeString(d.priv) pubB, _ := hex.DecodeString(d.pub) @@ -50,6 +51,8 @@ func TestPubKeySecp256k1Address(t *testing.T) { } func TestSignAndValidateSecp256k1(t *testing.T) { + t.Parallel() + privKey := secp256k1.GenPrivKey() pubKey := privKey.PubKey() @@ -68,6 +71,8 @@ func TestSignAndValidateSecp256k1(t *testing.T) { // This test is intended to justify the removal of calls to the underlying library // in creating the privkey. func TestSecp256k1LoadPrivkeyAndSerializeIsIdentity(t *testing.T) { + t.Parallel() + numberOfTests := 256 for i := 0; i < numberOfTests; i++ { // Seed the test case with some random bytes @@ -76,7 +81,7 @@ func TestSecp256k1LoadPrivkeyAndSerializeIsIdentity(t *testing.T) { // This function creates a private and public key in the underlying libraries format. // The private key is basically calling new(big.Int).SetBytes(pk), which removes leading zero bytes - priv, _ := underlyingSecp256k1.PrivKeyFromBytes(underlyingSecp256k1.S256(), privKeyBytes[:]) + priv, _ := underlyingSecp256k1.PrivKeyFromBytes(privKeyBytes[:]) // this takes the bytes returned by `(big int).Bytes()`, and if the length is less than 32 bytes, // pads the bytes from the left with zero bytes. Therefore these two functions composed // result in the identity function on privKeyBytes, hence the following equality check @@ -87,6 +92,8 @@ func TestSecp256k1LoadPrivkeyAndSerializeIsIdentity(t *testing.T) { } func TestGenPrivKeySecp256k1(t *testing.T) { + t.Parallel() + // curve oder N N := underlyingSecp256k1.S256().N tests := []struct { @@ -102,6 +109,8 @@ func TestGenPrivKeySecp256k1(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() + gotPrivKey := secp256k1.GenPrivKeySecp256k1(tt.secret) require.NotNil(t, gotPrivKey) // interpret as a big.Int and make sure it is a valid field element: diff --git a/tm2/pkg/crypto/tmhash/hash_test.go b/tm2/pkg/crypto/tmhash/hash_test.go index 4a129bb86ac..c69d86653b1 100644 --- a/tm2/pkg/crypto/tmhash/hash_test.go +++ b/tm2/pkg/crypto/tmhash/hash_test.go @@ -9,6 +9,8 @@ import ( ) func TestHash(t *testing.T) { + t.Parallel() + testVector := []byte("abc") hasher := tmhash.New() hasher.Write(testVector) @@ -25,6 +27,8 @@ func TestHash(t *testing.T) { } func TestHashTruncated(t *testing.T) { + t.Parallel() + testVector := []byte("abc") hasher := tmhash.NewTruncated() hasher.Write(testVector) diff --git a/tm2/pkg/crypto/util.go b/tm2/pkg/crypto/util.go deleted file mode 100644 index f017557f09b..00000000000 --- a/tm2/pkg/crypto/util.go +++ /dev/null @@ -1,7 +0,0 @@ -package crypto - -func cp(bz []byte) (ret []byte) { - ret = make([]byte, len(bz)) - copy(ret, bz) - return ret -} diff --git a/tm2/pkg/crypto/xchacha20poly1305/vector_test.go b/tm2/pkg/crypto/xchacha20poly1305/vector_test.go index 3001217f41e..7ed186de19d 100644 --- a/tm2/pkg/crypto/xchacha20poly1305/vector_test.go +++ b/tm2/pkg/crypto/xchacha20poly1305/vector_test.go @@ -19,6 +19,8 @@ func fromHex(bits string) []byte { } func TestHChaCha20(t *testing.T) { + t.Parallel() + for i, v := range hChaCha20Vectors { var key [32]byte var nonce [16]byte @@ -63,6 +65,8 @@ var hChaCha20Vectors = []struct { } func TestVectors(t *testing.T) { + t.Parallel() + for i, v := range vectors { if len(v.plaintext) == 0 { v.plaintext = make([]byte, len(v.ciphertext)) diff --git a/tm2/pkg/crypto/xchacha20poly1305/xchachapoly_test.go b/tm2/pkg/crypto/xchacha20poly1305/xchachapoly_test.go index 5b92a7607e7..ea7b0bee1a4 100644 --- a/tm2/pkg/crypto/xchacha20poly1305/xchachapoly_test.go +++ b/tm2/pkg/crypto/xchacha20poly1305/xchachapoly_test.go @@ -14,6 +14,8 @@ import ( // Use of this source code is governed by a BSD-style // license that can be found at the bottom of this file. func TestRandom(t *testing.T) { + t.Parallel() + // Some random tests to verify Open(Seal) == Plaintext for i := 0; i < 256; i++ { var nonce [24]byte diff --git a/tm2/pkg/crypto/xsalsa20symmetric/symmetric_test.go b/tm2/pkg/crypto/xsalsa20symmetric/symmetric_test.go index dea965a4356..7460add5185 100644 --- a/tm2/pkg/crypto/xsalsa20symmetric/symmetric_test.go +++ b/tm2/pkg/crypto/xsalsa20symmetric/symmetric_test.go @@ -11,6 +11,8 @@ import ( ) func TestSimple(t *testing.T) { + t.Parallel() + plaintext := []byte("sometext") secret := []byte("somesecretoflengththirtytwo===32") ciphertext := EncryptSymmetric(plaintext, secret) @@ -21,6 +23,8 @@ func TestSimple(t *testing.T) { } func TestSimpleWithKDF(t *testing.T) { + t.Parallel() + salt := []byte("1234567890123456") plaintext := []byte("sometext") secretPass := []byte("somesecret") diff --git a/tm2/pkg/db/backend_test.go b/tm2/pkg/db/backend_test.go index b6cd3f026aa..fd03629fd58 100644 --- a/tm2/pkg/db/backend_test.go +++ b/tm2/pkg/db/backend_test.go @@ -41,8 +41,12 @@ func testBackendGetSetDelete(t *testing.T, backend BackendType) { } func TestBackendsGetSetDelete(t *testing.T) { + t.Parallel() + for dbType := range backends { t.Run(string(dbType), func(t *testing.T) { + t.Parallel() + testBackendGetSetDelete(t, dbType) }) } @@ -59,6 +63,8 @@ func withDB(t *testing.T, creator dbCreator, fn func(DB)) { } func TestBackendsNilKeys(t *testing.T) { + t.Parallel() + // Test all backends. for dbType, creator := range backends { withDB(t, creator, func(db DB) { @@ -137,6 +143,8 @@ func TestBackendsNilKeys(t *testing.T) { } func TestGoLevelDBBackend(t *testing.T) { + t.Parallel() + name := fmt.Sprintf("test_%x", randStr(12)) db, err := NewDB(name, GoLevelDBBackend, t.TempDir()) require.NoError(t, err) @@ -146,9 +154,11 @@ func TestGoLevelDBBackend(t *testing.T) { } func TestDBIterator(t *testing.T) { + t.Parallel() + for dbType := range backends { t.Run(fmt.Sprintf("%v", dbType), func(t *testing.T) { - t.Helper() + t.Parallel() testDBIterator(t, dbType) }) diff --git a/tm2/pkg/db/boltdb_test.go b/tm2/pkg/db/boltdb_test.go index 031023f275a..57091fb53a2 100644 --- a/tm2/pkg/db/boltdb_test.go +++ b/tm2/pkg/db/boltdb_test.go @@ -10,6 +10,8 @@ import ( ) func TestBoltDBNewBoltDB(t *testing.T) { + t.Parallel() + name := fmt.Sprintf("test_%x", randStr(12)) db, err := NewBoltDB(name, t.TempDir()) diff --git a/tm2/pkg/db/c_level_db_test.go b/tm2/pkg/db/c_level_db_test.go index 2bdffd14a83..7044ea0f4de 100644 --- a/tm2/pkg/db/c_level_db_test.go +++ b/tm2/pkg/db/c_level_db_test.go @@ -88,6 +88,8 @@ func bytes2Int64(buf []byte) int64 { */ func TestCLevelDBBackend(t *testing.T) { + t.Parallel() + name := fmt.Sprintf("test_%x", randStr(12)) // Can't use "" (current directory) or "./" here because levigo.Open returns: // "Error initializing DB: IO error: test_XXX.db: Invalid argument" @@ -99,6 +101,8 @@ func TestCLevelDBBackend(t *testing.T) { } func TestCLevelDBStats(t *testing.T) { + t.Parallel() + name := fmt.Sprintf("test_%x", randStr(12)) db, err := NewDB(name, CLevelDBBackend, t.TempDir()) require.NoError(t, err) diff --git a/tm2/pkg/db/db_test.go b/tm2/pkg/db/db_test.go index 3634fb4bf03..62231645613 100644 --- a/tm2/pkg/db/db_test.go +++ b/tm2/pkg/db/db_test.go @@ -8,8 +8,12 @@ import ( ) func TestDBIteratorSingleKey(t *testing.T) { + t.Parallel() + for backend := range backends { t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { + t.Parallel() + db := newTempDB(t, backend) db.SetSync(bz("1"), bz("value_1")) @@ -27,8 +31,12 @@ func TestDBIteratorSingleKey(t *testing.T) { } func TestDBIteratorTwoKeys(t *testing.T) { + t.Parallel() + for backend := range backends { t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { + t.Parallel() + db := newTempDB(t, backend) db.SetSync(bz("1"), bz("value_1")) @@ -54,8 +62,12 @@ func TestDBIteratorTwoKeys(t *testing.T) { } func TestDBIteratorMany(t *testing.T) { + t.Parallel() + for backend := range backends { t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { + t.Parallel() + db := newTempDB(t, backend) keys := make([][]byte, 100) @@ -78,8 +90,12 @@ func TestDBIteratorMany(t *testing.T) { } func TestDBIteratorEmpty(t *testing.T) { + t.Parallel() + for backend := range backends { t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { + t.Parallel() + db := newTempDB(t, backend) itr := db.Iterator(nil, nil) @@ -90,8 +106,12 @@ func TestDBIteratorEmpty(t *testing.T) { } func TestDBIteratorEmptyBeginAfter(t *testing.T) { + t.Parallel() + for backend := range backends { t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { + t.Parallel() + db := newTempDB(t, backend) itr := db.Iterator(bz("1"), nil) @@ -102,8 +122,12 @@ func TestDBIteratorEmptyBeginAfter(t *testing.T) { } func TestDBIteratorNonemptyBeginAfter(t *testing.T) { + t.Parallel() + for backend := range backends { t.Run(fmt.Sprintf("Backend %s", backend), func(t *testing.T) { + t.Parallel() + db := newTempDB(t, backend) db.SetSync(bz("1"), bz("value_1")) @@ -115,6 +139,8 @@ func TestDBIteratorNonemptyBeginAfter(t *testing.T) { } func TestDBBatchWrite(t *testing.T) { + t.Parallel() + testCases := []struct { modify func(batch Batch) calls map[string]int diff --git a/tm2/pkg/db/fsdb.go b/tm2/pkg/db/fsdb.go index aa8cea83889..e11cd6d4ce5 100644 --- a/tm2/pkg/db/fsdb.go +++ b/tm2/pkg/db/fsdb.go @@ -2,7 +2,7 @@ package db import ( "fmt" - "io/ioutil" + "io" "net/url" "os" "path/filepath" @@ -189,7 +189,7 @@ func read(path string) ([]byte, error) { } defer f.Close() - d, err := ioutil.ReadAll(f) + d, err := io.ReadAll(f) if err != nil { return nil, err } diff --git a/tm2/pkg/db/go_level_db_test.go b/tm2/pkg/db/go_level_db_test.go index a85bb6e8713..677b8d86c08 100644 --- a/tm2/pkg/db/go_level_db_test.go +++ b/tm2/pkg/db/go_level_db_test.go @@ -9,6 +9,8 @@ import ( ) func TestGoLevelDBNewGoLevelDB(t *testing.T) { + t.Parallel() + dir := t.TempDir() name := fmt.Sprintf("test_%x", randStr(12)) diff --git a/tm2/pkg/db/gorocks_db_test.go b/tm2/pkg/db/gorocks_db_test.go index 9d1b10cef34..d4792424f39 100644 --- a/tm2/pkg/db/gorocks_db_test.go +++ b/tm2/pkg/db/gorocks_db_test.go @@ -10,6 +10,8 @@ import ( ) func TestGoRocksDBBackend(t *testing.T) { + t.Parallel() + name := fmt.Sprintf("test_%x", randStr(12)) db := NewDB(name, GoRocksDBBackend, t.TempDir()) @@ -18,6 +20,8 @@ func TestGoRocksDBBackend(t *testing.T) { } func TestGoRocksDBStats(t *testing.T) { + t.Parallel() + name := fmt.Sprintf("test_%x", randStr(12)) db := NewDB(name, GoRocksDBBackend, t.TempDir()) diff --git a/tm2/pkg/db/grocks_db_test.go b/tm2/pkg/db/grocks_db_test.go index 60592bc8d82..10b33026128 100644 --- a/tm2/pkg/db/grocks_db_test.go +++ b/tm2/pkg/db/grocks_db_test.go @@ -10,6 +10,8 @@ import ( ) func TestGRocksDBBackend(t *testing.T) { + t.Parallel() + name := fmt.Sprintf("test_%x", randStr(12)) db := NewDB(name, GRocksDBBackend, t.TempDir()) @@ -18,6 +20,8 @@ func TestGRocksDBBackend(t *testing.T) { } func TestGRocksDBStats(t *testing.T) { + t.Parallel() + name := fmt.Sprintf("test_%x", randStr(12)) db := NewDB(name, GRocksDBBackend, t.TempDir()) diff --git a/tm2/pkg/db/prefix_db.go b/tm2/pkg/db/prefix_db.go index ced82f922d1..29ed53639e8 100644 --- a/tm2/pkg/db/prefix_db.go +++ b/tm2/pkg/db/prefix_db.go @@ -329,7 +329,7 @@ func stripPrefix(key []byte, prefix []byte) (stripped []byte) { panic("should not happen") } if !bytes.Equal(key[:len(prefix)], prefix) { - panic("should not happne") + panic("should not happen") } return key[len(prefix):] } diff --git a/tm2/pkg/db/prefix_db_test.go b/tm2/pkg/db/prefix_db_test.go index e3e37c7d12c..0948cce98a5 100644 --- a/tm2/pkg/db/prefix_db_test.go +++ b/tm2/pkg/db/prefix_db_test.go @@ -18,6 +18,8 @@ func mockDBWithStuff() DB { } func TestPrefixDBSimple(t *testing.T) { + t.Parallel() + db := mockDBWithStuff() pdb := NewPrefixDB(db, bz("key")) @@ -36,6 +38,8 @@ func TestPrefixDBSimple(t *testing.T) { } func TestPrefixDBIterator1(t *testing.T) { + t.Parallel() + db := mockDBWithStuff() pdb := NewPrefixDB(db, bz("key")) @@ -54,6 +58,8 @@ func TestPrefixDBIterator1(t *testing.T) { } func TestPrefixDBIterator2(t *testing.T) { + t.Parallel() + db := mockDBWithStuff() pdb := NewPrefixDB(db, bz("key")) @@ -64,6 +70,8 @@ func TestPrefixDBIterator2(t *testing.T) { } func TestPrefixDBIterator3(t *testing.T) { + t.Parallel() + db := mockDBWithStuff() pdb := NewPrefixDB(db, bz("key")) @@ -82,6 +90,8 @@ func TestPrefixDBIterator3(t *testing.T) { } func TestPrefixDBIterator4(t *testing.T) { + t.Parallel() + db := mockDBWithStuff() pdb := NewPrefixDB(db, bz("key")) @@ -92,6 +102,8 @@ func TestPrefixDBIterator4(t *testing.T) { } func TestPrefixDBReverseIterator1(t *testing.T) { + t.Parallel() + db := mockDBWithStuff() pdb := NewPrefixDB(db, bz("key")) @@ -110,6 +122,8 @@ func TestPrefixDBReverseIterator1(t *testing.T) { } func TestPrefixDBReverseIterator2(t *testing.T) { + t.Parallel() + db := mockDBWithStuff() pdb := NewPrefixDB(db, bz("key")) @@ -128,6 +142,8 @@ func TestPrefixDBReverseIterator2(t *testing.T) { } func TestPrefixDBReverseIterator3(t *testing.T) { + t.Parallel() + db := mockDBWithStuff() pdb := NewPrefixDB(db, bz("key")) @@ -138,6 +154,8 @@ func TestPrefixDBReverseIterator3(t *testing.T) { } func TestPrefixDBReverseIterator4(t *testing.T) { + t.Parallel() + db := mockDBWithStuff() pdb := NewPrefixDB(db, bz("key")) @@ -148,6 +166,8 @@ func TestPrefixDBReverseIterator4(t *testing.T) { } func TestPrefixDBReverseIterator5(t *testing.T) { + t.Parallel() + db := mockDBWithStuff() pdb := NewPrefixDB(db, bz("key")) @@ -164,6 +184,8 @@ func TestPrefixDBReverseIterator5(t *testing.T) { } func TestPrefixDBReverseIterator6(t *testing.T) { + t.Parallel() + db := mockDBWithStuff() pdb := NewPrefixDB(db, bz("key")) @@ -178,6 +200,8 @@ func TestPrefixDBReverseIterator6(t *testing.T) { } func TestPrefixDBReverseIterator7(t *testing.T) { + t.Parallel() + db := mockDBWithStuff() pdb := NewPrefixDB(db, bz("key")) diff --git a/tm2/pkg/db/util_test.go b/tm2/pkg/db/util_test.go index adf52123290..f66fddde64e 100644 --- a/tm2/pkg/db/util_test.go +++ b/tm2/pkg/db/util_test.go @@ -7,8 +7,12 @@ import ( // Empty iterator for empty db. func TestPrefixIteratorNoMatchNil(t *testing.T) { + t.Parallel() + for backend := range backends { t.Run(fmt.Sprintf("Prefix w/ backend %s", backend), func(t *testing.T) { + t.Parallel() + db := newTempDB(t, backend) itr := IteratePrefix(db, []byte("2")) @@ -19,6 +23,8 @@ func TestPrefixIteratorNoMatchNil(t *testing.T) { // Empty iterator for db populated after iterator created. func TestPrefixIteratorNoMatch1(t *testing.T) { + t.Parallel() + for backend := range backends { if backend == BoltDBBackend { t.Log("bolt does not support concurrent writes while iterating") @@ -26,6 +32,8 @@ func TestPrefixIteratorNoMatch1(t *testing.T) { } t.Run(fmt.Sprintf("Prefix w/ backend %s", backend), func(t *testing.T) { + t.Parallel() + db := newTempDB(t, backend) itr := IteratePrefix(db, []byte("2")) db.SetSync(bz("1"), bz("value_1")) @@ -37,8 +45,12 @@ func TestPrefixIteratorNoMatch1(t *testing.T) { // Empty iterator for prefix starting after db entry. func TestPrefixIteratorNoMatch2(t *testing.T) { + t.Parallel() + for backend := range backends { t.Run(fmt.Sprintf("Prefix w/ backend %s", backend), func(t *testing.T) { + t.Parallel() + db := newTempDB(t, backend) db.SetSync(bz("3"), bz("value_3")) itr := IteratePrefix(db, []byte("4")) @@ -50,8 +62,12 @@ func TestPrefixIteratorNoMatch2(t *testing.T) { // Iterator with single val for db with single val, starting from that val. func TestPrefixIteratorMatch1(t *testing.T) { + t.Parallel() + for backend := range backends { t.Run(fmt.Sprintf("Prefix w/ backend %s", backend), func(t *testing.T) { + t.Parallel() + db := newTempDB(t, backend) db.SetSync(bz("2"), bz("value_2")) itr := IteratePrefix(db, bz("2")) @@ -68,8 +84,12 @@ func TestPrefixIteratorMatch1(t *testing.T) { // Iterator with prefix iterates over everything with same prefix. func TestPrefixIteratorMatches1N(t *testing.T) { + t.Parallel() + for backend := range backends { t.Run(fmt.Sprintf("Prefix w/ backend %s", backend), func(t *testing.T) { + t.Parallel() + db := newTempDB(t, backend) // prefixed diff --git a/tm2/pkg/errors/errors_test.go b/tm2/pkg/errors/errors_test.go index ddd2539738f..21115c21862 100644 --- a/tm2/pkg/errors/errors_test.go +++ b/tm2/pkg/errors/errors_test.go @@ -9,6 +9,8 @@ import ( ) func TestErrorPanic(t *testing.T) { + t.Parallel() + type pnk struct { msg string } @@ -31,6 +33,8 @@ func TestErrorPanic(t *testing.T) { } func TestWrapSomething(t *testing.T) { + t.Parallel() + err := Wrap("something", "formatter%v%v", 0, 1) assert.Equal(t, "something", err.Data()) @@ -40,6 +44,8 @@ func TestWrapSomething(t *testing.T) { } func TestWrapNothing(t *testing.T) { + t.Parallel() + err := Wrap(nil, "formatter%v%v", 0, 1) assert.Equal(t, @@ -51,6 +57,8 @@ func TestWrapNothing(t *testing.T) { } func TestErrorNew(t *testing.T) { + t.Parallel() + err := New("formatter%v%v", 0, 1) assert.Equal(t, @@ -62,6 +70,8 @@ func TestErrorNew(t *testing.T) { } func TestErrorNewWithDetails(t *testing.T) { + t.Parallel() + err := New("formatter%v%v", 0, 1) err.Trace(0, "trace %v", 1) err.Trace(0, "trace %v", 2) @@ -71,6 +81,8 @@ func TestErrorNewWithDetails(t *testing.T) { } func TestErrorNewWithStacktrace(t *testing.T) { + t.Parallel() + err := New("formatter%v%v", 0, 1).Stacktrace() assert.Equal(t, @@ -82,6 +94,8 @@ func TestErrorNewWithStacktrace(t *testing.T) { } func TestErrorNewWithTrace(t *testing.T) { + t.Parallel() + err := New("formatter%v%v", 0, 1) err.Trace(0, "trace %v", 1) err.Trace(0, "trace %v", 2) @@ -100,6 +114,8 @@ func TestErrorNewWithTrace(t *testing.T) { } func TestWrapError(t *testing.T) { + t.Parallel() + var err1 error = New("my message") var err2 error = Wrap(err1, "another message") assert.Equal(t, err1, err2) diff --git a/tm2/pkg/events/events_test.go b/tm2/pkg/events/events_test.go index f3b4aa2fd82..5232364b1c8 100644 --- a/tm2/pkg/events/events_test.go +++ b/tm2/pkg/events/events_test.go @@ -35,6 +35,8 @@ func TestAddListenerFireOnce(t *testing.T) { // TestAddListenerFireMany sets up an EventSwitch, subscribes a single // listener, and sends a thousand integers. func TestAddListenerFireMany(t *testing.T) { + t.Parallel() + evsw := NewEventSwitch() err := evsw.Start() require.NoError(t, err) @@ -62,6 +64,8 @@ func TestAddListenerFireMany(t *testing.T) { // TestAddListeners sets up an EventSwitch, subscribes three // listeners, and sends a thousand integers for each. func TestAddListeners(t *testing.T) { + t.Parallel() + evsw := NewEventSwitch() err := evsw.Start() require.NoError(t, err) @@ -100,6 +104,8 @@ func TestAddListeners(t *testing.T) { } func TestAddAndRemoveListenerConcurrency(t *testing.T) { + t.Parallel() + var ( stopInputEvent = false roundCount = 2000 @@ -145,6 +151,8 @@ func TestAddAndRemoveListenerConcurrency(t *testing.T) { } func TestAddAndRemoveListener(t *testing.T) { + t.Parallel() + evsw := NewEventSwitch() err := evsw.Start() require.NoError(t, err) @@ -192,6 +200,8 @@ func TestAddAndRemoveListener(t *testing.T) { // TestRemoveListener does basic tests on adding and removing func TestRemoveListener(t *testing.T) { + t.Parallel() + evsw := NewEventSwitch() err := evsw.Start() require.NoError(t, err) @@ -241,6 +251,8 @@ func TestRemoveListener(t *testing.T) { // NOTE: it is important to run this test with race conditions tracking on, // `go test -race`, to examine for possible race conditions. func TestRemoveListenersAsync(t *testing.T) { + t.Parallel() + evsw := NewEventSwitch() err := evsw.Start() require.NoError(t, err) diff --git a/tm2/pkg/events/store.go b/tm2/pkg/events/store.go index bea4626394e..1891fd17ac6 100644 --- a/tm2/pkg/events/store.go +++ b/tm2/pkg/events/store.go @@ -1,33 +1,12 @@ package events -import ( - "fmt" - - auto "github.com/gnolang/gno/tm2/pkg/autofile" -) - // StoreStream stores events to disk but is also listenaable. type StoreStream interface { Eventable SetHeight(height int64) // to demarcate height in WAL for replay. } -type storeStream struct { - afile *auto.AutoFile - buf []byte - height int64 -} - -func (ss *storeStream) SetHeight(height int64) { - if ss.height < height { - // write new height - ss.height = height - } else /* if height <= ss.height */ { - panic(fmt.Sprintf("invalid SetHeight height value. current %v, got %v", ss.height, height)) - } -} - -//---------------------------------------- +// ---------------------------------------- // move to own file // FilterStream is listenable and lets you filter. diff --git a/tm2/pkg/flow/io_test.go b/tm2/pkg/flow/io_test.go index 196c7c65d9a..d023d32c347 100644 --- a/tm2/pkg/flow/io_test.go +++ b/tm2/pkg/flow/io_test.go @@ -32,6 +32,8 @@ func nextStatus(m *Monitor) Status { } func TestReader(t *testing.T) { + t.Parallel() + in := make([]byte, 100) for i := range in { in[i] = byte(i) @@ -101,6 +103,8 @@ func TestReader(t *testing.T) { } func TestWriter(t *testing.T) { + t.Parallel() + b := make([]byte, 100) for i := range b { b[i] = byte(i) diff --git a/tm2/pkg/iavl/basic_test.go b/tm2/pkg/iavl/basic_test.go index ab46792aee4..f684209a682 100644 --- a/tm2/pkg/iavl/basic_test.go +++ b/tm2/pkg/iavl/basic_test.go @@ -13,6 +13,8 @@ import ( ) func TestBasic(t *testing.T) { + t.Parallel() + tree := NewMutableTree(db.NewMemDB(), 0) up := tree.Set([]byte("1"), []byte("one")) if up { @@ -103,6 +105,8 @@ func TestBasic(t *testing.T) { } func TestUnit(t *testing.T) { + t.Parallel() + expectHash := func(tree *ImmutableTree, hashCount int64) { // ensure number of new hash calculations is as expected. hash, count := tree.hashWithCount() @@ -184,6 +188,8 @@ func TestUnit(t *testing.T) { } func TestRemove(t *testing.T) { + t.Parallel() + size := 10000 keyLen, dataLen := 16, 40 @@ -214,6 +220,8 @@ func TestRemove(t *testing.T) { } func TestIntegration(t *testing.T) { + t.Parallel() + type record struct { key string value string @@ -279,6 +287,8 @@ func TestIntegration(t *testing.T) { } func TestIterateRange(t *testing.T) { + t.Parallel() + type record struct { key string value string @@ -363,6 +373,8 @@ func TestIterateRange(t *testing.T) { } func TestPersistence(t *testing.T) { + t.Parallel() + db := db.NewMemDB() // Create some random key value pairs @@ -390,6 +402,8 @@ func TestPersistence(t *testing.T) { } func TestProof(t *testing.T) { + t.Parallel() + // Construct some random tree db := db.NewMemDB() tree := NewMutableTree(db, 100) @@ -420,6 +434,8 @@ func TestProof(t *testing.T) { } func TestTreeProof(t *testing.T) { + t.Parallel() + db := db.NewMemDB() tree := NewMutableTree(db, 100) assert.Equal(t, tree.Hash(), []byte(nil)) diff --git a/tm2/pkg/iavl/benchmarks/bench_test.go b/tm2/pkg/iavl/benchmarks/bench_test.go index de6a573020e..77ad77eed28 100644 --- a/tm2/pkg/iavl/benchmarks/bench_test.go +++ b/tm2/pkg/iavl/benchmarks/bench_test.go @@ -75,19 +75,6 @@ func runKnownQueries(b *testing.B, t *iavl.MutableTree, keys [][]byte) { } } -func runInsert(b *testing.B, t *iavl.MutableTree, keyLen, dataLen, blockSize int) *iavl.MutableTree { - b.Helper() - - for i := 1; i <= b.N; i++ { - t.Set(randBytes(keyLen), randBytes(dataLen)) - if i%blockSize == 0 { - t.Hash() - t.SaveVersion() - } - } - return t -} - func runUpdate(b *testing.B, t *iavl.MutableTree, dataLen, blockSize int, keys [][]byte) *iavl.MutableTree { b.Helper() @@ -102,23 +89,6 @@ func runUpdate(b *testing.B, t *iavl.MutableTree, dataLen, blockSize int, keys [ return t } -func runDelete(b *testing.B, t *iavl.MutableTree, blockSize int, keys [][]byte) *iavl.MutableTree { - b.Helper() - - var key []byte - l := int32(len(keys)) - for i := 1; i <= b.N; i++ { - key = keys[rand.Int31n(l)] - // key = randBytes(16) - // TODO: test if removed, use more keys (from insert) - t.Remove(key) - if i%blockSize == 0 { - commitTree(b, t) - } - } - return t -} - // runBlock measures time for an entire block, not just one tx func runBlock(b *testing.B, t *iavl.MutableTree, keyLen, dataLen, blockSize int, keys [][]byte) *iavl.MutableTree { b.Helper() diff --git a/tm2/pkg/iavl/common/random_test.go b/tm2/pkg/iavl/common/random_test.go index 6ed73ab898c..4c1ee8024e0 100644 --- a/tm2/pkg/iavl/common/random_test.go +++ b/tm2/pkg/iavl/common/random_test.go @@ -11,12 +11,16 @@ import ( ) func TestRandStr(t *testing.T) { + t.Parallel() + l := 243 s := RandStr(l) assert.Equal(t, l, len(s)) } func TestRandBytes(t *testing.T) { + t.Parallel() + l := 243 b := RandBytes(l) assert.Equal(t, l, len(b)) diff --git a/tm2/pkg/iavl/node.go b/tm2/pkg/iavl/node.go index f2a944afc6a..ee522016f0c 100644 --- a/tm2/pkg/iavl/node.go +++ b/tm2/pkg/iavl/node.go @@ -371,10 +371,6 @@ func (node *Node) traverse(t *ImmutableTree, ascending bool, cb func(*Node) bool }) } -func (node *Node) traverseWithDepth(t *ImmutableTree, ascending bool, cb func(*Node, uint8) bool) bool { - return node.traverseInRange(t, nil, nil, ascending, false, 0, cb) -} - func (node *Node) traverseInRange(t *ImmutableTree, start, end []byte, ascending bool, inclusive bool, depth uint8, cb func(*Node, uint8) bool) bool { afterStart := start == nil || bytes.Compare(start, node.key) < 0 startOrAfter := start == nil || bytes.Compare(start, node.key) <= 0 diff --git a/tm2/pkg/iavl/nodedb.go b/tm2/pkg/iavl/nodedb.go index 3e59b3480e9..e1998d8cc1d 100644 --- a/tm2/pkg/iavl/nodedb.go +++ b/tm2/pkg/iavl/nodedb.go @@ -385,7 +385,7 @@ func (ndb *nodeDB) saveRoot(hash []byte, version int64) error { return nil } -////////////////// Utility and test functions ///////////////////////////////// +// ----------- Utility and test functions // ----------- func (ndb *nodeDB) leafNodes() []*Node { leaves := []*Node{} diff --git a/tm2/pkg/iavl/proof_forgery_test.go b/tm2/pkg/iavl/proof_forgery_test.go index a742ad40fcc..028675f7c48 100644 --- a/tm2/pkg/iavl/proof_forgery_test.go +++ b/tm2/pkg/iavl/proof_forgery_test.go @@ -14,6 +14,8 @@ import ( ) func TestProofForgery(t *testing.T) { + t.Parallel() + source := rand.NewSource(0) r := rand.New(source) cacheSize := 0 diff --git a/tm2/pkg/iavl/proof_path.go b/tm2/pkg/iavl/proof_path.go index f66fcf3c48c..a5ac14bc4e7 100644 --- a/tm2/pkg/iavl/proof_path.go +++ b/tm2/pkg/iavl/proof_path.go @@ -1,11 +1,8 @@ package iavl import ( - "bytes" "fmt" "strings" - - "github.com/gnolang/gno/tm2/pkg/errors" ) // pathWithLeaf is a path to a leaf node and the leaf node itself. @@ -28,14 +25,6 @@ func (pwl pathWithLeaf) StringIndented(indent string) string { indent) } -// `verify` checks that the leaf node's hash + the inner nodes merkle-izes to -// the given root. If it returns an error, it means the leafHash or the -// PathToLeaf is incorrect. -func (pwl pathWithLeaf) verify(root []byte) error { - leafHash := pwl.Leaf.Hash() - return pwl.Path.verify(leafHash, root) -} - // `computeRootHash` computes the root hash with leaf node. // Does not verify the root hash. func (pwl pathWithLeaf) computeRootHash() []byte { @@ -73,21 +62,6 @@ func (pl PathToLeaf) stringIndented(indent string) string { indent) } -// `verify` checks that the leaf node's hash + the inner nodes merkle-izes to -// the given root. If it returns an error, it means the leafHash or the -// PathToLeaf is incorrect. -func (pl PathToLeaf) verify(leafHash []byte, root []byte) error { - hash := leafHash - for i := len(pl) - 1; i >= 0; i-- { - pin := pl[i] - hash = pin.Hash(hash) - } - if !bytes.Equal(root, hash) { - return errors.Wrap(ErrInvalidProof, "") - } - return nil -} - // `computeRootHash` computes the root hash assuming some leaf hash. // Does not verify the root hash. func (pl PathToLeaf) computeRootHash(leafHash []byte) []byte { @@ -117,37 +91,6 @@ func (pl PathToLeaf) isRightmost() bool { return true } -func (pl PathToLeaf) isEmpty() bool { - return pl == nil || len(pl) == 0 -} - -func (pl PathToLeaf) dropRoot() PathToLeaf { - if pl.isEmpty() { - return pl - } - return pl[:len(pl)-1] -} - -func (pl PathToLeaf) hasCommonRoot(pl2 PathToLeaf) bool { - if pl.isEmpty() || pl2.isEmpty() { - return false - } - leftEnd := pl[len(pl)-1] - rightEnd := pl2[len(pl2)-1] - - return bytes.Equal(leftEnd.Left, rightEnd.Left) && - bytes.Equal(leftEnd.Right, rightEnd.Right) -} - -func (pl PathToLeaf) isLeftAdjacentTo(pl2 PathToLeaf) bool { - for pl.hasCommonRoot(pl2) { - pl, pl2 = pl.dropRoot(), pl2.dropRoot() - } - pl, pl2 = pl.dropRoot(), pl2.dropRoot() - - return pl.isRightmost() && pl2.isLeftmost() -} - // returns -1 if invalid. func (pl PathToLeaf) Index() (idx int64) { for i, node := range pl { diff --git a/tm2/pkg/iavl/proof_range.go b/tm2/pkg/iavl/proof_range.go index 799fb65cebb..ea6bce24fc0 100644 --- a/tm2/pkg/iavl/proof_range.go +++ b/tm2/pkg/iavl/proof_range.go @@ -299,7 +299,7 @@ func (proof *RangeProof) _computeRootHash() (rootHash []byte, treeEnd bool, err return rootHash, treeEnd, nil } -/////////////////////////////////////////////////////////////////////////////// +// ----------- // keyStart is inclusive and keyEnd is exclusive. // If keyStart or keyEnd don't exist, the leaf before keyStart @@ -442,7 +442,7 @@ func (t *ImmutableTree) getRangeProof(keyStart, keyEnd []byte, limit int) (proof }, keys, values, nil } -//---------------------------------------- +// ---------------------------------------- // GetWithProof gets the value under the key if it exists, or returns nil. // A proof of existence or absence is returned alongside the value. diff --git a/tm2/pkg/iavl/proof_test.go b/tm2/pkg/iavl/proof_test.go index f67ea45b00a..ad4d85d1cd5 100644 --- a/tm2/pkg/iavl/proof_test.go +++ b/tm2/pkg/iavl/proof_test.go @@ -15,6 +15,8 @@ import ( ) func TestTreeGetWithProof(t *testing.T) { + t.Parallel() + tree := NewMutableTree(db.NewMemDB(), 0) require := require.New(t) for _, ikey := range []byte{0x11, 0x32, 0x50, 0x72, 0x99} { @@ -49,6 +51,8 @@ func TestTreeGetWithProof(t *testing.T) { } func TestTreeKeyExistsProof(t *testing.T) { + t.Parallel() + tree := NewMutableTree(db.NewMemDB(), 0) root := tree.WorkingHash() @@ -115,6 +119,8 @@ func TestTreeKeyExistsProof(t *testing.T) { } func TestTreeKeyInRangeProofs(t *testing.T) { + t.Parallel() + tree := NewMutableTree(db.NewMemDB(), 0) require := require.New(t) keys := []byte{0x0a, 0x11, 0x2e, 0x32, 0x50, 0x72, 0x99, 0xa1, 0xe4, 0xf7} // 10 total. diff --git a/tm2/pkg/iavl/tree_dotgraph_test.go b/tm2/pkg/iavl/tree_dotgraph_test.go index 29be03ca241..3c0233f51fa 100644 --- a/tm2/pkg/iavl/tree_dotgraph_test.go +++ b/tm2/pkg/iavl/tree_dotgraph_test.go @@ -1,13 +1,15 @@ package iavl import ( - "io/ioutil" + "io" "testing" db "github.com/gnolang/gno/tm2/pkg/db" ) func TestWriteDOTGraph(t *testing.T) { + t.Parallel() + tree := NewMutableTree(db.NewMemDB(), 0) for _, ikey := range []byte{ 0x0a, 0x11, 0x2e, 0x32, 0x50, 0x72, 0x99, 0xa1, 0xe4, 0xf7, @@ -15,5 +17,5 @@ func TestWriteDOTGraph(t *testing.T) { key := []byte{ikey} tree.Set(key, key) } - WriteDOTGraph(ioutil.Discard, tree.ImmutableTree, []PathToLeaf{}) + WriteDOTGraph(io.Discard, tree.ImmutableTree, []PathToLeaf{}) } diff --git a/tm2/pkg/iavl/tree_fuzz_test.go b/tm2/pkg/iavl/tree_fuzz_test.go index 7ea9bb50389..64e20d944a2 100644 --- a/tm2/pkg/iavl/tree_fuzz_test.go +++ b/tm2/pkg/iavl/tree_fuzz_test.go @@ -105,6 +105,8 @@ func genRandomProgram(size int) *program { // Generate many programs and run them. func TestMutableTreeFuzz(t *testing.T) { + t.Parallel() + maxIterations := testFuzzIterations progsPerIteration := 100000 iterations := 0 diff --git a/tm2/pkg/iavl/tree_test.go b/tm2/pkg/iavl/tree_test.go index 56a00ee2424..ba5bbe3e5ef 100644 --- a/tm2/pkg/iavl/tree_test.go +++ b/tm2/pkg/iavl/tree_test.go @@ -51,6 +51,8 @@ func getTestDB() (db.DB, func()) { } func TestVersionedRandomTree(t *testing.T) { + t.Parallel() + require := require.New(t) d, closeDB := getTestDB() @@ -105,6 +107,8 @@ func TestVersionedRandomTree(t *testing.T) { } func TestVersionedRandomTreeSmallKeys(t *testing.T) { + t.Parallel() + require := require.New(t) d, closeDB := getTestDB() defer closeDB() @@ -146,6 +150,8 @@ func TestVersionedRandomTreeSmallKeys(t *testing.T) { } func TestVersionedRandomTreeSmallKeysRandomDeletes(t *testing.T) { + t.Parallel() + require := require.New(t) d, closeDB := getTestDB() defer closeDB() @@ -187,6 +193,8 @@ func TestVersionedRandomTreeSmallKeysRandomDeletes(t *testing.T) { } func TestVersionedTreeSpecial1(t *testing.T) { + t.Parallel() + tree := NewMutableTree(db.NewMemDB(), 100) tree.Set([]byte("C"), []byte("so43QQFN")) @@ -209,6 +217,8 @@ func TestVersionedTreeSpecial1(t *testing.T) { } func TestVersionedRandomTreeSpecial2(t *testing.T) { + t.Parallel() + require := require.New(t) tree := NewMutableTree(db.NewMemDB(), 100) @@ -225,6 +235,8 @@ func TestVersionedRandomTreeSpecial2(t *testing.T) { } func TestVersionedEmptyTree(t *testing.T) { + t.Parallel() + require := require.New(t) d, closeDB := getTestDB() defer closeDB() @@ -281,6 +293,8 @@ func TestVersionedEmptyTree(t *testing.T) { } func TestVersionedTree(t *testing.T) { + t.Parallel() + require := require.New(t) d, closeDB := getTestDB() defer closeDB() @@ -468,6 +482,8 @@ func TestVersionedTree(t *testing.T) { } func TestVersionedTreeVersionDeletingEfficiency(t *testing.T) { + t.Parallel() + d, closeDB := getTestDB() defer closeDB() @@ -512,6 +528,8 @@ func TestVersionedTreeVersionDeletingEfficiency(t *testing.T) { } func TestVersionedTreeOrphanDeleting(t *testing.T) { + t.Parallel() + mdb := db.NewMemDB() tree := NewMutableTree(mdb, 0) @@ -550,6 +568,8 @@ func TestVersionedTreeOrphanDeleting(t *testing.T) { } func TestVersionedTreeSpecialCase(t *testing.T) { + t.Parallel() + require := require.New(t) tree := NewMutableTree(db.NewMemDB(), 100) @@ -571,6 +591,8 @@ func TestVersionedTreeSpecialCase(t *testing.T) { } func TestVersionedTreeSpecialCase2(t *testing.T) { + t.Parallel() + require := require.New(t) d := db.NewMemDB() @@ -598,6 +620,8 @@ func TestVersionedTreeSpecialCase2(t *testing.T) { } func TestVersionedTreeSpecialCase3(t *testing.T) { + t.Parallel() + require := require.New(t) tree := NewMutableTree(db.NewMemDB(), 0) @@ -626,6 +650,8 @@ func TestVersionedTreeSpecialCase3(t *testing.T) { } func TestVersionedTreeSaveAndLoad(t *testing.T) { + t.Parallel() + require := require.New(t) d := db.NewMemDB() tree := NewMutableTree(d, 0) @@ -677,6 +703,8 @@ func TestVersionedTreeSaveAndLoad(t *testing.T) { } func TestVersionedTreeErrors(t *testing.T) { + t.Parallel() + require := require.New(t) tree := NewMutableTree(db.NewMemDB(), 100) @@ -706,6 +734,8 @@ func TestVersionedTreeErrors(t *testing.T) { } func TestVersionedCheckpoints(t *testing.T) { + t.Parallel() + require := require.New(t) d, closeDB := getTestDB() defer closeDB() @@ -762,6 +792,8 @@ func TestVersionedCheckpoints(t *testing.T) { } func TestVersionedCheckpointsSpecialCase(t *testing.T) { + t.Parallel() + require := require.New(t) tree := NewMutableTree(db.NewMemDB(), 0) key := []byte("k") @@ -788,6 +820,8 @@ func TestVersionedCheckpointsSpecialCase(t *testing.T) { } func TestVersionedCheckpointsSpecialCase2(t *testing.T) { + t.Parallel() + tree := NewMutableTree(db.NewMemDB(), 0) tree.Set([]byte("U"), []byte("XamDUtiJ")) @@ -808,6 +842,8 @@ func TestVersionedCheckpointsSpecialCase2(t *testing.T) { } func TestVersionedCheckpointsSpecialCase3(t *testing.T) { + t.Parallel() + tree := NewMutableTree(db.NewMemDB(), 0) tree.Set([]byte("n"), []byte("2wUCUs8q")) @@ -828,6 +864,8 @@ func TestVersionedCheckpointsSpecialCase3(t *testing.T) { } func TestVersionedCheckpointsSpecialCase4(t *testing.T) { + t.Parallel() + tree := NewMutableTree(db.NewMemDB(), 0) tree.Set([]byte("U"), []byte("XamDUtiJ")) @@ -860,6 +898,8 @@ func TestVersionedCheckpointsSpecialCase4(t *testing.T) { } func TestVersionedCheckpointsSpecialCase5(t *testing.T) { + t.Parallel() + tree := NewMutableTree(db.NewMemDB(), 0) tree.Set([]byte("R"), []byte("ygZlIzeW")) @@ -877,6 +917,8 @@ func TestVersionedCheckpointsSpecialCase5(t *testing.T) { } func TestVersionedCheckpointsSpecialCase6(t *testing.T) { + t.Parallel() + tree := NewMutableTree(db.NewMemDB(), 0) tree.Set([]byte("Y"), []byte("MW79JQeV")) @@ -909,6 +951,8 @@ func TestVersionedCheckpointsSpecialCase6(t *testing.T) { } func TestVersionedCheckpointsSpecialCase7(t *testing.T) { + t.Parallel() + tree := NewMutableTree(db.NewMemDB(), 100) tree.Set([]byte("n"), []byte("OtqD3nyn")) @@ -942,6 +986,8 @@ func TestVersionedCheckpointsSpecialCase7(t *testing.T) { } func TestVersionedTreeEfficiency(t *testing.T) { + t.Parallel() + require := require.New(t) tree := NewMutableTree(db.NewMemDB(), 0) versions := 20 @@ -977,6 +1023,8 @@ func TestVersionedTreeEfficiency(t *testing.T) { } func TestVersionedTreeProofs(t *testing.T) { + t.Parallel() + require := require.New(t) tree := NewMutableTree(db.NewMemDB(), 0) @@ -1047,6 +1095,8 @@ func TestVersionedTreeProofs(t *testing.T) { } func TestOrphans(t *testing.T) { + t.Parallel() + // If you create a sequence of saved versions // Then randomly delete versions other than the first and last until only those two remain // Any remaining orphan nodes should be constrained to just the first version @@ -1079,6 +1129,8 @@ func TestOrphans(t *testing.T) { } func TestVersionedTreeHash(t *testing.T) { + t.Parallel() + require := require.New(t) tree := NewMutableTree(db.NewMemDB(), 0) @@ -1101,6 +1153,8 @@ func TestVersionedTreeHash(t *testing.T) { } func TestNilValueSemantics(t *testing.T) { + t.Parallel() + require := require.New(t) tree := NewMutableTree(db.NewMemDB(), 0) @@ -1110,6 +1164,8 @@ func TestNilValueSemantics(t *testing.T) { } func TestCopyValueSemantics(t *testing.T) { + t.Parallel() + require := require.New(t) tree := NewMutableTree(db.NewMemDB(), 0) @@ -1127,6 +1183,8 @@ func TestCopyValueSemantics(t *testing.T) { } func TestRollback(t *testing.T) { + t.Parallel() + require := require.New(t) tree := NewMutableTree(db.NewMemDB(), 0) @@ -1156,6 +1214,8 @@ func TestRollback(t *testing.T) { } func TestLazyLoadVersion(t *testing.T) { + t.Parallel() + mdb := db.NewMemDB() tree := NewMutableTree(mdb, 0) maxVersions := 10 @@ -1194,6 +1254,8 @@ func TestLazyLoadVersion(t *testing.T) { } func TestOverwrite(t *testing.T) { + t.Parallel() + require := require.New(t) mdb := db.NewMemDB() @@ -1226,6 +1288,8 @@ func TestOverwrite(t *testing.T) { } func TestLoadVersionForOverwriting(t *testing.T) { + t.Parallel() + require := require.New(t) mdb := db.NewMemDB() @@ -1290,7 +1354,7 @@ func TestLoadVersionForOverwriting(t *testing.T) { require.NoError(err, "SaveVersion should not fail.") } -//////////////////////////// BENCHMARKS /////////////////////////////////////// +// ----------- BENCHMARKS // ----------- func BenchmarkTreeLoadAndDelete(b *testing.B) { if testing.Short() { diff --git a/tm2/pkg/log/logger.go b/tm2/pkg/log/logger.go deleted file mode 100644 index 8c34c994833..00000000000 --- a/tm2/pkg/log/logger.go +++ /dev/null @@ -1,48 +0,0 @@ -package log - -import ( - "io" - "sync" -) - -type LogLevel int - -const ( - LevelDebug LogLevel = iota - LevelInfo - LevelError -) - -type Logger interface { - Debug(msg string, keyvals ...interface{}) - Info(msg string, keyvals ...interface{}) - Error(msg string, keyvals ...interface{}) - - With(keyvals ...interface{}) Logger - - SetLevel(LogLevel) -} - -//---------------------------------------- - -// NewSyncWriter returns a new writer that is safe for concurrent use by -// multiple goroutines. Writes to the returned writer are passed on to w. If -// another write is already in progress, the calling goroutine blocks until -// the writer is available. -func NewSyncWriter(w io.Writer) io.Writer { - return &syncWriter{Writer: w} -} - -// syncWriter synchronizes concurrent writes to an io.Writer. -type syncWriter struct { - sync.Mutex - io.Writer -} - -// Write writes p to the underlying io.Writer. If another write is already in -// progress, the calling goroutine blocks until the syncWriter is available. -func (w *syncWriter) Write(p []byte) (n int, err error) { - w.Lock() - defer w.Unlock() - return w.Writer.Write(p) -} diff --git a/tm2/pkg/log/noop.go b/tm2/pkg/log/noop.go new file mode 100644 index 00000000000..b7c465ede0f --- /dev/null +++ b/tm2/pkg/log/noop.go @@ -0,0 +1,34 @@ +package log + +import ( + "context" + + "golang.org/x/exp/slog" +) + +// NewNoopLogger returns a new no-op logger +func NewNoopLogger() *slog.Logger { + return slog.New(newNoopHandler()) +} + +type noopHandler struct{} + +func newNoopHandler() *noopHandler { + return &noopHandler{} +} + +func (n *noopHandler) Enabled(_ context.Context, _ slog.Level) bool { + return false +} + +func (n *noopHandler) Handle(_ context.Context, _ slog.Record) error { + return nil +} + +func (n *noopHandler) WithAttrs(_ []slog.Attr) slog.Handler { + return n +} + +func (n *noopHandler) WithGroup(_ string) slog.Handler { + return n +} diff --git a/tm2/pkg/log/nop_logger.go b/tm2/pkg/log/nop_logger.go deleted file mode 100644 index 5a12b088082..00000000000 --- a/tm2/pkg/log/nop_logger.go +++ /dev/null @@ -1,19 +0,0 @@ -package log - -type nopLogger struct{} - -// Interface assertions -var _ Logger = (*nopLogger)(nil) - -// NewNopLogger returns a logger that doesn't do anything. -func NewNopLogger() Logger { return &nopLogger{} } - -func (nopLogger) Info(string, ...interface{}) {} -func (nopLogger) Debug(string, ...interface{}) {} -func (nopLogger) Error(string, ...interface{}) {} - -func (l *nopLogger) With(...interface{}) Logger { - return l -} - -func (l *nopLogger) SetLevel(LogLevel) {} diff --git a/tm2/pkg/log/testing.go b/tm2/pkg/log/testing.go new file mode 100644 index 00000000000..be610ee38d2 --- /dev/null +++ b/tm2/pkg/log/testing.go @@ -0,0 +1,75 @@ +package log + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/jaekwon/testify/require" + "golang.org/x/exp/slog" +) + +// NewTestingLogger returns a new testing logger +func NewTestingLogger(t *testing.T) *slog.Logger { + t.Helper() + + // Parse the environment vars + envLevel := os.Getenv("LOG_LEVEL") + envPath := os.Getenv("LOG_PATH_DIR") + + if !testing.Verbose() && envLevel == "" && envPath == "" { + return NewNoopLogger() + } + + // Default logger config + logLevel := slog.LevelError + logOutput := os.Stdout + + // Set the logger level, if any + switch strings.ToLower(envLevel) { + case "info": + logLevel = slog.LevelInfo + case "debug": + logLevel = slog.LevelDebug + case "warn": + logLevel = slog.LevelWarn + } + + // Check if the log output needs to be a file + if envPath != "" { + // Create the top-level log directory + if err := os.Mkdir(envPath, 0o755); err != nil && !os.IsExist(err) { + t.Fatalf("Failed to create log directory: %v", err) + } + + logName := fmt.Sprintf( + "%s-%d.log", + strings.ReplaceAll(t.Name(), "/", "_"), // unique test name + time.Now().Unix(), // unique test timestamp + ) + logPath := filepath.Join(envPath, logName) + + logFile, err := os.Create(logPath) + require.NoError(t, err) + + t.Cleanup(func() { + _ = logFile.Close() + }) + + logOutput = logFile + } + + // Create the log handler + logHandler := slog.NewTextHandler( + logOutput, + &slog.HandlerOptions{ + AddSource: true, + Level: logLevel, + }, + ) + + return slog.New(logHandler) +} diff --git a/tm2/pkg/log/testing_logger.go b/tm2/pkg/log/testing_logger.go deleted file mode 100644 index 05e91b54a7d..00000000000 --- a/tm2/pkg/log/testing_logger.go +++ /dev/null @@ -1,58 +0,0 @@ -package log - -import ( - "io" - "os" - "testing" - - "github.com/gnolang/gno/tm2/pkg/colors" -) - -// reuse the same logger across all tests -var _testingLogger Logger - -// TestingLogger returns a TMLogger which writes to STDOUT if testing being run -// with the verbose (-v) flag, NopLogger otherwise. -// -// Note that the call to TestingLogger() must be made -// inside a test (not in the init func) because -// verbose flag only set at the time of testing. -func TestingLogger() Logger { - return TestingLoggerWithOutput(os.Stdout) -} - -// TestingLoggerWOutput returns a TMLogger which writes to (w io.Writer) if testing being run -// with the verbose (-v) flag, NopLogger otherwise. -// -// Note that the call to TestingLoggerWithOutput(w io.Writer) must be made -// inside a test (not in the init func) because -// verbose flag only set at the time of testing. -func TestingLoggerWithOutput(w io.Writer) Logger { - if _testingLogger != nil { - return _testingLogger - } - - if testing.Verbose() { - _testingLogger = NewTMLogger(NewSyncWriter(w)) - } else { - _testingLogger = NewNopLogger() - } - - return _testingLogger -} - -// TestingLoggerWithColorFn allow you to provide your own color function. See -// TestingLogger for documentation. -func TestingLoggerWithColorFn(colorFn func(keyvals ...interface{}) colors.Color) Logger { - if _testingLogger != nil { - return _testingLogger - } - - if testing.Verbose() { - _testingLogger = NewTMLoggerWithColorFn(NewSyncWriter(os.Stdout), colorFn) - } else { - _testingLogger = NewNopLogger() - } - - return _testingLogger -} diff --git a/tm2/pkg/log/tm_logger.go b/tm2/pkg/log/tm_logger.go deleted file mode 100644 index 555ece714cd..00000000000 --- a/tm2/pkg/log/tm_logger.go +++ /dev/null @@ -1,136 +0,0 @@ -package log - -import ( - "fmt" - "io" - - "github.com/gnolang/gno/tm2/pkg/colors" -) - -const ( - logKeyLevel = ".level" - logKeyMsg = ".msg" -) - -type tmLogger struct { - level LogLevel - colorFn func(keyvals ...interface{}) colors.Color - writer io.Writer -} - -var _ Logger = (*tmLogger)(nil) - -func NewTMLogger(w io.Writer) *tmLogger { - // Color by level value - colorFn := func(keyvals ...interface{}) colors.Color { - if keyvals[0] != logKeyLevel { - panic(fmt.Sprintf("expected level key to be first, got %v", keyvals[0])) - } - switch keyvals[1].(LogLevel) { - case LevelDebug: - return colors.Gray - case LevelError: - return colors.Red - default: - return colors.None - } - } - return &tmLogger{ - level: LevelDebug, - colorFn: colorFn, - writer: w, - } -} - -// NewTMLoggerWithColorFn allows you to provide your own color function. -func NewTMLoggerWithColorFn(w io.Writer, colorFn func(keyvals ...interface{}) colors.Color) *tmLogger { - return &tmLogger{ - level: LevelDebug, - colorFn: colorFn, - writer: w, - } -} - -func (l *tmLogger) SetLevel(lvl LogLevel) { - l.level = lvl -} - -// Debug logs a message at level Debug. -func (l *tmLogger) Debug(msg string, keyvals ...interface{}) { - if l.level <= LevelDebug { - writeLog(l.writer, LevelDebug, l.colorFn, msg, keyvals...) - } -} - -// Info logs a message at level Info. -func (l *tmLogger) Info(msg string, keyvals ...interface{}) { - if l.level <= LevelInfo { - writeLog(l.writer, LevelInfo, l.colorFn, msg, keyvals...) - } -} - -// Error logs a message at level Error. -func (l *tmLogger) Error(msg string, keyvals ...interface{}) { - if l.level <= LevelError { - writeLog(l.writer, LevelError, l.colorFn, msg, keyvals...) - } -} - -// With returns a new contextual logger with keyvals prepended to those passed -// to calls to Info, Debug or Error. -func (l *tmLogger) With(keyvals ...interface{}) Logger { - return newWithLogger(l, keyvals) -} - -//---------------------------------------- - -type withLogger struct { - base Logger - keyvals []interface{} -} - -var _ Logger = (*withLogger)(nil) - -func newWithLogger(base Logger, keyvals []interface{}) *withLogger { - return &withLogger{ - base: base, - keyvals: keyvals, - } -} - -func (l *withLogger) Debug(msg string, keyvals ...interface{}) { - keyvals = append(keyvals, l.keyvals) - l.base.Debug(msg, keyvals) -} - -func (l *withLogger) Info(msg string, keyvals ...interface{}) { - keyvals = append(keyvals, l.keyvals) - l.base.Info(msg, keyvals) -} - -func (l *withLogger) Error(msg string, keyvals ...interface{}) { - keyvals = append(keyvals, l.keyvals) - l.base.Error(msg, keyvals) -} - -func (l *withLogger) With(keyvals ...interface{}) Logger { - return newWithLogger(l, keyvals) -} - -func (l *withLogger) SetLevel(LogLevel) { - panic("SetLevel not supported on derived WithLogger") -} - -//---------------------------------------- - -func writeLog(w io.Writer, level LogLevel, colorFn func(keyvals ...interface{}) colors.Color, msg string, keyvals ...interface{}) { - keyvals = append([]interface{}{ - logKeyLevel, level, - logKeyMsg, msg, - }, keyvals...) - color := colorFn(keyvals...) - str := fmt.Sprintln(keyvals...) - str = str[:len(str)-1] - str = color(str) - fmt.Fprintln(w, str) -} diff --git a/tm2/pkg/log/tm_logger_test.go b/tm2/pkg/log/tm_logger_test.go deleted file mode 100644 index a6d56bb8feb..00000000000 --- a/tm2/pkg/log/tm_logger_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package log_test - -import ( - "bytes" - "io/ioutil" - "strings" - "testing" - - "github.com/gnolang/gno/tm2/pkg/log" -) - -func TestLoggerLogsItsErrors(t *testing.T) { - var buf bytes.Buffer - - logger := log.NewTMLogger(&buf) - logger.Info("foo", "baz baz", "bar") - msg := strings.TrimSpace(buf.String()) - if !strings.Contains(msg, "foo") { - t.Errorf("Expected logger msg to contain foo, got %s", msg) - } -} - -func BenchmarkTMLoggerSimple(b *testing.B) { - benchmarkRunner(b, log.NewTMLogger(ioutil.Discard), baseInfoMessage) -} - -func BenchmarkTMLoggerContextual(b *testing.B) { - benchmarkRunner(b, log.NewTMLogger(ioutil.Discard), withInfoMessage) -} - -func benchmarkRunner(b *testing.B, logger log.Logger, f func(log.Logger)) { - b.Helper() - - lc := logger.With("common_key", "common_value") - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - f(lc) - } -} - -var ( - baseInfoMessage = func(logger log.Logger) { logger.Info("foo_message", "foo_key", "foo_value") } - withInfoMessage = func(logger log.Logger) { logger.With("a", "b").Info("c", "d", "f") } -) diff --git a/tm2/pkg/os/net_test.go b/tm2/pkg/os/net_test.go index 258ee28bb28..8cbba60aef2 100644 --- a/tm2/pkg/os/net_test.go +++ b/tm2/pkg/os/net_test.go @@ -7,6 +7,8 @@ import ( ) func TestProtocolAndAddress(t *testing.T) { + t.Parallel() + cases := []struct { fullAddr string proto string diff --git a/tm2/pkg/os/tempfile_test.go b/tm2/pkg/os/tempfile_test.go index 12d6abb27cb..ec294d58e30 100644 --- a/tm2/pkg/os/tempfile_test.go +++ b/tm2/pkg/os/tempfile_test.go @@ -5,7 +5,6 @@ package os import ( "bytes" "fmt" - "io/ioutil" "os" "testing" @@ -21,7 +20,7 @@ func TestWriteFileAtomic(t *testing.T) { perm os.FileMode = 0o600 ) - f, err := ioutil.TempFile("/tmp", "write-atomic-test-") + f, err := os.CreateTemp("/tmp", "write-atomic-test-") if err != nil { t.Fatal(err) } diff --git a/tm2/pkg/p2p/config/config.go b/tm2/pkg/p2p/config/config.go index 855c0fd9844..a0ed2d39381 100644 --- a/tm2/pkg/p2p/config/config.go +++ b/tm2/pkg/p2p/config/config.go @@ -6,7 +6,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/errors" ) -//----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // P2PConfig const ( @@ -16,63 +16,61 @@ const ( FuzzModeDelay ) -var defaultConfigDir = "config" // duplicate across module configs? - // P2PConfig defines the configuration options for the Tendermint peer-to-peer networking layer type P2PConfig struct { RootDir string `toml:"home"` // Address to listen for incoming connections - ListenAddress string `toml:"laddr"` + ListenAddress string `toml:"laddr" comment:"Address to listen for incoming connections"` // Address to advertise to peers for them to dial - ExternalAddress string `toml:"external_address"` + ExternalAddress string `toml:"external_address" comment:"Address to advertise to peers for them to dial\n If empty, will use the same port as the laddr,\n and will introspect on the listener or use UPnP\n to figure out the address."` // Comma separated list of seed nodes to connect to - Seeds string `toml:"seeds"` + Seeds string `toml:"seeds" comment:"Comma separated list of seed nodes to connect to"` // Comma separated list of nodes to keep persistent connections to - PersistentPeers string `toml:"persistent_peers"` + PersistentPeers string `toml:"persistent_peers" comment:"Comma separated list of nodes to keep persistent connections to"` // UPNP port forwarding - UPNP bool `toml:"upnp"` + UPNP bool `toml:"upnp" comment:"UPNP port forwarding"` // Maximum number of inbound peers - MaxNumInboundPeers int `toml:"max_num_inbound_peers"` + MaxNumInboundPeers int `toml:"max_num_inbound_peers" comment:"Maximum number of inbound peers"` // Maximum number of outbound peers to connect to, excluding persistent peers - MaxNumOutboundPeers int `toml:"max_num_outbound_peers"` + MaxNumOutboundPeers int `toml:"max_num_outbound_peers" comment:"Maximum number of outbound peers to connect to, excluding persistent peers"` // Time to wait before flushing messages out on the connection - FlushThrottleTimeout time.Duration `toml:"flush_throttle_timeout"` + FlushThrottleTimeout time.Duration `toml:"flush_throttle_timeout" comment:"Time to wait before flushing messages out on the connection"` // Maximum size of a message packet payload, in bytes - MaxPacketMsgPayloadSize int `toml:"max_packet_msg_payload_size"` + MaxPacketMsgPayloadSize int `toml:"max_packet_msg_payload_size" comment:"Maximum size of a message packet payload, in bytes"` // Rate at which packets can be sent, in bytes/second - SendRate int64 `toml:"send_rate"` + SendRate int64 `toml:"send_rate" comment:"Rate at which packets can be sent, in bytes/second"` // Rate at which packets can be received, in bytes/second - RecvRate int64 `toml:"recv_rate"` + RecvRate int64 `toml:"recv_rate" comment:"Rate at which packets can be received, in bytes/second"` // Set true to enable the peer-exchange reactor - PexReactor bool `toml:"pex"` + PexReactor bool `toml:"pex" comment:"Set true to enable the peer-exchange reactor"` // Seed mode, in which node constantly crawls the network and looks for // peers. If another node asks it for addresses, it responds and disconnects. // // Does not work if the peer-exchange reactor is disabled. - SeedMode bool `toml:"seed_mode"` + SeedMode bool `toml:"seed_mode" comment:"Seed mode, in which node constantly crawls the network and looks for\n peers. If another node asks it for addresses, it responds and disconnects.\n\n Does not work if the peer-exchange reactor is disabled."` // Comma separated list of peer IDs to keep private (will not be gossiped to // other peers) - PrivatePeerIDs string `toml:"private_peer_ids"` + PrivatePeerIDs string `toml:"private_peer_ids" comment:"Comma separated list of peer IDs to keep private (will not be gossiped to other peers)"` // Toggle to disable guard against peers connecting from the same ip. - AllowDuplicateIP bool `toml:"allow_duplicate_ip"` + AllowDuplicateIP bool `toml:"allow_duplicate_ip" comment:"Toggle to disable guard against peers connecting from the same ip."` // Peer connection configuration. - HandshakeTimeout time.Duration `toml:"handshake_timeout"` + HandshakeTimeout time.Duration `toml:"handshake_timeout" comment:"Peer connection configuration."` DialTimeout time.Duration `toml:"dial_timeout"` // Testing params. diff --git a/tm2/pkg/p2p/conn/connection.go b/tm2/pkg/p2p/conn/connection.go index 1f726410aaf..7f0cf39fe5f 100644 --- a/tm2/pkg/p2p/conn/connection.go +++ b/tm2/pkg/p2p/conn/connection.go @@ -13,10 +13,11 @@ import ( "sync/atomic" "time" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/amino" "github.com/gnolang/gno/tm2/pkg/errors" "github.com/gnolang/gno/tm2/pkg/flow" - "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/maths" "github.com/gnolang/gno/tm2/pkg/service" "github.com/gnolang/gno/tm2/pkg/timer" @@ -197,7 +198,7 @@ func NewMConnectionWithConfig(conn net.Conn, chDescs []*ChannelDescriptor, onRec return mconn } -func (c *MConnection) SetLogger(l log.Logger) { +func (c *MConnection) SetLogger(l *slog.Logger) { c.BaseService.SetLogger(l) for _, ch := range c.channels { ch.SetLogger(l) @@ -732,7 +733,7 @@ type Channel struct { maxPacketMsgPayloadSize int - Logger log.Logger + Logger *slog.Logger } func newChannel(conn *MConnection, desc ChannelDescriptor) *Channel { @@ -749,7 +750,7 @@ func newChannel(conn *MConnection, desc ChannelDescriptor) *Channel { } } -func (ch *Channel) SetLogger(l log.Logger) { +func (ch *Channel) SetLogger(l *slog.Logger) { ch.Logger = l } diff --git a/tm2/pkg/p2p/conn/connection_test.go b/tm2/pkg/p2p/conn/connection_test.go index 6974dd97e56..7bbe88ded22 100644 --- a/tm2/pkg/p2p/conn/connection_test.go +++ b/tm2/pkg/p2p/conn/connection_test.go @@ -16,32 +16,43 @@ import ( const maxPingPongPacketSize = 1024 // bytes -func createTestMConnection(conn net.Conn) *MConnection { +func createTestMConnection(t *testing.T, conn net.Conn) *MConnection { + t.Helper() + onReceive := func(chID byte, msgBytes []byte) { } onError := func(r interface{}) { } - c := createMConnectionWithCallbacks(conn, onReceive, onError) - c.SetLogger(log.TestingLogger()) + c := createMConnectionWithCallbacks(t, conn, onReceive, onError) + c.SetLogger(log.NewTestingLogger(t)) return c } -func createMConnectionWithCallbacks(conn net.Conn, onReceive func(chID byte, msgBytes []byte), onError func(r interface{})) *MConnection { +func createMConnectionWithCallbacks( + t *testing.T, + conn net.Conn, + onReceive func(chID byte, msgBytes []byte), + onError func(r interface{}), +) *MConnection { + t.Helper() + cfg := DefaultMConnConfig() cfg.PingInterval = 90 * time.Millisecond cfg.PongTimeout = 45 * time.Millisecond chDescs := []*ChannelDescriptor{{ID: 0x01, Priority: 1, SendQueueCapacity: 1}} c := NewMConnectionWithConfig(conn, chDescs, onReceive, onError, cfg) - c.SetLogger(log.TestingLogger()) + c.SetLogger(log.NewTestingLogger(t)) return c } func TestMConnectionSendFlushStop(t *testing.T) { + t.Parallel() + server, client := NetPipe() defer server.Close() //nolint: errcheck defer client.Close() //nolint: errcheck - clientConn := createTestMConnection(client) + clientConn := createTestMConnection(t, client) err := clientConn.Start() require.Nil(t, err) defer clientConn.Stop() @@ -82,11 +93,13 @@ func TestMConnectionSendFlushStop(t *testing.T) { } func TestMConnectionSend(t *testing.T) { + t.Parallel() + server, client := NetPipe() defer server.Close() //nolint: errcheck defer client.Close() //nolint: errcheck - mconn := createTestMConnection(client) + mconn := createTestMConnection(t, client) err := mconn.Start() require.Nil(t, err) defer mconn.Stop() @@ -113,6 +126,8 @@ func TestMConnectionSend(t *testing.T) { } func TestMConnectionReceive(t *testing.T) { + t.Parallel() + server, client := NetPipe() defer server.Close() //nolint: errcheck defer client.Close() //nolint: errcheck @@ -125,12 +140,12 @@ func TestMConnectionReceive(t *testing.T) { onError := func(r interface{}) { errorsCh <- r } - mconn1 := createMConnectionWithCallbacks(client, onReceive, onError) + mconn1 := createMConnectionWithCallbacks(t, client, onReceive, onError) err := mconn1.Start() require.Nil(t, err) defer mconn1.Stop() - mconn2 := createTestMConnection(server) + mconn2 := createTestMConnection(t, server) err = mconn2.Start() require.Nil(t, err) defer mconn2.Stop() @@ -149,11 +164,13 @@ func TestMConnectionReceive(t *testing.T) { } func TestMConnectionStatus(t *testing.T) { + t.Parallel() + server, client := NetPipe() defer server.Close() //nolint: errcheck defer client.Close() //nolint: errcheck - mconn := createTestMConnection(client) + mconn := createTestMConnection(t, client) err := mconn.Start() require.Nil(t, err) defer mconn.Stop() @@ -164,6 +181,8 @@ func TestMConnectionStatus(t *testing.T) { } func TestMConnectionPongTimeoutResultsInError(t *testing.T) { + t.Parallel() + server, client := net.Pipe() defer server.Close() defer client.Close() @@ -176,7 +195,7 @@ func TestMConnectionPongTimeoutResultsInError(t *testing.T) { onError := func(r interface{}) { errorsCh <- r } - mconn := createMConnectionWithCallbacks(client, onReceive, onError) + mconn := createMConnectionWithCallbacks(t, client, onReceive, onError) err := mconn.Start() require.Nil(t, err) defer mconn.Stop() @@ -203,6 +222,8 @@ func TestMConnectionPongTimeoutResultsInError(t *testing.T) { } func TestMConnectionMultiplePongsInTheBeginning(t *testing.T) { + t.Parallel() + server, client := net.Pipe() defer server.Close() defer client.Close() @@ -215,7 +236,7 @@ func TestMConnectionMultiplePongsInTheBeginning(t *testing.T) { onError := func(r interface{}) { errorsCh <- r } - mconn := createMConnectionWithCallbacks(client, onReceive, onError) + mconn := createMConnectionWithCallbacks(t, client, onReceive, onError) err := mconn.Start() require.Nil(t, err) defer mconn.Stop() @@ -256,6 +277,8 @@ func TestMConnectionMultiplePongsInTheBeginning(t *testing.T) { } func TestMConnectionMultiplePings(t *testing.T) { + t.Parallel() + server, client := net.Pipe() defer server.Close() defer client.Close() @@ -268,7 +291,7 @@ func TestMConnectionMultiplePings(t *testing.T) { onError := func(r interface{}) { errorsCh <- r } - mconn := createMConnectionWithCallbacks(client, onReceive, onError) + mconn := createMConnectionWithCallbacks(t, client, onReceive, onError) err := mconn.Start() require.Nil(t, err) defer mconn.Stop() @@ -293,6 +316,8 @@ func TestMConnectionMultiplePings(t *testing.T) { } func TestMConnectionPingPongs(t *testing.T) { + t.Parallel() + // check that we are not leaking any go-routines defer leaktest.CheckTimeout(t, 10*time.Second)() @@ -309,7 +334,7 @@ func TestMConnectionPingPongs(t *testing.T) { onError := func(r interface{}) { errorsCh <- r } - mconn := createMConnectionWithCallbacks(client, onReceive, onError) + mconn := createMConnectionWithCallbacks(t, client, onReceive, onError) err := mconn.Start() require.Nil(t, err) defer mconn.Stop() @@ -348,6 +373,8 @@ func TestMConnectionPingPongs(t *testing.T) { } func TestMConnectionStopsAndReturnsError(t *testing.T) { + t.Parallel() + server, client := NetPipe() defer server.Close() //nolint: errcheck defer client.Close() //nolint: errcheck @@ -360,7 +387,7 @@ func TestMConnectionStopsAndReturnsError(t *testing.T) { onError := func(r interface{}) { errorsCh <- r } - mconn := createMConnectionWithCallbacks(client, onReceive, onError) + mconn := createMConnectionWithCallbacks(t, client, onReceive, onError) err := mconn.Start() require.Nil(t, err) defer mconn.Stop() @@ -394,17 +421,17 @@ func newClientAndServerConnsForReadErrors(t *testing.T, chOnErr chan struct{}) ( {ID: 0x02, Priority: 1, SendQueueCapacity: 1}, } mconnClient := NewMConnection(client, chDescs, onReceive, onError) - mconnClient.SetLogger(log.TestingLogger().With("module", "client")) + mconnClient.SetLogger(log.NewNoopLogger().With("module", "client")) err := mconnClient.Start() require.Nil(t, err) // create server conn with 1 channel // it fires on chOnErr when there's an error - serverLogger := log.TestingLogger().With("module", "server") + serverLogger := log.NewNoopLogger().With("module", "server") onError = func(r interface{}) { chOnErr <- struct{}{} } - mconnServer := createMConnectionWithCallbacks(server, onReceive, onError) + mconnServer := createMConnectionWithCallbacks(t, server, onReceive, onError) mconnServer.SetLogger(serverLogger) err = mconnServer.Start() require.Nil(t, err) @@ -422,6 +449,8 @@ func expectSend(ch chan struct{}) bool { } func TestMConnectionReadErrorBadEncoding(t *testing.T) { + t.Parallel() + chOnErr := make(chan struct{}) mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr) defer mconnClient.Stop() @@ -440,6 +469,8 @@ func TestMConnectionReadErrorBadEncoding(t *testing.T) { } func TestMConnectionReadErrorUnknownChannel(t *testing.T) { + t.Parallel() + chOnErr := make(chan struct{}) mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr) defer mconnClient.Stop() @@ -457,6 +488,8 @@ func TestMConnectionReadErrorUnknownChannel(t *testing.T) { } func TestMConnectionReadErrorLongMessage(t *testing.T) { + t.Parallel() + chOnErr := make(chan struct{}) chOnRcv := make(chan struct{}) @@ -499,6 +532,8 @@ func TestMConnectionReadErrorLongMessage(t *testing.T) { } func TestMConnectionReadErrorUnknownMsgType(t *testing.T) { + t.Parallel() + chOnErr := make(chan struct{}) mconnClient, mconnServer := newClientAndServerConnsForReadErrors(t, chOnErr) defer mconnClient.Stop() @@ -513,11 +548,13 @@ func TestMConnectionReadErrorUnknownMsgType(t *testing.T) { } func TestMConnectionTrySend(t *testing.T) { + t.Parallel() + server, client := NetPipe() defer server.Close() defer client.Close() - mconn := createTestMConnection(client) + mconn := createTestMConnection(t, client) err := mconn.Start() require.Nil(t, err) defer mconn.Stop() diff --git a/tm2/pkg/p2p/conn/secret_connection_test.go b/tm2/pkg/p2p/conn/secret_connection_test.go index 521f651e78b..b238297ae5e 100644 --- a/tm2/pkg/p2p/conn/secret_connection_test.go +++ b/tm2/pkg/p2p/conn/secret_connection_test.go @@ -98,6 +98,8 @@ func makeSecretConnPair(tb testing.TB) (fooSecConn, barSecConn *SecretConnection } func TestSecretConnectionHandshake(t *testing.T) { + t.Parallel() + fooSecConn, barSecConn := makeSecretConnPair(t) if err := fooSecConn.Close(); err != nil { t.Error(err) @@ -110,6 +112,8 @@ func TestSecretConnectionHandshake(t *testing.T) { // Test that shareEphPubKey rejects lower order public keys based on an // (incomplete) blacklist. func TestShareLowOrderPubkey(t *testing.T) { + t.Parallel() + fooConn, barConn := makeKVStoreConnPair() defer fooConn.Close() defer barConn.Close() @@ -141,6 +145,8 @@ func TestShareLowOrderPubkey(t *testing.T) { // Test that additionally that the Diffie-Hellman shared secret is non-zero. // The shared secret would be zero for lower order pub-keys (but tested against the blacklist only). func TestComputeDHFailsOnLowOrder(t *testing.T) { + t.Parallel() + _, locPrivKey := genEphKeys() for _, remLowOrderPubKey := range blacklist { remLowOrderPubKey := remLowOrderPubKey @@ -153,6 +159,8 @@ func TestComputeDHFailsOnLowOrder(t *testing.T) { } func TestConcurrentWrite(t *testing.T) { + t.Parallel() + fooSecConn, barSecConn := makeSecretConnPair(t) fooWriteText := random.RandStr(dataMaxSize) @@ -175,6 +183,8 @@ func TestConcurrentWrite(t *testing.T) { } func TestConcurrentRead(t *testing.T) { + t.Parallel() + fooSecConn, barSecConn := makeSecretConnPair(t) fooWriteText := random.RandStr(dataMaxSize) n := 100 @@ -221,6 +231,8 @@ func readLots(t *testing.T, wg *sync.WaitGroup, conn net.Conn, n int) { } func TestSecretConnectionReadWrite(t *testing.T) { + t.Parallel() + fooConn, barConn := makeKVStoreConnPair() fooWrites, barWrites := []string{}, []string{} fooReads, barReads := []string{}, []string{} @@ -234,7 +246,7 @@ func TestSecretConnectionReadWrite(t *testing.T) { // A helper that will run with (fooConn, fooWrites, fooReads) and vice versa genNodeRunner := func(id string, nodeConn kvstoreConn, nodeWrites []string, nodeReads *[]string) async.Task { return func(_ int) (interface{}, error, bool) { - // Initiate cryptographic private key and secret connection trhough nodeConn. + // Initiate cryptographic private key and secret connection through nodeConn. nodePrvKey := ed25519.GenPrivKey() nodeSecretConn, err := MakeSecretConnection(nodeConn, nodePrvKey) if err != nil { @@ -341,6 +353,8 @@ func TestSecretConnectionReadWrite(t *testing.T) { var update = flag.Bool("update", false, "update .golden files") func TestDeriveSecretsAndChallengeGolden(t *testing.T) { + t.Parallel() + goldenFilepath := filepath.Join("testdata", t.Name()+".golden") if *update { t.Logf("Updating golden test vector file %s", goldenFilepath) @@ -386,6 +400,8 @@ func (pk privKeyWithNilPubKey) PubKey() crypto.PubKey { return nil } func (pk privKeyWithNilPubKey) Equals(pk2 crypto.PrivKey) bool { return pk.orig.Equals(pk2) } func TestNilPubkey(t *testing.T) { + t.Parallel() + fooConn, barConn := makeKVStoreConnPair() fooPrvKey := ed25519.GenPrivKey() barPrvKey := privKeyWithNilPubKey{ed25519.GenPrivKey()} @@ -404,6 +420,8 @@ func TestNilPubkey(t *testing.T) { } func TestNonEd25519Pubkey(t *testing.T) { + t.Parallel() + fooConn, barConn := makeKVStoreConnPair() fooPrvKey := ed25519.GenPrivKey() barPrvKey := secp256k1.GenPrivKey() diff --git a/tm2/pkg/p2p/key_test.go b/tm2/pkg/p2p/key_test.go index 8046f58eb25..4f67cc0a5da 100644 --- a/tm2/pkg/p2p/key_test.go +++ b/tm2/pkg/p2p/key_test.go @@ -11,6 +11,8 @@ import ( ) func TestLoadOrGenNodeKey(t *testing.T) { + t.Parallel() + filePath := filepath.Join(os.TempDir(), random.RandStr(12)+"_peer_id.json") nodeKey, err := LoadOrGenNodeKey(filePath) @@ -22,13 +24,15 @@ func TestLoadOrGenNodeKey(t *testing.T) { assert.Equal(t, nodeKey, nodeKey2) } -//---------------------------------------------------------- +// ---------------------------------------------------------- func padBytes(bz []byte, targetBytes int) []byte { return append(bz, bytes.Repeat([]byte{0xFF}, targetBytes-len(bz))...) } func TestPoWTarget(t *testing.T) { + t.Parallel() + targetBytes := 20 cases := []struct { difficulty uint diff --git a/tm2/pkg/p2p/mock/reactor.go b/tm2/pkg/p2p/mock/reactor.go index 335366920db..fe123fdc0b2 100644 --- a/tm2/pkg/p2p/mock/reactor.go +++ b/tm2/pkg/p2p/mock/reactor.go @@ -13,7 +13,7 @@ type Reactor struct { func NewReactor() *Reactor { r := &Reactor{} r.BaseReactor = *p2p.NewBaseReactor("Reactor", r) - r.SetLogger(log.TestingLogger()) + r.SetLogger(log.NewNoopLogger()) return r } diff --git a/tm2/pkg/p2p/netaddress_test.go b/tm2/pkg/p2p/netaddress_test.go index a5dc3fca375..413d020c153 100644 --- a/tm2/pkg/p2p/netaddress_test.go +++ b/tm2/pkg/p2p/netaddress_test.go @@ -11,6 +11,8 @@ import ( ) func TestAddress2ID(t *testing.T) { + t.Parallel() + idbz, _ := hex.DecodeString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") id := crypto.AddressFromBytes(idbz).ID() assert.Equal(t, crypto.ID("g1m6kmam774klwlh4dhmhaatd7al02m0h0jwnyc6"), id) @@ -21,6 +23,8 @@ func TestAddress2ID(t *testing.T) { } func TestNewNetAddress(t *testing.T) { + t.Parallel() + tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8080") require.Nil(t, err) @@ -41,6 +45,8 @@ func TestNewNetAddress(t *testing.T) { } func TestNewNetAddressFromString(t *testing.T) { + t.Parallel() + testCases := []struct { name string addr string @@ -85,6 +91,8 @@ func TestNewNetAddressFromString(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() + addr, err := NewNetAddressFromString(tc.addr) if tc.correct { if assert.Nil(t, err, tc.addr) { @@ -98,6 +106,8 @@ func TestNewNetAddressFromString(t *testing.T) { } func TestNewNetAddressFromStrings(t *testing.T) { + t.Parallel() + addrs, errs := NewNetAddressFromStrings([]string{ "127.0.0.1:8080", "g1m6kmam774klwlh4dhmhaatd7al02m0h0jwnyc6@127.0.0.1:8080", @@ -108,11 +118,15 @@ func TestNewNetAddressFromStrings(t *testing.T) { } func TestNewNetAddressFromIPPort(t *testing.T) { + t.Parallel() + addr := NewNetAddressFromIPPort("", net.ParseIP("127.0.0.1"), 8080) assert.Equal(t, "127.0.0.1:8080", addr.String()) } func TestNetAddressProperties(t *testing.T) { + t.Parallel() + // TODO add more test cases testCases := []struct { addr string @@ -140,6 +154,8 @@ func TestNetAddressProperties(t *testing.T) { } func TestNetAddressReachabilityTo(t *testing.T) { + t.Parallel() + // TODO add more test cases testCases := []struct { addr string diff --git a/tm2/pkg/p2p/node_info.go b/tm2/pkg/p2p/node_info.go index 9653a83c38a..48ba8f7776b 100644 --- a/tm2/pkg/p2p/node_info.go +++ b/tm2/pkg/p2p/node_info.go @@ -3,6 +3,7 @@ package p2p import ( "fmt" + "github.com/gnolang/gno/tm2/pkg/bft/state/eventstore" "github.com/gnolang/gno/tm2/pkg/strings" "github.com/gnolang/gno/tm2/pkg/versionset" ) @@ -100,7 +101,7 @@ func (info NodeInfo) Validate() error { other := info.Other txIndex := other.TxIndex switch txIndex { - case "", "on", "off": + case "", eventstore.StatusOn, eventstore.StatusOff: default: return fmt.Errorf("info.Other.TxIndex should be either 'on', 'off', or empty string, got '%v'", txIndex) } diff --git a/tm2/pkg/p2p/node_info_test.go b/tm2/pkg/p2p/node_info_test.go index ea97239643f..58f1dab8854 100644 --- a/tm2/pkg/p2p/node_info_test.go +++ b/tm2/pkg/p2p/node_info_test.go @@ -11,6 +11,8 @@ import ( ) func TestNodeInfoValidate(t *testing.T) { + t.Parallel() + // empty fails ni := NodeInfo{} assert.Error(t, ni.Validate()) @@ -86,6 +88,8 @@ func TestNodeInfoValidate(t *testing.T) { } func TestNodeInfoCompatible(t *testing.T) { + t.Parallel() + nodeKey1 := NodeKey{PrivKey: ed25519.GenPrivKey()} nodeKey2 := NodeKey{PrivKey: ed25519.GenPrivKey()} name := "testing" diff --git a/tm2/pkg/p2p/peer.go b/tm2/pkg/p2p/peer.go index c9eea1eca65..11814b585c1 100644 --- a/tm2/pkg/p2p/peer.go +++ b/tm2/pkg/p2p/peer.go @@ -4,8 +4,9 @@ import ( "fmt" "net" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/cmap" - "github.com/gnolang/gno/tm2/pkg/log" connm "github.com/gnolang/gno/tm2/pkg/p2p/conn" "github.com/gnolang/gno/tm2/pkg/service" ) @@ -156,7 +157,7 @@ func (p *peer) String() string { // Implements service.Service // SetLogger implements BaseService. -func (p *peer) SetLogger(l log.Logger) { +func (p *peer) SetLogger(l *slog.Logger) { p.Logger = l p.mconn.SetLogger(l) } diff --git a/tm2/pkg/p2p/peer_test.go b/tm2/pkg/p2p/peer_test.go index 070d783d2af..28217c4486e 100644 --- a/tm2/pkg/p2p/peer_test.go +++ b/tm2/pkg/p2p/peer_test.go @@ -19,6 +19,8 @@ import ( ) func TestPeerBasic(t *testing.T) { + t.Parallel() + assert, require := assert.New(t), require.New(t) // simulate remote peer @@ -26,7 +28,7 @@ func TestPeerBasic(t *testing.T) { rp.Start() defer rp.Stop() - p, err := createOutboundPeerAndPerformHandshake(rp.Addr(), cfg, conn.DefaultMConnConfig()) + p, err := createOutboundPeerAndPerformHandshake(t, rp.Addr(), cfg, conn.DefaultMConnConfig()) require.Nil(err) err = p.Start() @@ -43,6 +45,8 @@ func TestPeerBasic(t *testing.T) { } func TestPeerSend(t *testing.T) { + t.Parallel() + assert, require := assert.New(t), require.New(t) config := cfg @@ -52,7 +56,7 @@ func TestPeerSend(t *testing.T) { rp.Start() defer rp.Stop() - p, err := createOutboundPeerAndPerformHandshake(rp.Addr(), config, conn.DefaultMConnConfig()) + p, err := createOutboundPeerAndPerformHandshake(t, rp.Addr(), config, conn.DefaultMConnConfig()) require.Nil(err) err = p.Start() @@ -65,10 +69,13 @@ func TestPeerSend(t *testing.T) { } func createOutboundPeerAndPerformHandshake( + t *testing.T, addr *NetAddress, config *config.P2PConfig, mConfig conn.MConnConfig, ) (*peer, error) { + t.Helper() + chDescs := []*conn.ChannelDescriptor{ {ID: testCh, Priority: 1}, } @@ -86,7 +93,7 @@ func createOutboundPeerAndPerformHandshake( } p := newPeer(pc, mConfig, peerNodeInfo, reactorsByCh, chDescs, func(p Peer, r interface{}) {}) - p.SetLogger(log.TestingLogger().With("peer", addr)) + p.SetLogger(log.NewTestingLogger(t).With("peer", addr)) return p, nil } diff --git a/tm2/pkg/p2p/switch.go b/tm2/pkg/p2p/switch.go index 1f693544d5a..368de659fb1 100644 --- a/tm2/pkg/p2p/switch.go +++ b/tm2/pkg/p2p/switch.go @@ -387,10 +387,6 @@ func (sw *Switch) reconnectToPeer(addr *NetAddress) { // --------------------------------------------------------------------- // Dialing -type privateAddr interface { - PrivateAddr() bool -} - // DialPeersAsync dials a list of peers asynchronously in random order. // Used to dial peers from config on startup or from unsafe-RPC (trusted sources). // It ignores NetAddressLookupError. However, if there are other errors, first diff --git a/tm2/pkg/p2p/switch_test.go b/tm2/pkg/p2p/switch_test.go index a0d4853493b..a7033b466fe 100644 --- a/tm2/pkg/p2p/switch_test.go +++ b/tm2/pkg/p2p/switch_test.go @@ -52,7 +52,7 @@ func NewTestReactor(channels []*conn.ChannelDescriptor, logMessages bool) *TestR msgsReceived: make(map[byte][]PeerMessage), } tr.BaseReactor = *NewBaseReactor("TestReactor", tr) - tr.SetLogger(log.TestingLogger()) + tr.SetLogger(log.NewNoopLogger()) return tr } @@ -105,6 +105,8 @@ func initSwitchFunc(i int, sw *Switch) *Switch { } func TestSwitches(t *testing.T) { + t.Parallel() + s1, s2 := MakeSwitchPair(t, initSwitchFunc) defer s1.Stop() defer s2.Stop() @@ -152,6 +154,8 @@ func assertMsgReceivedWithTimeout(t *testing.T, msgBytes []byte, channel byte, r } func TestSwitchFiltersOutItself(t *testing.T) { + t.Parallel() + s1 := MakeSwitch(cfg, 1, "127.0.0.1", "123.123.123", initSwitchFunc) // simulate s1 having a public IP by creating a remote peer with the same ID @@ -176,6 +180,8 @@ func TestSwitchFiltersOutItself(t *testing.T) { } func TestSwitchPeerFilter(t *testing.T) { + t.Parallel() + var ( filters = []PeerFilterFunc{ func(_ IPeerSet, _ Peer) error { return nil }, @@ -219,6 +225,8 @@ func TestSwitchPeerFilter(t *testing.T) { } func TestSwitchPeerFilterTimeout(t *testing.T) { + t.Parallel() + var ( filters = []PeerFilterFunc{ func(_ IPeerSet, _ Peer) error { @@ -260,6 +268,8 @@ func TestSwitchPeerFilterTimeout(t *testing.T) { } func TestSwitchPeerFilterDuplicate(t *testing.T) { + t.Parallel() + sw := MakeSwitch(cfg, 1, "testing", "123.123.123", initSwitchFunc) sw.Start() defer sw.Stop() @@ -303,6 +313,8 @@ func assertNoPeersAfterTimeout(t *testing.T, sw *Switch, timeout time.Duration) } func TestSwitchStopsNonPersistentPeerOnError(t *testing.T) { + t.Parallel() + assert, require := assert.New(t), require.New(t) sw := MakeSwitch(cfg, 1, "testing", "123.123.123", initSwitchFunc) @@ -338,6 +350,8 @@ func TestSwitchStopsNonPersistentPeerOnError(t *testing.T) { } func TestSwitchStopPeerForError(t *testing.T) { + t.Parallel() + // make two connected switches sw1, sw2 := MakeSwitchPair(t, func(i int, sw *Switch) *Switch { return initSwitchFunc(i, sw) @@ -360,6 +374,8 @@ func TestSwitchStopPeerForError(t *testing.T) { } func TestSwitchReconnectsToOutboundPersistentPeer(t *testing.T) { + t.Parallel() + sw := MakeSwitch(cfg, 1, "testing", "123.123.123", initSwitchFunc) err := sw.Start() require.NoError(t, err) @@ -405,6 +421,8 @@ func TestSwitchReconnectsToOutboundPersistentPeer(t *testing.T) { } func TestSwitchReconnectsToInboundPersistentPeer(t *testing.T) { + t.Parallel() + sw := MakeSwitch(cfg, 1, "testing", "123.123.123", initSwitchFunc) err := sw.Start() require.NoError(t, err) @@ -430,6 +448,8 @@ func TestSwitchReconnectsToInboundPersistentPeer(t *testing.T) { } func TestSwitchDialPeersAsync(t *testing.T) { + t.Parallel() + if testing.Short() { return } @@ -460,6 +480,8 @@ func waitUntilSwitchHasAtLeastNPeers(sw *Switch, n int) { } func TestSwitchFullConnectivity(t *testing.T) { + t.Parallel() + switches := MakeConnectedSwitches(cfg, 3, initSwitchFunc, Connect2Switches) defer func() { for _, sw := range switches { @@ -475,6 +497,8 @@ func TestSwitchFullConnectivity(t *testing.T) { } func TestSwitchAcceptRoutine(t *testing.T) { + t.Parallel() + cfg.MaxNumInboundPeers = 5 // make switch @@ -547,6 +571,8 @@ func (errorTransport) Cleanup(Peer) { } func TestSwitchAcceptRoutineErrorCases(t *testing.T) { + t.Parallel() + sw := NewSwitch(cfg, errorTransport{FilterTimeoutError{}}) assert.NotPanics(t, func() { err := sw.Start() @@ -599,6 +625,8 @@ func (r *mockReactor) InitCalledBeforeRemoveFinished() bool { // see stopAndRemovePeer func TestFlappySwitchInitPeerIsNotCalledBeforeRemovePeer(t *testing.T) { + t.Parallel() + testutils.FilterStability(t, testutils.Flappy) // make reactor diff --git a/tm2/pkg/p2p/test_util.go b/tm2/pkg/p2p/test_util.go index 7ac46a07266..3e7abbc6c6f 100644 --- a/tm2/pkg/p2p/test_util.go +++ b/tm2/pkg/p2p/test_util.go @@ -17,7 +17,7 @@ import ( const testCh = 0x01 -//------------------------------------------------ +// ------------------------------------------------ func AddPeerToSwitchPeerSet(sw *Switch, peer Peer) { sw.peers.Add(peer) @@ -33,7 +33,7 @@ func CreateRandomPeer(outbound bool) *peer { nodeInfo: NodeInfo{NetAddress: netAddr}, mconn: &conn.MConnection{}, } - p.SetLogger(log.TestingLogger().With("peer", addr)) + p.SetLogger(log.NewNoopLogger().With("peer", addr)) return p } @@ -53,7 +53,7 @@ func CreateRoutableAddr() (addr string, netAddr *NetAddress) { return } -//------------------------------------------------------------------ +// ------------------------------------------------------------------ // Connects switches via arbitrary net.Conn. Used for testing. const TEST_HOST = "localhost" @@ -175,7 +175,7 @@ func MakeSwitch( // TODO: let the config be passed in? sw := initSwitch(i, NewSwitch(cfg, t, opts...)) - sw.SetLogger(log.TestingLogger().With("switch", i)) + sw.SetLogger(log.NewNoopLogger().With("switch", i)) sw.SetNodeKey(&nodeKey) for ch := range sw.reactorsByCh { @@ -223,7 +223,7 @@ func testPeerConn( return newPeerConn(outbound, persistent, conn, socketAddr), nil } -//---------------------------------------------------------------- +// ---------------------------------------------------------------- // rand node info func testNodeInfo(id ID, name string) NodeInfo { diff --git a/tm2/pkg/p2p/transport_test.go b/tm2/pkg/p2p/transport_test.go index 49ab9ac52e3..63b1c26e666 100644 --- a/tm2/pkg/p2p/transport_test.go +++ b/tm2/pkg/p2p/transport_test.go @@ -33,6 +33,8 @@ func newMultiplexTransport( } func TestTransportMultiplexConnFilter(t *testing.T) { + t.Parallel() + mt := newMultiplexTransport( emptyNodeInfo(), NodeKey{ @@ -87,6 +89,8 @@ func TestTransportMultiplexConnFilter(t *testing.T) { } func TestTransportMultiplexConnFilterTimeout(t *testing.T) { + t.Parallel() + mt := newMultiplexTransport( emptyNodeInfo(), NodeKey{ @@ -137,6 +141,8 @@ func TestTransportMultiplexConnFilterTimeout(t *testing.T) { } func TestTransportMultiplexAcceptMultiple(t *testing.T) { + t.Parallel() + mt := testSetupMultiplexTransport(t) laddr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr()) @@ -212,6 +218,8 @@ func testDialer(dialAddr NetAddress, errc chan error) { } func TestFlappyTransportMultiplexAcceptNonBlocking(t *testing.T) { + t.Parallel() + testutils.FilterStability(t, testutils.Flappy) mt := testSetupMultiplexTransport(t) @@ -298,6 +306,8 @@ func TestFlappyTransportMultiplexAcceptNonBlocking(t *testing.T) { } func TestTransportMultiplexValidateNodeInfo(t *testing.T) { + t.Parallel() + mt := testSetupMultiplexTransport(t) errc := make(chan error) @@ -339,6 +349,8 @@ func TestTransportMultiplexValidateNodeInfo(t *testing.T) { } func TestTransportMultiplexRejectMismatchID(t *testing.T) { + t.Parallel() + mt := testSetupMultiplexTransport(t) errc := make(chan error) @@ -378,6 +390,8 @@ func TestTransportMultiplexRejectMismatchID(t *testing.T) { } func TestTransportMultiplexDialRejectWrongID(t *testing.T) { + t.Parallel() + mt := testSetupMultiplexTransport(t) var ( @@ -407,6 +421,8 @@ func TestTransportMultiplexDialRejectWrongID(t *testing.T) { } func TestTransportMultiplexRejectIncompatible(t *testing.T) { + t.Parallel() + mt := testSetupMultiplexTransport(t) errc := make(chan error) @@ -443,6 +459,8 @@ func TestTransportMultiplexRejectIncompatible(t *testing.T) { } func TestTransportMultiplexRejectSelf(t *testing.T) { + t.Parallel() + mt := testSetupMultiplexTransport(t) errc := make(chan error) @@ -482,6 +500,8 @@ func TestTransportMultiplexRejectSelf(t *testing.T) { } func TestTransportConnDuplicateIPFilter(t *testing.T) { + t.Parallel() + filter := ConnDuplicateIPFilter() if err := filter(nil, &testTransportConn{}, nil); err != nil { @@ -507,6 +527,8 @@ func TestTransportConnDuplicateIPFilter(t *testing.T) { } func TestTransportHandshake(t *testing.T) { + t.Parallel() + ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) diff --git a/tm2/pkg/p2p/upnp/probe.go b/tm2/pkg/p2p/upnp/probe.go index 6a2c1d8bf7d..29498124aa7 100644 --- a/tm2/pkg/p2p/upnp/probe.go +++ b/tm2/pkg/p2p/upnp/probe.go @@ -5,7 +5,7 @@ import ( "net" "time" - "github.com/gnolang/gno/tm2/pkg/log" + "golang.org/x/exp/slog" ) type UPNPCapabilities struct { @@ -13,7 +13,7 @@ type UPNPCapabilities struct { Hairpin bool } -func makeUPNPListener(intPort int, extPort int, logger log.Logger) (NAT, net.Listener, net.IP, error) { +func makeUPNPListener(intPort int, extPort int, logger *slog.Logger) (NAT, net.Listener, net.IP, error) { nat, err := Discover() if err != nil { return nil, nil, nil, fmt.Errorf("NAT upnp could not be discovered: %w", err) @@ -40,7 +40,7 @@ func makeUPNPListener(intPort int, extPort int, logger log.Logger) (NAT, net.Lis return nat, listener, ext, nil } -func testHairpin(listener net.Listener, extAddr string, logger log.Logger) (supportsHairpin bool) { +func testHairpin(listener net.Listener, extAddr string, logger *slog.Logger) (supportsHairpin bool) { // Listener go func() { inConn, err := listener.Accept() @@ -81,7 +81,7 @@ func testHairpin(listener net.Listener, extAddr string, logger log.Logger) (supp return supportsHairpin } -func Probe(logger log.Logger) (caps UPNPCapabilities, err error) { +func Probe(logger *slog.Logger) (caps UPNPCapabilities, err error) { logger.Info("Probing for UPnP!") intPort, extPort := 8001, 8001 diff --git a/tm2/pkg/p2p/upnp/upnp.go b/tm2/pkg/p2p/upnp/upnp.go index 40f2067e232..cd47ac35553 100644 --- a/tm2/pkg/p2p/upnp/upnp.go +++ b/tm2/pkg/p2p/upnp/upnp.go @@ -10,7 +10,7 @@ import ( "encoding/xml" "errors" "fmt" - "io/ioutil" + "io" "net" "net/http" "strconv" @@ -306,7 +306,7 @@ func (n *upnpNAT) getExternalIPAddress() (info statusInfo, err error) { return } var envelope Envelope - data, err := ioutil.ReadAll(response.Body) + data, err := io.ReadAll(response.Body) if err != nil { return } @@ -363,7 +363,7 @@ func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int // TODO: check response to see if the port was forwarded // log.Println(message, response) // JAE: - // body, err := ioutil.ReadAll(response.Body) + // body, err := io.ReadAll(response.Body) // fmt.Println(string(body), err) mappedExternalPort = externalPort _ = response diff --git a/tm2/pkg/random/random_test.go b/tm2/pkg/random/random_test.go index e2d45533626..71e05e95e16 100644 --- a/tm2/pkg/random/random_test.go +++ b/tm2/pkg/random/random_test.go @@ -15,18 +15,24 @@ import ( ) func TestRandStr(t *testing.T) { + t.Parallel() + l := 243 s := RandStr(l) assert.Equal(t, l, len(s)) } func TestRandBytes(t *testing.T) { + t.Parallel() + l := 243 b := RandBytes(l) assert.Equal(t, l, len(b)) } func TestRandIntn(t *testing.T) { + t.Parallel() + n := 243 for i := 0; i < 100; i++ { x := RandIntn(n) @@ -76,6 +82,8 @@ func testThemAll() string { } func TestRngConcurrencySafety(t *testing.T) { + t.Parallel() + var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) diff --git a/tm2/pkg/sdk/auth/ante.go b/tm2/pkg/sdk/auth/ante.go index c00a98df4d3..6abc4380e89 100644 --- a/tm2/pkg/sdk/auth/ante.go +++ b/tm2/pkg/sdk/auth/ante.go @@ -238,7 +238,7 @@ func processSig( } if !simulate && !pubKey.VerifyBytes(signBytes, sig.Signature) { - return nil, abciResult(std.ErrUnauthorized("signature verification failed; verify correct account sequence and chain-id")) + return nil, abciResult(std.ErrUnauthorized("signature verification failed; verify correct account, sequence, and chain-id")) } if err := acc.SetSequence(acc.GetSequence() + 1); err != nil { diff --git a/tm2/pkg/sdk/auth/ante_test.go b/tm2/pkg/sdk/auth/ante_test.go index 2dc3ec08176..9c15cca18a2 100644 --- a/tm2/pkg/sdk/auth/ante_test.go +++ b/tm2/pkg/sdk/auth/ante_test.go @@ -59,6 +59,8 @@ func defaultAnteOptions() AnteOptions { // Test various error cases in the AnteHandler control flow. func TestAnteHandlerSigErrors(t *testing.T) { + t.Parallel() + // setup env := setupTestEnv() ctx := env.ctx @@ -107,6 +109,8 @@ func TestAnteHandlerSigErrors(t *testing.T) { // Test logic around account number checking with one signer and many signers. func TestAnteHandlerAccountNumbers(t *testing.T) { + t.Parallel() + // setup env := setupTestEnv() anteHandler := NewAnteHandler(env.acck, env.bank, DefaultSigVerificationGasConsumer, defaultAnteOptions()) @@ -164,6 +168,8 @@ func TestAnteHandlerAccountNumbers(t *testing.T) { // Test logic around account number checking with many signers when BlockHeight is 0. func TestAnteHandlerAccountNumbersAtBlockHeightZero(t *testing.T) { + t.Parallel() + // setup env := setupTestEnv() anteHandler := NewAnteHandler(env.acck, env.bank, DefaultSigVerificationGasConsumer, defaultAnteOptions()) @@ -223,6 +229,8 @@ func TestAnteHandlerAccountNumbersAtBlockHeightZero(t *testing.T) { // Test logic around sequence checking with one signer and many signers. func TestAnteHandlerSequences(t *testing.T) { + t.Parallel() + // setup env := setupTestEnv() anteHandler := NewAnteHandler(env.acck, env.bank, DefaultSigVerificationGasConsumer, defaultAnteOptions()) @@ -300,6 +308,8 @@ func TestAnteHandlerSequences(t *testing.T) { // Test logic around fee deduction. func TestAnteHandlerFees(t *testing.T) { + t.Parallel() + // setup env := setupTestEnv() ctx := env.ctx @@ -341,6 +351,8 @@ func TestAnteHandlerFees(t *testing.T) { // Test logic around memo gas consumption. func TestAnteHandlerMemoGas(t *testing.T) { + t.Parallel() + // setup env := setupTestEnv() anteHandler := NewAnteHandler(env.acck, env.bank, DefaultSigVerificationGasConsumer, defaultAnteOptions()) @@ -381,6 +393,8 @@ func TestAnteHandlerMemoGas(t *testing.T) { } func TestAnteHandlerMultiSigner(t *testing.T) { + t.Parallel() + // setup env := setupTestEnv() anteHandler := NewAnteHandler(env.acck, env.bank, DefaultSigVerificationGasConsumer, defaultAnteOptions()) @@ -431,6 +445,8 @@ func TestAnteHandlerMultiSigner(t *testing.T) { } func TestAnteHandlerBadSignBytes(t *testing.T) { + t.Parallel() + // setup env := setupTestEnv() anteHandler := NewAnteHandler(env.acck, env.bank, DefaultSigVerificationGasConsumer, defaultAnteOptions()) @@ -508,6 +524,8 @@ func TestAnteHandlerBadSignBytes(t *testing.T) { } func TestAnteHandlerSetPubKey(t *testing.T) { + t.Parallel() + // setup env := setupTestEnv() anteHandler := NewAnteHandler(env.acck, env.bank, DefaultSigVerificationGasConsumer, defaultAnteOptions()) @@ -560,6 +578,8 @@ func TestAnteHandlerSetPubKey(t *testing.T) { } func TestProcessPubKey(t *testing.T) { + t.Parallel() + env := setupTestEnv() ctx := env.ctx @@ -588,7 +608,10 @@ func TestProcessPubKey(t *testing.T) { {"pubkey doesn't match addr, simulate on", args{acc1, std.Signature{PubKey: priv2.PubKey()}, true}, false}, } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() + _, err := ProcessPubKey(tt.args.acc, tt.args.sig, tt.args.simulate) require.Equal(t, tt.wantErr, !err.IsOK()) }) @@ -596,6 +619,8 @@ func TestProcessPubKey(t *testing.T) { } func TestConsumeSignatureVerificationGas(t *testing.T) { + t.Parallel() + params := DefaultParams() msg := []byte{1, 2, 3, 4} @@ -625,7 +650,10 @@ func TestConsumeSignatureVerificationGas(t *testing.T) { {"unknown key", args{store.NewInfiniteGasMeter(), nil, nil, params}, 0, true}, } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() + res := DefaultSigVerificationGasConsumer(tt.args.meter, tt.args.sig, tt.args.pubkey, tt.args.params) if tt.shouldErr { @@ -671,6 +699,8 @@ func expectedGasCostByKeys(pubkeys []crypto.PubKey) int64 { } func TestCountSubkeys(t *testing.T) { + t.Parallel() + genPubKeys := func(n int) []crypto.PubKey { var ret []crypto.PubKey for i := 0; i < n; i++ { @@ -698,13 +728,18 @@ func TestCountSubkeys(t *testing.T) { {"multi level multikey", args{multiLevelMultiKey}, 11}, } for _, tt := range tests { - t.Run(tt.name, func(T *testing.T) { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + require.Equal(t, tt.want, std.CountSubKeys(tt.args.pub)) }) } } func TestAnteHandlerSigLimitExceeded(t *testing.T) { + t.Parallel() + // setup env := setupTestEnv() anteHandler := NewAnteHandler(env.acck, env.bank, DefaultSigVerificationGasConsumer, defaultAnteOptions()) @@ -742,6 +777,8 @@ func TestAnteHandlerSigLimitExceeded(t *testing.T) { } func TestEnsureSufficientMempoolFees(t *testing.T) { + t.Parallel() + // setup env := setupTestEnv() ctx := env.ctx.WithMinGasPrices( @@ -775,6 +812,8 @@ func TestEnsureSufficientMempoolFees(t *testing.T) { // Test custom SignatureVerificationGasConsumer func TestCustomSignatureVerificationGasConsumer(t *testing.T) { + t.Parallel() + // setup env := setupTestEnv() // setup an ante handler that only accepts PubKeyEd25519 diff --git a/tm2/pkg/sdk/auth/keeper.go b/tm2/pkg/sdk/auth/keeper.go index c814eb21d9f..405256c6877 100644 --- a/tm2/pkg/sdk/auth/keeper.go +++ b/tm2/pkg/sdk/auth/keeper.go @@ -3,9 +3,10 @@ package auth import ( "fmt" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/amino" "github.com/gnolang/gno/tm2/pkg/crypto" - "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/sdk" "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/store" @@ -32,7 +33,7 @@ func NewAccountKeeper( } // Logger returns a module-specific logger. -func (ak AccountKeeper) Logger(ctx sdk.Context) log.Logger { +func (ak AccountKeeper) Logger(ctx sdk.Context) *slog.Logger { return ctx.Logger().With("module", fmt.Sprintf("auth")) } diff --git a/tm2/pkg/sdk/auth/keeper_test.go b/tm2/pkg/sdk/auth/keeper_test.go index 3af74cf4a4c..d40d96cdb4b 100644 --- a/tm2/pkg/sdk/auth/keeper_test.go +++ b/tm2/pkg/sdk/auth/keeper_test.go @@ -9,6 +9,8 @@ import ( ) func TestAccountMapperGetSet(t *testing.T) { + t.Parallel() + env := setupTestEnv() addr := crypto.AddressFromPreimage([]byte("some-address")) @@ -38,6 +40,8 @@ func TestAccountMapperGetSet(t *testing.T) { } func TestAccountMapperRemoveAccount(t *testing.T) { + t.Parallel() + env := setupTestEnv() addr1 := crypto.AddressFromPreimage([]byte("addr1")) addr2 := crypto.AddressFromPreimage([]byte("addr2")) diff --git a/tm2/pkg/sdk/auth/test_common.go b/tm2/pkg/sdk/auth/test_common.go index a51c8576bad..13b2ee5c55a 100644 --- a/tm2/pkg/sdk/auth/test_common.go +++ b/tm2/pkg/sdk/auth/test_common.go @@ -19,33 +19,6 @@ type testEnv struct { bank BankKeeperI } -// moduleAccount defines an account for modules that holds coins on a pool -type moduleAccount struct { - *std.BaseAccount - name string `json:"name" yaml:"name"` // name of the module - permissions []string `json:"permissions" yaml"permissions"` // permissions of module account -} - -// HasPermission returns whether or not the module account has permission. -func (ma moduleAccount) HasPermission(permission string) bool { - for _, perm := range ma.permissions { - if perm == permission { - return true - } - } - return false -} - -// GetName returns the the name of the holder's module -func (ma moduleAccount) GetName() string { - return ma.name -} - -// GetPermissions returns permissions granted to the module account -func (ma moduleAccount) GetPermissions() []string { - return ma.permissions -} - func setupTestEnv() testEnv { db := dbm.NewMemDB() @@ -58,7 +31,7 @@ func setupTestEnv() testEnv { acck := NewAccountKeeper(authCapKey, std.ProtoBaseAccount) bank := NewDummyBankKeeper(acck) - ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{Height: 1, ChainID: "test-chain-id"}, log.NewNopLogger()) + ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{Height: 1, ChainID: "test-chain-id"}, log.NewNoopLogger()) ctx = ctx.WithValue(AuthParamsContextKey{}, DefaultParams()) ctx = ctx.WithConsensusParams(&abci.ConsensusParams{ Block: &abci.BlockParams{ diff --git a/tm2/pkg/sdk/bank/bank.proto b/tm2/pkg/sdk/bank/bank.proto index b2a1b0e8249..fd8d6437e63 100644 --- a/tm2/pkg/sdk/bank/bank.proto +++ b/tm2/pkg/sdk/bank/bank.proto @@ -14,7 +14,7 @@ message InputOutputMismatchError { } message MsgSend { - string FromAddress = 1; - string ToAddress = 2; - string Amount = 3; + string from_address = 1; + string to_address = 2; + string amount = 3; } \ No newline at end of file diff --git a/tm2/pkg/sdk/bank/common_test.go b/tm2/pkg/sdk/bank/common_test.go index b78ae702cbb..fcbd5d2cb94 100644 --- a/tm2/pkg/sdk/bank/common_test.go +++ b/tm2/pkg/sdk/bank/common_test.go @@ -29,7 +29,7 @@ func setupTestEnv() testEnv { ms.MountStoreWithDB(authCapKey, iavl.StoreConstructor, db) ms.LoadLatestVersion() - ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{ChainID: "test-chain-id"}, log.NewNopLogger()) + ctx := sdk.NewContext(sdk.RunTxModeDeliver, ms, &bft.Header{ChainID: "test-chain-id"}, log.NewNoopLogger()) acck := auth.NewAccountKeeper( authCapKey, std.ProtoBaseAccount, ) diff --git a/tm2/pkg/sdk/bank/handler_test.go b/tm2/pkg/sdk/bank/handler_test.go index fef4810c027..85fc68fc304 100644 --- a/tm2/pkg/sdk/bank/handler_test.go +++ b/tm2/pkg/sdk/bank/handler_test.go @@ -16,6 +16,8 @@ import ( ) func TestInvalidMsg(t *testing.T) { + t.Parallel() + h := NewHandler(BankKeeper{}) res := h.Process(sdk.NewContext(sdk.RunTxModeDeliver, nil, &bft.Header{ChainID: "test-chain"}, nil), tu.NewTestMsg()) require.False(t, res.IsOK()) @@ -23,6 +25,8 @@ func TestInvalidMsg(t *testing.T) { } func TestBalances(t *testing.T) { + t.Parallel() + env := setupTestEnv() h := NewHandler(env.bank) _, _, addr := tu.KeyTestPubAddr() @@ -51,6 +55,8 @@ func TestBalances(t *testing.T) { } func TestQuerierRouteNotFound(t *testing.T) { + t.Parallel() + env := setupTestEnv() h := NewHandler(env.bank) req := abci.RequestQuery{ diff --git a/tm2/pkg/sdk/bank/keeper.go b/tm2/pkg/sdk/bank/keeper.go index dfbef78f016..87791258108 100644 --- a/tm2/pkg/sdk/bank/keeper.go +++ b/tm2/pkg/sdk/bank/keeper.go @@ -3,8 +3,9 @@ package bank import ( "fmt" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/crypto" - "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/sdk" "github.com/gnolang/gno/tm2/pkg/sdk/auth" "github.com/gnolang/gno/tm2/pkg/std" @@ -178,7 +179,7 @@ func (bank BankKeeper) SetCoins(ctx sdk.Context, addr crypto.Address, amt std.Co return nil } -//---------------------------------------- +// ---------------------------------------- // ViewKeeper // ViewKeeperI defines a module interface that facilitates read only access to @@ -201,7 +202,7 @@ func NewViewKeeper(acck auth.AccountKeeper) ViewKeeper { } // Logger returns a module-specific logger. -func (view ViewKeeper) Logger(ctx sdk.Context) log.Logger { +func (view ViewKeeper) Logger(ctx sdk.Context) *slog.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", ModuleName)) } diff --git a/tm2/pkg/sdk/bank/keeper_test.go b/tm2/pkg/sdk/bank/keeper_test.go index d4e230330d5..59b4c12689c 100644 --- a/tm2/pkg/sdk/bank/keeper_test.go +++ b/tm2/pkg/sdk/bank/keeper_test.go @@ -11,6 +11,8 @@ import ( ) func TestKeeper(t *testing.T) { + t.Parallel() + env := setupTestEnv() ctx := env.ctx @@ -88,6 +90,8 @@ func TestKeeper(t *testing.T) { } func TestBankKeeper(t *testing.T) { + t.Parallel() + env := setupTestEnv() ctx := env.ctx @@ -134,6 +138,8 @@ func TestBankKeeper(t *testing.T) { } func TestViewKeeper(t *testing.T) { + t.Parallel() + env := setupTestEnv() ctx := env.ctx view := NewViewKeeper(env.acck) diff --git a/tm2/pkg/sdk/baseapp.go b/tm2/pkg/sdk/baseapp.go index 1d356f7ad65..e564435456a 100644 --- a/tm2/pkg/sdk/baseapp.go +++ b/tm2/pkg/sdk/baseapp.go @@ -8,12 +8,13 @@ import ( "strings" "syscall" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/amino" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" bft "github.com/gnolang/gno/tm2/pkg/bft/types" dbm "github.com/gnolang/gno/tm2/pkg/db" "github.com/gnolang/gno/tm2/pkg/errors" - "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/store" ) @@ -27,7 +28,7 @@ var ( // BaseApp reflects the ABCI application implementation. type BaseApp struct { // initialized on creation - logger log.Logger + logger *slog.Logger name string // application name from abci.Info db dbm.DB // common DB backend cms store.CommitMultiStore // Main (uncached) state @@ -80,7 +81,12 @@ var _ abci.Application = (*BaseApp)(nil) // // NOTE: The db is used to store the version number for now. func NewBaseApp( - name string, logger log.Logger, db dbm.DB, baseKey store.StoreKey, mainKey store.StoreKey, options ...func(*BaseApp), + name string, + logger *slog.Logger, + db dbm.DB, + baseKey store.StoreKey, + mainKey store.StoreKey, + options ...func(*BaseApp), ) *BaseApp { app := &BaseApp{ logger: logger, @@ -109,7 +115,7 @@ func (app *BaseApp) AppVersion() string { } // Logger returns the logger of the BaseApp. -func (app *BaseApp) Logger() log.Logger { +func (app *BaseApp) Logger() *slog.Logger { return app.logger } diff --git a/tm2/pkg/sdk/baseapp_test.go b/tm2/pkg/sdk/baseapp_test.go index 2d130583885..1d8e73acc8d 100644 --- a/tm2/pkg/sdk/baseapp_test.go +++ b/tm2/pkg/sdk/baseapp_test.go @@ -8,6 +8,8 @@ import ( "reflect" "testing" + "golang.org/x/exp/slog" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -15,7 +17,6 @@ import ( abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" bft "github.com/gnolang/gno/tm2/pkg/bft/types" dbm "github.com/gnolang/gno/tm2/pkg/db" - "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/sdk/testutils" "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/store/dbadapter" @@ -55,8 +56,10 @@ func newTxCounter(txInt int64, msgInts ...int64) std.Tx { return tx } -func defaultLogger() log.Logger { - return log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app") +func defaultLogger() *slog.Logger { + logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) + + return logger.With("module", "sdk/app") } func newBaseApp(name string, db dbm.DB, options ...func(*BaseApp)) *BaseApp { @@ -80,6 +83,8 @@ func setupBaseApp(t *testing.T, options ...func(*BaseApp)) *BaseApp { } func TestMountStores(t *testing.T) { + t.Parallel() + app := setupBaseApp(t) // check both stores @@ -92,6 +97,8 @@ func TestMountStores(t *testing.T) { // Test that we can make commits and then reload old versions. // Test that LoadLatestVersion actually does. func TestLoadVersion(t *testing.T) { + t.Parallel() + pruningOpt := SetPruningOptions(store.PruneSyncable) name := t.Name() db := dbm.NewMemDB() @@ -139,6 +146,8 @@ func TestLoadVersion(t *testing.T) { } func TestAppVersionSetterGetter(t *testing.T) { + t.Parallel() + pruningOpt := SetPruningOptions(store.PruneSyncable) name := t.Name() db := dbm.NewMemDB() @@ -158,6 +167,8 @@ func TestAppVersionSetterGetter(t *testing.T) { } func TestLoadVersionInvalid(t *testing.T) { + t.Parallel() + pruningOpt := SetPruningOptions(store.PruneSyncable) name := t.Name() db := dbm.NewMemDB() @@ -198,6 +209,8 @@ func testLoadVersionHelper(t *testing.T, app *BaseApp, expectedHeight int64, exp } func TestOptionFunction(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() bap := newBaseApp("starting name", db, testChangeNameHelper("new name")) require.Equal(t, bap.name, "new name", "BaseApp should have had name changed via option function") @@ -211,6 +224,8 @@ func testChangeNameHelper(name string) func(*BaseApp) { // Test that Info returns the latest committed state. func TestInfo(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() app := newBaseApp(t.Name(), db) @@ -229,6 +244,8 @@ func TestInfo(t *testing.T) { } func TestBaseAppOptionSeal(t *testing.T) { + t.Parallel() + app := setupBaseApp(t) require.Panics(t, func() { @@ -258,6 +275,8 @@ func TestBaseAppOptionSeal(t *testing.T) { } func TestSetMinGasPrices(t *testing.T) { + t.Parallel() + minGasPrices, err := ParseGasPrices("5000stake/10gas") require.Nil(t, err) db := dbm.NewMemDB() @@ -266,6 +285,8 @@ func TestSetMinGasPrices(t *testing.T) { } func TestInitChainer(t *testing.T) { + t.Parallel() + name := t.Name() // keep the db and logger ourselves so // we can reload the same app later @@ -444,10 +465,6 @@ func (mch msgCounterHandler) Query(ctx Context, req abci.RequestQuery) abci.Resp panic("should not happen") } -func i2b(i int64) []byte { - return []byte{byte(i)} -} - func getIntFromStore(store store.Store, key []byte) int64 { bz := store.Get(key) if len(bz) == 0 { @@ -477,7 +494,7 @@ func incrementingCounter(t *testing.T, store store.Store, counterKey []byte, cou return } -//--------------------------------------------------------------------- +// --------------------------------------------------------------------- // Tx processing - CheckTx, DeliverTx, SimulateTx. // These tests use the serialized tx as input, while most others will use the // Check(), Deliver(), Simulate() methods directly. @@ -487,6 +504,8 @@ func incrementingCounter(t *testing.T, store store.Store, counterKey []byte, cou // on the store within a block, and that the CheckTx state // gets reset to the latest committed state during Commit func TestCheckTx(t *testing.T) { + t.Parallel() + // This ante handler reads the key and checks that the value matches the current counter. // This ensures changes to the kvstore persist across successive CheckTx. counterKey := []byte("counter-key") @@ -530,6 +549,8 @@ func TestCheckTx(t *testing.T) { // Test that successive DeliverTx can see each others' effects // on the store, both within and across blocks. func TestDeliverTx(t *testing.T) { + t.Parallel() + // test increments in the ante anteKey := []byte("ante-key") anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, mainKey, anteKey)) } @@ -566,14 +587,10 @@ func TestDeliverTx(t *testing.T) { } } -// Number of messages doesn't matter to CheckTx. -func TestMultiMsgCheckTx(t *testing.T) { - // TODO: ensure we get the same results - // with one message or many -} - // One call to DeliverTx should process all the messages, in order. func TestMultiMsgDeliverTx(t *testing.T) { + t.Parallel() + // increment the tx counter anteKey := []byte("ante-key") anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, mainKey, anteKey)) } @@ -633,17 +650,12 @@ func TestMultiMsgDeliverTx(t *testing.T) { require.Equal(t, int64(2), msgCounter2) } -// Interleave calls to Check and Deliver and ensure -// that there is no cross-talk. Check sees results of the previous Check calls -// and Deliver sees that of the previous Deliver calls, but they don't see eachother. -func TestConcurrentCheckDeliver(t *testing.T) { - // TODO -} - // Simulate a transaction that uses gas to compute the gas. // Simulate() and Query(".app/simulate", txBytes) should give // the same results. func TestSimulateTx(t *testing.T) { + t.Parallel() + gasConsumed := int64(5) anteOpt := func(bapp *BaseApp) { @@ -704,6 +716,8 @@ func TestSimulateTx(t *testing.T) { } func TestRunInvalidTransaction(t *testing.T) { + t.Parallel() + anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(func(ctx Context, tx Tx, simulate bool) (newCtx Context, res Result, abort bool) { newCtx = ctx @@ -779,6 +793,8 @@ func TestRunInvalidTransaction(t *testing.T) { // Test that transactions exceeding gas limits fail func TestTxGasLimits(t *testing.T) { + t.Parallel() + gasGranted := int64(10) anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(func(ctx Context, tx Tx, simulate bool) (newCtx Context, res Result, abort bool) { @@ -853,6 +869,8 @@ func TestTxGasLimits(t *testing.T) { // Test that transactions exceeding gas limits fail func TestMaxBlockGasLimits(t *testing.T) { + t.Parallel() + gasGranted := int64(10) anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(func(ctx Context, tx Tx, simulate bool) (newCtx Context, res Result, abort bool) { @@ -942,6 +960,8 @@ func TestMaxBlockGasLimits(t *testing.T) { } func TestBaseAppAnteHandler(t *testing.T) { + t.Parallel() + anteKey := []byte("ante-key") anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, mainKey, anteKey)) @@ -1011,6 +1031,8 @@ func TestBaseAppAnteHandler(t *testing.T) { } func TestGasConsumptionBadTx(t *testing.T) { + t.Parallel() + gasWanted := int64(5) anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(func(ctx Context, tx Tx, simulate bool) (newCtx Context, res Result, abort bool) { @@ -1074,6 +1096,8 @@ func TestGasConsumptionBadTx(t *testing.T) { // Test that we can only query from the latest committed state. func TestQuery(t *testing.T) { + t.Parallel() + key, value := []byte("hello"), []byte("goodbye") anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(func(ctx Context, tx Tx, simulate bool) (newCtx Context, res Result, abort bool) { diff --git a/tm2/pkg/sdk/context.go b/tm2/pkg/sdk/context.go index 6e33cb1f419..c208a3e2e75 100644 --- a/tm2/pkg/sdk/context.go +++ b/tm2/pkg/sdk/context.go @@ -4,9 +4,10 @@ import ( "context" "time" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/amino" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" - "github.com/gnolang/gno/tm2/pkg/log" "github.com/gnolang/gno/tm2/pkg/store" "github.com/gnolang/gno/tm2/pkg/store/gas" ) @@ -26,7 +27,7 @@ type Context struct { header abci.Header chainID string txBytes []byte - logger log.Logger + logger *slog.Logger voteInfo []abci.VoteInfo gasMeter store.GasMeter // XXX make passthroughGasMeter w/ blockGasMeter? blockGasMeter store.GasMeter @@ -46,7 +47,7 @@ func (c Context) BlockHeight() int64 { return c.header.GetHeight() } func (c Context) BlockTime() time.Time { return c.header.GetTime() } func (c Context) ChainID() string { return c.chainID } func (c Context) TxBytes() []byte { return c.txBytes } -func (c Context) Logger() log.Logger { return c.logger } +func (c Context) Logger() *slog.Logger { return c.logger } func (c Context) VoteInfos() []abci.VoteInfo { return c.voteInfo } func (c Context) GasMeter() store.GasMeter { return c.gasMeter } func (c Context) BlockGasMeter() store.GasMeter { return c.blockGasMeter } @@ -65,7 +66,7 @@ func (c Context) ConsensusParams() *abci.ConsensusParams { } // create a new context -func NewContext(mode RunTxMode, ms store.MultiStore, header abci.Header, logger log.Logger) Context { +func NewContext(mode RunTxMode, ms store.MultiStore, header abci.Header, logger *slog.Logger) Context { if header.GetChainID() == "" { panic("header chain id cannot be empty") } @@ -112,7 +113,7 @@ func (c Context) WithTxBytes(txBytes []byte) Context { return c } -func (c Context) WithLogger(logger log.Logger) Context { +func (c Context) WithLogger(logger *slog.Logger) Context { c.logger = logger return c } diff --git a/tm2/pkg/sdk/sdk.proto b/tm2/pkg/sdk/sdk.proto index 50e2e6c42eb..62fbfc19758 100644 --- a/tm2/pkg/sdk/sdk.proto +++ b/tm2/pkg/sdk/sdk.proto @@ -9,7 +9,7 @@ import "github.com/gnolang/gno/tm2/pkg/crypto/merkle/merkle.proto"; // messages message Result { - abci.ResponseBase ResponseBase = 1; - sint64 GasWanted = 2; - sint64 GasUsed = 3; + abci.ResponseBase response_base = 1 [json_name = "ResponseBase"]; + sint64 gas_wanted = 2 [json_name = "GasWanted"]; + sint64 gas_used = 3 [json_name = "GasUsed"]; } \ No newline at end of file diff --git a/tm2/pkg/service/service.go b/tm2/pkg/service/service.go index e1e9189e044..2a9fa0c3bfc 100644 --- a/tm2/pkg/service/service.go +++ b/tm2/pkg/service/service.go @@ -5,6 +5,8 @@ import ( "fmt" "sync/atomic" + "golang.org/x/exp/slog" + "github.com/gnolang/gno/tm2/pkg/log" ) @@ -49,7 +51,7 @@ type Service interface { String() string // SetLogger sets a logger. - SetLogger(log.Logger) + SetLogger(*slog.Logger) } /* @@ -95,7 +97,7 @@ Typical usage: } */ type BaseService struct { - Logger log.Logger + Logger *slog.Logger name string started uint32 // atomic stopped uint32 // atomic @@ -106,9 +108,9 @@ type BaseService struct { } // NewBaseService creates a new BaseService. -func NewBaseService(logger log.Logger, name string, impl Service) *BaseService { +func NewBaseService(logger *slog.Logger, name string, impl Service) *BaseService { if logger == nil { - logger = log.NewNopLogger() + logger = log.NewNoopLogger() } return &BaseService{ @@ -120,7 +122,7 @@ func NewBaseService(logger log.Logger, name string, impl Service) *BaseService { } // SetLogger implements Service by setting a logger. -func (bs *BaseService) SetLogger(l log.Logger) { +func (bs *BaseService) SetLogger(l *slog.Logger) { bs.Logger = l } diff --git a/tm2/pkg/service/service_test.go b/tm2/pkg/service/service_test.go index b3817d6f60a..adf14e0c539 100644 --- a/tm2/pkg/service/service_test.go +++ b/tm2/pkg/service/service_test.go @@ -16,6 +16,8 @@ func (testService) OnReset() error { } func TestBaseServiceWait(t *testing.T) { + t.Parallel() + ts := &testService{} ts.BaseService = *NewBaseService(nil, "TestService", ts) ts.Start() @@ -37,6 +39,8 @@ func TestBaseServiceWait(t *testing.T) { } func TestBaseServiceReset(t *testing.T) { + t.Parallel() + ts := &testService{} ts.BaseService = *NewBaseService(nil, "TestService", ts) ts.Start() diff --git a/tm2/pkg/std/account.go b/tm2/pkg/std/account.go index 604444b4046..c70f43d22e9 100644 --- a/tm2/pkg/std/account.go +++ b/tm2/pkg/std/account.go @@ -5,6 +5,11 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/errors" + + _ "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" + _ "github.com/gnolang/gno/tm2/pkg/crypto/mock" + _ "github.com/gnolang/gno/tm2/pkg/crypto/multisig" + _ "github.com/gnolang/gno/tm2/pkg/crypto/secp256k1" ) // Account is an interface used to store coins at a given address within state. diff --git a/tm2/pkg/std/coin.go b/tm2/pkg/std/coin.go index d5eb9bb1da0..75063320ad3 100644 --- a/tm2/pkg/std/coin.go +++ b/tm2/pkg/std/coin.go @@ -619,11 +619,9 @@ var ( // Denominations can be 3 ~ 16 characters long. reDnmString = `[a-z][a-z0-9]{2,15}` reAmt = `[[:digit:]]+` - reDecAmt = `[[:digit:]]*\.[[:digit:]]+` reSpc = `[[:space:]]*` reDnm = regexp.MustCompile(fmt.Sprintf(`^%s$`, reDnmString)) reCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reAmt, reSpc, reDnmString)) - reDecCoin = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, reDecAmt, reSpc, reDnmString)) ) func validateDenom(denom string) error { diff --git a/tm2/pkg/std/coin_test.go b/tm2/pkg/std/coin_test.go index 923781ed412..8d9a9359b61 100644 --- a/tm2/pkg/std/coin_test.go +++ b/tm2/pkg/std/coin_test.go @@ -18,12 +18,16 @@ var ( // Coin tests func TestCoin(t *testing.T) { + t.Parallel() + require.Panics(t, func() { NewCoin(testDenom1, -1) }) require.Panics(t, func() { NewCoin(strings.ToUpper(testDenom1), 10) }) require.Equal(t, int64(5), NewCoin(testDenom1, 5).Amount) } func TestIsEqualCoin(t *testing.T) { + t.Parallel() + cases := []struct { inputOne Coin inputTwo Coin @@ -46,6 +50,8 @@ func TestIsEqualCoin(t *testing.T) { } func TestCoinIsValid(t *testing.T) { + t.Parallel() + cases := []struct { coin Coin expectPass bool @@ -66,6 +72,8 @@ func TestCoinIsValid(t *testing.T) { } func TestAddCoin(t *testing.T) { + t.Parallel() + cases := []struct { inputOne Coin inputTwo Coin @@ -88,6 +96,8 @@ func TestAddCoin(t *testing.T) { } func TestSubCoin(t *testing.T) { + t.Parallel() + cases := []struct { inputOne Coin inputTwo Coin @@ -120,6 +130,8 @@ func TestSubCoin(t *testing.T) { } func TestIsGTECoin(t *testing.T) { + t.Parallel() + cases := []struct { inputOne Coin inputTwo Coin @@ -142,6 +154,8 @@ func TestIsGTECoin(t *testing.T) { } func TestIsLTCoin(t *testing.T) { + t.Parallel() + cases := []struct { inputOne Coin inputTwo Coin @@ -167,6 +181,8 @@ func TestIsLTCoin(t *testing.T) { } func TestCoinIsZero(t *testing.T) { + t.Parallel() + coin := NewCoin(testDenom1, 0) res := coin.IsZero() require.True(t, res) @@ -180,6 +196,8 @@ func TestCoinIsZero(t *testing.T) { // Coins tests func TestIsZeroCoins(t *testing.T) { + t.Parallel() + cases := []struct { inputOne Coins expected bool @@ -198,6 +216,8 @@ func TestIsZeroCoins(t *testing.T) { } func TestEqualCoins(t *testing.T) { + t.Parallel() + cases := []struct { inputOne Coins inputTwo Coins @@ -224,6 +244,8 @@ func TestEqualCoins(t *testing.T) { } func TestAddCoins(t *testing.T) { + t.Parallel() + zero := int64(0) one := int64(1) two := int64(2) @@ -248,6 +270,8 @@ func TestAddCoins(t *testing.T) { } func TestSubCoins(t *testing.T) { + t.Parallel() + zero := int64(0) one := int64(1) two := int64(2) @@ -277,6 +301,8 @@ func TestSubCoins(t *testing.T) { } func TestCoins(t *testing.T) { + t.Parallel() + good := Coins{ {"gas", int64(1)}, {"mineral", int64(1)}, @@ -339,6 +365,8 @@ func TestCoins(t *testing.T) { } func TestCoinsGT(t *testing.T) { + t.Parallel() + one := int64(1) two := int64(2) @@ -351,6 +379,8 @@ func TestCoinsGT(t *testing.T) { } func TestCoinsLT(t *testing.T) { + t.Parallel() + one := int64(1) two := int64(2) @@ -366,6 +396,8 @@ func TestCoinsLT(t *testing.T) { } func TestCoinsLTE(t *testing.T) { + t.Parallel() + one := int64(1) two := int64(2) @@ -381,6 +413,8 @@ func TestCoinsLTE(t *testing.T) { } func TestParse(t *testing.T) { + t.Parallel() + one := int64(1) cases := []struct { @@ -413,6 +447,8 @@ func TestParse(t *testing.T) { } func TestSortCoins(t *testing.T) { + t.Parallel() + good := Coins{ NewCoin("gas", 1), NewCoin("mineral", 1), @@ -462,6 +498,8 @@ func TestSortCoins(t *testing.T) { } func TestAmountOf(t *testing.T) { + t.Parallel() + case0 := Coins{} case1 := Coins{ NewCoin("gold", 0), @@ -504,6 +542,8 @@ func TestAmountOf(t *testing.T) { } func TestCoinsIsAnyGTE(t *testing.T) { + t.Parallel() + one := int64(1) two := int64(2) @@ -524,6 +564,8 @@ func TestCoinsIsAnyGTE(t *testing.T) { } func TestCoinsIsAllGT(t *testing.T) { + t.Parallel() + one := int64(1) two := int64(2) @@ -544,6 +586,8 @@ func TestCoinsIsAllGT(t *testing.T) { } func TestCoinsIsAllGTE(t *testing.T) { + t.Parallel() + one := int64(1) two := int64(2) @@ -566,6 +610,8 @@ func TestCoinsIsAllGTE(t *testing.T) { } func TestNewCoins(t *testing.T) { + t.Parallel() + tenatom := NewCoin("atom", 10) tenbtc := NewCoin("btc", 10) zeroeth := NewCoin("eth", 0) @@ -582,7 +628,10 @@ func TestNewCoins(t *testing.T) { {"panic on dups", []Coin{tenatom, tenatom}, Coins{}, true}, } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() + if tt.wantPanic { require.Panics(t, func() { NewCoins(tt.coins...) }) return @@ -594,6 +643,8 @@ func TestNewCoins(t *testing.T) { } func TestCoinsIsAnyGT(t *testing.T) { + t.Parallel() + twoAtom := NewCoin("atom", 2) fiveAtom := NewCoin("atom", 5) threeEth := NewCoin("eth", 3) @@ -613,6 +664,8 @@ func TestCoinsIsAnyGT(t *testing.T) { } func TestFindDup(t *testing.T) { + t.Parallel() + abc := NewCoin("abc", 10) def := NewCoin("def", 10) ghi := NewCoin("ghi", 10) @@ -632,7 +685,10 @@ func TestFindDup(t *testing.T) { {"dup after first position", args{Coins{abc, def, def}}, 2}, } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() + if got := findDup(tt.args.coins); got != tt.want { t.Errorf("findDup() = %v, want %v", got, tt.want) } @@ -641,6 +697,8 @@ func TestFindDup(t *testing.T) { } func TestMarshalJSONCoins(t *testing.T) { + t.Parallel() + testCases := []struct { name string input Coins @@ -652,7 +710,11 @@ func TestMarshalJSONCoins(t *testing.T) { } for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + bz, err := amino.MarshalJSON(tc.input) require.NoError(t, err) require.Equal(t, tc.strOutput, string(bz)) diff --git a/tm2/pkg/std/kvpair_test.go b/tm2/pkg/std/kvpair_test.go index d030da48053..5edf750e6dd 100644 --- a/tm2/pkg/std/kvpair_test.go +++ b/tm2/pkg/std/kvpair_test.go @@ -7,6 +7,8 @@ import ( ) func TestKVPairs(t *testing.T) { + t.Parallel() + kvs := KVPairs{ {Key: []byte("k2"), Value: []byte("")}, {Key: []byte("k1"), Value: []byte("2")}, @@ -32,6 +34,8 @@ func TestKVPairs(t *testing.T) { } func TestKI64Pairs(t *testing.T) { + t.Parallel() + kvs := KI64Pairs{ {Key: []byte("k2"), Value: 0}, {Key: []byte("k1"), Value: 2}, diff --git a/tm2/pkg/std/memfile.go b/tm2/pkg/std/memfile.go index 99b8061ea3b..66ae854b65f 100644 --- a/tm2/pkg/std/memfile.go +++ b/tm2/pkg/std/memfile.go @@ -13,11 +13,15 @@ type MemFile struct { Body string } +// MemPackage represents the information and files of a package which will be +// stored in memory. It will generally be initialized by package gnolang's +// ReadMemPackage. +// // NOTE: in the future, a MemPackage may represent // updates/additional-files for an existing package. type MemPackage struct { - Name string - Path string + Name string // package name as declared by `package` + Path string // import path Files []*MemFile } @@ -34,32 +38,27 @@ func (mempkg *MemPackage) IsEmpty() bool { return len(mempkg.Files) == 0 } -const ( - reDomainPart = `gno\.land` - rePathPart = `[a-z][a-z0-9_]*` - rePkgName = `^[a-z][a-z0-9_]*$` - rePkgPath = reDomainPart + `/p/` + rePathPart + `(/` + rePathPart + `)*` - reRlmPath = reDomainPart + `/r/` + rePathPart + `(/` + rePathPart + `)*` - rePkgOrRlmPath = `^(` + rePkgPath + `|` + reRlmPath + `)$` - reFileName = `^[a-zA-Z0-9_]*\.[a-z0-9_\.]*$` +const rePathPart = `[a-z][a-z0-9_]*` + +var ( + rePkgName = regexp.MustCompile(`^[a-z][a-z0-9_]*$`) + rePkgOrRlmPath = regexp.MustCompile(`gno\.land/(?:p|r)(?:/` + rePathPart + `)+`) + reFileName = regexp.MustCompile(`^[a-zA-Z0-9_]*\.[a-z0-9_\.]*$`) ) // path must not contain any dots after the first domain component. // file names must contain dots. // NOTE: this is to prevent conflicts with nested paths. func (mempkg *MemPackage) Validate() error { - ok, _ := regexp.MatchString(rePkgName, mempkg.Name) - if !ok { + if !rePkgName.MatchString(mempkg.Name) { return errors.New(fmt.Sprintf("invalid package name %q", mempkg.Name)) } - ok, _ = regexp.MatchString(rePkgOrRlmPath, mempkg.Path) - if !ok { + if !rePkgOrRlmPath.MatchString(mempkg.Path) { return errors.New(fmt.Sprintf("invalid package/realm path %q", mempkg.Path)) } fnames := map[string]struct{}{} for _, memfile := range mempkg.Files { - ok, _ := regexp.MatchString(reFileName, memfile.Name) - if !ok { + if !reFileName.MatchString(memfile.Name) { return errors.New(fmt.Sprintf("invalid file name %q", memfile.Name)) } if _, exists := fnames[memfile.Name]; exists { diff --git a/tm2/pkg/std/package_test.go b/tm2/pkg/std/package_test.go new file mode 100644 index 00000000000..0a21188737b --- /dev/null +++ b/tm2/pkg/std/package_test.go @@ -0,0 +1,26 @@ +package std_test + +import ( + "testing" + + "github.com/gnolang/gno/tm2/pkg/amino" + "github.com/gnolang/gno/tm2/pkg/std" + "github.com/stretchr/testify/require" +) + +func TestAminoBaseAccount(t *testing.T) { + b := []byte(`{ + "address": "g1x90eh5ejc22548hjqznm2egyvn8ny36lqu460f", + "coins": "4200000ugnot", + "public_key": { + "@type": "/tm.PubKeySecp256k1", + "value": "AwMzujfppqEi8lozMVD8ORENUR8SIE06VLNP8FGL0aQ2" + }, + "account_number": "159", + "sequence": "33" +}`) + acc := std.BaseAccount{} + + err := amino.UnmarshalJSON(b, &acc) + require.NoError(t, err) +} diff --git a/tm2/pkg/std/std.proto b/tm2/pkg/std/std.proto index d7b9b9c737b..2fad1eeff38 100644 --- a/tm2/pkg/std/std.proto +++ b/tm2/pkg/std/std.proto @@ -8,22 +8,22 @@ import "google/protobuf/any.proto"; // messages message BaseAccount { - string Address = 1; - string Coins = 2; - google.protobuf.Any PubKey = 3; - uint64 AccountNumber = 4; - uint64 Sequence = 5; + string address = 1; + string coins = 2; + google.protobuf.Any pub_key = 3 [json_name = "public_key"]; + uint64 account_number = 4; + uint64 sequence = 5; } message MemFile { - string Name = 1; - string Body = 2; + string name = 1 [json_name = "Name"]; + string body = 2 [json_name = "Body"]; } message MemPackage { - string Name = 1; - string Path = 2; - repeated MemFile Files = 3; + string name = 1 [json_name = "Name"]; + string path = 2 [json_name = "Path"]; + repeated MemFile files = 3 [json_name = "Files"]; } message InternalError { diff --git a/tm2/pkg/store/cache/store_test.go b/tm2/pkg/store/cache/store_test.go index 5daf83fcb61..adf122fc637 100644 --- a/tm2/pkg/store/cache/store_test.go +++ b/tm2/pkg/store/cache/store_test.go @@ -22,6 +22,8 @@ func keyFmt(i int) []byte { return bz(fmt.Sprintf("key%0.8d", i)) } func valFmt(i int) []byte { return bz(fmt.Sprintf("value%0.8d", i)) } func TestCacheStore(t *testing.T) { + t.Parallel() + mem := dbadapter.Store{dbm.NewMemDB()} st := cache.New(mem) @@ -65,12 +67,16 @@ func TestCacheStore(t *testing.T) { } func TestCacheStoreNoNilSet(t *testing.T) { + t.Parallel() + mem := dbadapter.Store{dbm.NewMemDB()} st := cache.New(mem) require.Panics(t, func() { st.Set([]byte("key"), nil) }, "setting a nil value should panic") } func TestCacheStoreNested(t *testing.T) { + t.Parallel() + mem := dbadapter.Store{dbm.NewMemDB()} st := cache.New(mem) @@ -100,6 +106,8 @@ func TestCacheStoreNested(t *testing.T) { } func TestCacheKVIteratorBounds(t *testing.T) { + t.Parallel() + st := newCacheStore() // set some items @@ -151,6 +159,8 @@ func TestCacheKVIteratorBounds(t *testing.T) { } func TestCacheKVReverseIteratorBounds(t *testing.T) { + t.Parallel() + st := newCacheStore() // set some items @@ -195,6 +205,8 @@ func TestCacheKVReverseIteratorBounds(t *testing.T) { } func TestCacheKVMergeIteratorBasics(t *testing.T) { + t.Parallel() + st := newCacheStore() // set and delete an item in the cache, iterator should be empty @@ -243,6 +255,8 @@ func TestCacheKVMergeIteratorBasics(t *testing.T) { } func TestCacheKVMergeIteratorDeleteLast(t *testing.T) { + t.Parallel() + st := newCacheStore() // set some items and write them @@ -269,6 +283,8 @@ func TestCacheKVMergeIteratorDeleteLast(t *testing.T) { } func TestCacheKVMergeIteratorDeletes(t *testing.T) { + t.Parallel() + st := newCacheStore() truth := dbm.NewMemDB() @@ -303,6 +319,8 @@ func TestCacheKVMergeIteratorDeletes(t *testing.T) { } func TestCacheKVMergeIteratorChunks(t *testing.T) { + t.Parallel() + st := newCacheStore() // Use the truth to check values on the merge iterator @@ -334,6 +352,8 @@ func TestCacheKVMergeIteratorChunks(t *testing.T) { } func TestCacheKVMergeIteratorRandom(t *testing.T) { + t.Parallel() + st := newCacheStore() truth := dbm.NewMemDB() diff --git a/tm2/pkg/store/gas/store_test.go b/tm2/pkg/store/gas/store_test.go index 70f1fc8ea7f..54729abf2ed 100644 --- a/tm2/pkg/store/gas/store_test.go +++ b/tm2/pkg/store/gas/store_test.go @@ -13,18 +13,14 @@ import ( "github.com/stretchr/testify/require" ) -func newGasKVStore() types.Store { - meter := types.NewGasMeter(10000) - mem := dbadapter.Store{dbm.NewMemDB()} - return gas.New(mem, meter, types.DefaultGasConfig()) -} - func bz(s string) []byte { return []byte(s) } func keyFmt(i int) []byte { return bz(fmt.Sprintf("key%0.8d", i)) } func valFmt(i int) []byte { return bz(fmt.Sprintf("value%0.8d", i)) } func TestGasKVStoreBasic(t *testing.T) { + t.Parallel() + mem := dbadapter.Store{dbm.NewMemDB()} meter := types.NewGasMeter(10000) st := gas.New(mem, meter, types.DefaultGasConfig()) @@ -37,6 +33,8 @@ func TestGasKVStoreBasic(t *testing.T) { } func TestGasKVStoreIterator(t *testing.T) { + t.Parallel() + mem := dbadapter.Store{dbm.NewMemDB()} meter := types.NewGasMeter(10000) st := gas.New(mem, meter, types.DefaultGasConfig()) @@ -61,6 +59,8 @@ func TestGasKVStoreIterator(t *testing.T) { } func TestGasKVStoreOutOfGasSet(t *testing.T) { + t.Parallel() + mem := dbadapter.Store{dbm.NewMemDB()} meter := types.NewGasMeter(0) st := gas.New(mem, meter, types.DefaultGasConfig()) @@ -68,6 +68,8 @@ func TestGasKVStoreOutOfGasSet(t *testing.T) { } func TestGasKVStoreOutOfGasIterator(t *testing.T) { + t.Parallel() + mem := dbadapter.Store{dbm.NewMemDB()} meter := types.NewGasMeter(20000) st := gas.New(mem, meter, types.DefaultGasConfig()) diff --git a/tm2/pkg/store/iavl/store_test.go b/tm2/pkg/store/iavl/store_test.go index 83afae91860..a29dbab096a 100644 --- a/tm2/pkg/store/iavl/store_test.go +++ b/tm2/pkg/store/iavl/store_test.go @@ -49,6 +49,8 @@ func newAlohaTree(t *testing.T, db dbm.DB) (*iavl.MutableTree, types.CommitID) { } func TestGetImmutable(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() tree, cID := newAlohaTree(t, db) store := UnsafeNewStore(tree, storeOptions(10, 10)) @@ -79,6 +81,8 @@ func TestGetImmutable(t *testing.T) { } func TestTestGetImmutableIterator(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() tree, cID := newAlohaTree(t, db) store := UnsafeNewStore(tree, storeOptions(10, 10)) @@ -102,6 +106,8 @@ func TestTestGetImmutableIterator(t *testing.T) { } func TestIAVLStoreGetSetHasDelete(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() tree, _ := newAlohaTree(t, db) iavlStore := UnsafeNewStore(tree, storeOptions(numRecent, storeEvery)) @@ -127,6 +133,8 @@ func TestIAVLStoreGetSetHasDelete(t *testing.T) { } func TestIAVLStoreNoNilSet(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() tree, _ := newAlohaTree(t, db) iavlStore := UnsafeNewStore(tree, storeOptions(numRecent, storeEvery)) @@ -134,6 +142,8 @@ func TestIAVLStoreNoNilSet(t *testing.T) { } func TestIAVLIterator(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() tree, _ := newAlohaTree(t, db) iavlStore := UnsafeNewStore(tree, storeOptions(numRecent, storeEvery)) @@ -207,6 +217,8 @@ func TestIAVLIterator(t *testing.T) { } func TestIAVLReverseIterator(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) iavlStore := UnsafeNewStore(tree, storeOptions(numRecent, storeEvery)) @@ -240,6 +252,8 @@ func TestIAVLReverseIterator(t *testing.T) { } func TestIAVLPrefixIterator(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) iavlStore := UnsafeNewStore(tree, storeOptions(numRecent, storeEvery)) @@ -302,6 +316,8 @@ func TestIAVLPrefixIterator(t *testing.T) { } func TestIAVLReversePrefixIterator(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) iavlStore := UnsafeNewStore(tree, storeOptions(numRecent, storeEvery)) @@ -368,6 +384,8 @@ func nextVersion(iavl *Store) { } func TestIAVLDefaultPruning(t *testing.T) { + t.Parallel() + // Expected stored / deleted version numbers for: // numRecent = 5, storeEvery = 3 states := []pruneState{ @@ -392,6 +410,8 @@ func TestIAVLDefaultPruning(t *testing.T) { } func TestIAVLAlternativePruning(t *testing.T) { + t.Parallel() + // Expected stored / deleted version numbers for: // numRecent = 3, storeEvery = 5 states := []pruneState{ @@ -442,6 +462,8 @@ func testPruning(t *testing.T, numRecent int64, storeEvery int64, states []prune } func TestIAVLNoPrune(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) iavlStore := UnsafeNewStore(tree, storeOptions(numRecent, int64(1))) @@ -457,6 +479,8 @@ func TestIAVLNoPrune(t *testing.T) { } func TestIAVLPruneEverything(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) iavlStore := UnsafeNewStore(tree, storeOptions(int64(0), int64(0))) @@ -475,6 +499,8 @@ func TestIAVLPruneEverything(t *testing.T) { } func TestIAVLStoreQuery(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) iavlStore := UnsafeNewStore(tree, storeOptions(numRecent, storeEvery)) diff --git a/tm2/pkg/store/prefix/store_test.go b/tm2/pkg/store/prefix/store_test.go index 70f0eae05c8..6f70c5bd16f 100644 --- a/tm2/pkg/store/prefix/store_test.go +++ b/tm2/pkg/store/prefix/store_test.go @@ -88,6 +88,8 @@ func testPrefixStore(t *testing.T, baseStore types.Store, prefix []byte) { } func TestIAVLStorePrefix(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() tree := tiavl.NewMutableTree(db, cacheSize) iavlStore := iavl.UnsafeNewStore(tree, types.StoreOptions{ @@ -101,6 +103,8 @@ func TestIAVLStorePrefix(t *testing.T) { } func TestPrefixStoreNoNilSet(t *testing.T) { + t.Parallel() + meter := types.NewGasMeter(100000000) mem := dbadapter.Store{dbm.NewMemDB()} gasStore := gas.New(mem, meter, types.DefaultGasConfig()) @@ -108,6 +112,8 @@ func TestPrefixStoreNoNilSet(t *testing.T) { } func TestPrefixStoreIterate(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() baseStore := dbadapter.Store{db} prefix := []byte("test") @@ -135,6 +141,8 @@ func incFirstByte(bz []byte) { } func TestCloneAppend(t *testing.T) { + t.Parallel() + kvps := genRandomKVPairs() for _, kvp := range kvps { bz := cloneAppend(kvp.key, kvp.value) @@ -154,6 +162,8 @@ func TestCloneAppend(t *testing.T) { } func TestPrefixStoreIteratorEdgeCase(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() baseStore := dbadapter.Store{db} @@ -184,6 +194,8 @@ func TestPrefixStoreIteratorEdgeCase(t *testing.T) { } func TestPrefixStoreReverseIteratorEdgeCase(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() baseStore := dbadapter.Store{db} @@ -323,6 +335,8 @@ func checkNextPanics(t *testing.T, itr types.Iterator) { } func TestPrefixDBSimple(t *testing.T) { + t.Parallel() + store := mockStoreWithStuff() pstore := New(store, bz("key")) @@ -341,6 +355,8 @@ func TestPrefixDBSimple(t *testing.T) { } func TestPrefixDBIterator1(t *testing.T) { + t.Parallel() + store := mockStoreWithStuff() pstore := New(store, bz("key")) @@ -359,6 +375,8 @@ func TestPrefixDBIterator1(t *testing.T) { } func TestPrefixDBIterator2(t *testing.T) { + t.Parallel() + store := mockStoreWithStuff() pstore := New(store, bz("key")) @@ -369,6 +387,8 @@ func TestPrefixDBIterator2(t *testing.T) { } func TestPrefixDBIterator3(t *testing.T) { + t.Parallel() + store := mockStoreWithStuff() pstore := New(store, bz("key")) @@ -387,6 +407,8 @@ func TestPrefixDBIterator3(t *testing.T) { } func TestPrefixDBIterator4(t *testing.T) { + t.Parallel() + store := mockStoreWithStuff() pstore := New(store, bz("key")) @@ -397,6 +419,8 @@ func TestPrefixDBIterator4(t *testing.T) { } func TestPrefixDBReverseIterator1(t *testing.T) { + t.Parallel() + store := mockStoreWithStuff() pstore := New(store, bz("key")) @@ -415,6 +439,8 @@ func TestPrefixDBReverseIterator1(t *testing.T) { } func TestPrefixDBReverseIterator2(t *testing.T) { + t.Parallel() + store := mockStoreWithStuff() pstore := New(store, bz("key")) @@ -433,6 +459,8 @@ func TestPrefixDBReverseIterator2(t *testing.T) { } func TestPrefixDBReverseIterator3(t *testing.T) { + t.Parallel() + store := mockStoreWithStuff() pstore := New(store, bz("key")) @@ -443,6 +471,8 @@ func TestPrefixDBReverseIterator3(t *testing.T) { } func TestPrefixDBReverseIterator4(t *testing.T) { + t.Parallel() + store := mockStoreWithStuff() pstore := New(store, bz("key")) diff --git a/tm2/pkg/store/rootmulti/dbadapter.go b/tm2/pkg/store/rootmulti/dbadapter.go deleted file mode 100644 index 254d139a608..00000000000 --- a/tm2/pkg/store/rootmulti/dbadapter.go +++ /dev/null @@ -1,33 +0,0 @@ -package rootmulti - -import ( - "github.com/gnolang/gno/tm2/pkg/store/dbadapter" - "github.com/gnolang/gno/tm2/pkg/store/types" -) - -var commithash = []byte("FAKE_HASH") - -//---------------------------------------- -// commitDBStoreWrapper should only be used for simulation/debugging, -// as it doesn't compute any commit hash, and it cannot load older state. - -// Wrapper type for dbm.Db with implementation of KVStore -type commitDBStoreAdapter struct { - dbadapter.Store -} - -func (cdsa commitDBStoreAdapter) Commit() types.CommitID { - return types.CommitID{ - Version: -1, - Hash: commithash, - } -} - -func (cdsa commitDBStoreAdapter) LastCommitID() types.CommitID { - return types.CommitID{ - Version: -1, - Hash: commithash, - } -} - -func (cdsa commitDBStoreAdapter) SetPruning(_ types.PruningOptions) {} diff --git a/tm2/pkg/store/rootmulti/proof_test.go b/tm2/pkg/store/rootmulti/proof_test.go index 565ea59face..c79cbb07bd5 100644 --- a/tm2/pkg/store/rootmulti/proof_test.go +++ b/tm2/pkg/store/rootmulti/proof_test.go @@ -13,6 +13,8 @@ import ( ) func TestVerifyIAVLStoreQueryProof(t *testing.T) { + t.Parallel() + // Create main tree for testing. db := dbm.NewMemDB() opts := types.StoreOptions{ @@ -58,6 +60,8 @@ func TestVerifyIAVLStoreQueryProof(t *testing.T) { } func TestVerifyMultiStoreQueryProof(t *testing.T) { + t.Parallel() + // Create main tree for testing. db := dbm.NewMemDB() store := NewMultiStore(db) @@ -113,6 +117,8 @@ func TestVerifyMultiStoreQueryProof(t *testing.T) { } func TestVerifyMultiStoreQueryProofEmptyStore(t *testing.T) { + t.Parallel() + // Create main tree for testing. db := dbm.NewMemDB() store := NewMultiStore(db) @@ -142,6 +148,8 @@ func TestVerifyMultiStoreQueryProofEmptyStore(t *testing.T) { } func TestVerifyMultiStoreQueryProofAbsence(t *testing.T) { + t.Parallel() + // Create main tree for testing. db := dbm.NewMemDB() store := NewMultiStore(db) diff --git a/tm2/pkg/store/rootmulti/store_test.go b/tm2/pkg/store/rootmulti/store_test.go index e660f80c6bb..e6a04ee5ded 100644 --- a/tm2/pkg/store/rootmulti/store_test.go +++ b/tm2/pkg/store/rootmulti/store_test.go @@ -15,6 +15,8 @@ import ( ) func TestStoreType(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() store := NewMultiStore(db) store.MountStoreWithDB( @@ -22,6 +24,8 @@ func TestStoreType(t *testing.T) { } func TestStoreMount(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() store := NewMultiStore(db) @@ -37,6 +41,8 @@ func TestStoreMount(t *testing.T) { } func TestCacheMultiStoreWithVersion(t *testing.T) { + t.Parallel() + var db dbm.DB = dbm.NewMemDB() ms := newMultiStoreWithMounts(db) err := ms.LoadLatestVersion() @@ -74,6 +80,8 @@ func TestCacheMultiStoreWithVersion(t *testing.T) { } func TestHashStableWithEmptyCommit(t *testing.T) { + t.Parallel() + var db dbm.DB = dbm.NewMemDB() ms := newMultiStoreWithMounts(db) err := ms.LoadLatestVersion() @@ -98,6 +106,8 @@ func TestHashStableWithEmptyCommit(t *testing.T) { } func TestMultistoreCommitLoad(t *testing.T) { + t.Parallel() + var db dbm.DB = dbm.NewMemDB() store := newMultiStoreWithMounts(db) err := store.LoadLatestVersion() @@ -158,6 +168,8 @@ func TestMultistoreCommitLoad(t *testing.T) { } func TestParsePath(t *testing.T) { + t.Parallel() + _, _, err := parsePath("foo") require.Error(t, err) @@ -178,6 +190,8 @@ func TestParsePath(t *testing.T) { } func TestMultiStoreQuery(t *testing.T) { + t.Parallel() + db := dbm.NewMemDB() multi := newMultiStoreWithMounts(db) err := multi.LoadLatestVersion() diff --git a/tm2/pkg/store/types/gas_test.go b/tm2/pkg/store/types/gas_test.go index 7fef09227c5..410ba0b7e92 100644 --- a/tm2/pkg/store/types/gas_test.go +++ b/tm2/pkg/store/types/gas_test.go @@ -9,6 +9,8 @@ import ( ) func TestGasMeter(t *testing.T) { + t.Parallel() + cases := []struct { limit Gas usage []Gas @@ -46,6 +48,8 @@ func TestGasMeter(t *testing.T) { } func TestAddUint64Overflow(t *testing.T) { + t.Parallel() + testCases := []struct { a, b int64 result int64 diff --git a/tm2/pkg/strings/string_test.go b/tm2/pkg/strings/string_test.go index 1ec7b0d56be..55deac00b1f 100644 --- a/tm2/pkg/strings/string_test.go +++ b/tm2/pkg/strings/string_test.go @@ -9,6 +9,8 @@ import ( ) func TestStringInSlice(t *testing.T) { + t.Parallel() + assert.True(t, StringInSlice("a", []string{"a", "b", "c"})) assert.False(t, StringInSlice("d", []string{"a", "b", "c"})) assert.True(t, StringInSlice("", []string{""})) @@ -16,6 +18,8 @@ func TestStringInSlice(t *testing.T) { } func TestIsASCIIText(t *testing.T) { + t.Parallel() + notASCIIText := []string{ "", "\xC2", "\xC2\xA2", "\xFF", "\x80", "\xF0", "\n", "\t", } @@ -31,6 +35,8 @@ func TestIsASCIIText(t *testing.T) { } func TestASCIITrim(t *testing.T) { + t.Parallel() + assert.Equal(t, ASCIITrim(" "), "") assert.Equal(t, ASCIITrim(" a"), "a") assert.Equal(t, ASCIITrim("a "), "a") @@ -39,6 +45,8 @@ func TestASCIITrim(t *testing.T) { } func TestStringSliceEqual(t *testing.T) { + t.Parallel() + tests := []struct { a []string b []string diff --git a/tm2/pkg/timer/throttle_timer_test.go b/tm2/pkg/timer/throttle_timer_test.go index 2bee1dc0125..fb82fe39ff6 100644 --- a/tm2/pkg/timer/throttle_timer_test.go +++ b/tm2/pkg/timer/throttle_timer_test.go @@ -36,6 +36,8 @@ func (c *thCounter) Read() { } func TestThrottle(test *testing.T) { + test.Parallel() + assert := asrt.New(test) ms := 100