diff --git a/.github/goreleaser.yaml b/.github/goreleaser.yaml index ab98aa92555..1984493d36f 100644 --- a/.github/goreleaser.yaml +++ b/.github/goreleaser.yaml @@ -1,4 +1,10 @@ project_name: gno +version: 2 + +env: + - TAG_VERSION={{ if index .Env "TAG_VERSION" }}{{ .Env.TAG_VERSION }}{{ else }}latest{{ end }} + # supported in next versions -> https://github.com/goreleaser/goreleaser/issues/5059 + # - TAG_VERSION="{{ envOrDefault "TAG_VERSION" "latest" }}" before: hooks: @@ -65,6 +71,22 @@ builds: goarm: - 6 - 7 + - id: gnofaucet + dir: ./contribs/gnofaucet + binary: gnofaucet + env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + goarch: + - amd64 + - arm64 + - arm + goarm: + - 6 + - 7 + gomod: proxy: true @@ -99,7 +121,7 @@ dockers: goarch: amd64 image_templates: - "ghcr.io/gnolang/{{ .ProjectName }}:{{ .Version }}-amd64" - - "ghcr.io/gnolang/{{ .ProjectName }}:latest-amd64" + - "ghcr.io/gnolang/{{ .ProjectName }}:{{ .Env.TAG_VERSION }}-amd64" build_flag_templates: - "--target=gno" - "--platform=linux/amd64" @@ -119,7 +141,7 @@ dockers: goarch: arm64 image_templates: - "ghcr.io/gnolang/{{ .ProjectName }}:{{ .Version }}-arm64v8" - - "ghcr.io/gnolang/{{ .ProjectName }}:latest-arm64v8" + - "ghcr.io/gnolang/{{ .ProjectName }}:{{ .Env.TAG_VERSION }}-arm64v8" build_flag_templates: - "--target=gno" - "--platform=linux/arm64/v8" @@ -140,7 +162,7 @@ dockers: goarm: 6 image_templates: - "ghcr.io/gnolang/{{ .ProjectName }}:{{ .Version }}-armv6" - - "ghcr.io/gnolang/{{ .ProjectName }}:latest-armv6" + - "ghcr.io/gnolang/{{ .ProjectName }}:{{ .Env.TAG_VERSION }}-armv6" build_flag_templates: - "--target=gno" - "--platform=linux/arm/v6" @@ -161,7 +183,7 @@ dockers: goarm: 7 image_templates: - "ghcr.io/gnolang/{{ .ProjectName }}:{{ .Version }}-armv7" - - "ghcr.io/gnolang/{{ .ProjectName }}:latest-armv7" + - "ghcr.io/gnolang/{{ .ProjectName }}:{{ .Env.TAG_VERSION }}-armv7" build_flag_templates: - "--target=gno" - "--platform=linux/arm/v7" @@ -183,7 +205,7 @@ dockers: goarch: amd64 image_templates: - "ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Version }}-amd64" - - "ghcr.io/gnolang/{{ .ProjectName }}/gnoland:latest-amd64" + - "ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Env.TAG_VERSION }}-amd64" build_flag_templates: - "--target=gnoland" - "--platform=linux/amd64" @@ -204,7 +226,7 @@ dockers: goarch: arm64 image_templates: - "ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Version }}-arm64v8" - - "ghcr.io/gnolang/{{ .ProjectName }}/gnoland:latest-arm64v8" + - "ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Env.TAG_VERSION }}-arm64v8" build_flag_templates: - "--target=gnoland" - "--platform=linux/arm64/v8" @@ -226,7 +248,7 @@ dockers: goarm: 6 image_templates: - "ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Version }}-armv6" - - "ghcr.io/gnolang/{{ .ProjectName }}/gnoland:latest-armv6" + - "ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Env.TAG_VERSION }}-armv6" build_flag_templates: - "--target=gnoland" - "--platform=linux/arm/v6" @@ -248,7 +270,7 @@ dockers: goarm: 7 image_templates: - "ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Version }}-armv7" - - "ghcr.io/gnolang/{{ .ProjectName }}/gnoland:latest-armv7" + - "ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Env.TAG_VERSION }}-armv7" build_flag_templates: - "--target=gnoland" - "--platform=linux/arm/v7" @@ -270,7 +292,7 @@ dockers: goarch: amd64 image_templates: - "ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Version }}-amd64" - - "ghcr.io/gnolang/{{ .ProjectName }}/gnokey:latest-amd64" + - "ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Env.TAG_VERSION }}-amd64" build_flag_templates: - "--target=gnokey" - "--platform=linux/amd64" @@ -286,7 +308,7 @@ dockers: goarch: arm64 image_templates: - "ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Version }}-arm64v8" - - "ghcr.io/gnolang/{{ .ProjectName }}/gnokey:latest-arm64v8" + - "ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Env.TAG_VERSION }}-arm64v8" build_flag_templates: - "--target=gnokey" - "--platform=linux/arm64/v8" @@ -303,7 +325,7 @@ dockers: goarm: 6 image_templates: - "ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Version }}-armv6" - - "ghcr.io/gnolang/{{ .ProjectName }}/gnokey:latest-armv6" + - "ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Env.TAG_VERSION }}-armv6" build_flag_templates: - "--target=gnokey" - "--platform=linux/arm/v6" @@ -320,7 +342,7 @@ dockers: goarm: 7 image_templates: - "ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Version }}-armv7" - - "ghcr.io/gnolang/{{ .ProjectName }}/gnokey:latest-armv7" + - "ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Env.TAG_VERSION }}-armv7" build_flag_templates: - "--target=gnokey" - "--platform=linux/arm/v7" @@ -338,7 +360,7 @@ dockers: goarch: amd64 image_templates: - "ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Version }}-amd64" - - "ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:latest-amd64" + - "ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Env.TAG_VERSION }}-amd64" build_flag_templates: - "--target=gnoweb" - "--platform=linux/amd64" @@ -354,7 +376,7 @@ dockers: goarch: arm64 image_templates: - "ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Version }}-arm64v8" - - "ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:latest-arm64v8" + - "ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Env.TAG_VERSION }}-arm64v8" build_flag_templates: - "--target=gnoweb" - "--platform=linux/arm64/v8" @@ -371,7 +393,7 @@ dockers: goarm: 6 image_templates: - "ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Version }}-armv6" - - "ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:latest-armv6" + - "ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Env.TAG_VERSION }}-armv6" build_flag_templates: - "--target=gnoweb" - "--platform=linux/arm/v6" @@ -388,7 +410,7 @@ dockers: goarm: 7 image_templates: - "ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Version }}-armv7" - - "ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:latest-armv7" + - "ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Env.TAG_VERSION }}-armv7" build_flag_templates: - "--target=gnoweb" - "--platform=linux/arm/v7" @@ -399,6 +421,74 @@ dockers: ids: - gnoweb + # gnofaucet + - use: buildx + dockerfile: Dockerfile.release + goos: linux + goarch: amd64 + image_templates: + - "ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Version }}-amd64" + - "ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Env.TAG_VERSION }}-amd64" + build_flag_templates: + - "--target=gnofaucet" + - "--platform=linux/amd64" + - "--label=org.opencontainers.image.created={{.Date}}" + - "--label=org.opencontainers.image.title={{.ProjectName}}/gnofaucet" + - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.version={{.Version}}" + ids: + - gnofaucet + - use: buildx + dockerfile: Dockerfile.release + goos: linux + goarch: arm64 + image_templates: + - "ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Version }}-arm64v8" + - "ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Env.TAG_VERSION }}-arm64v8" + build_flag_templates: + - "--target=gnofaucet" + - "--platform=linux/arm64/v8" + - "--label=org.opencontainers.image.created={{.Date}}" + - "--label=org.opencontainers.image.title={{.ProjectName}}/gnofaucet" + - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.version={{.Version}}" + ids: + - gnofaucet + - use: buildx + dockerfile: Dockerfile.release + goos: linux + goarch: arm + goarm: 6 + image_templates: + - "ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Version }}-armv6" + - "ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Env.TAG_VERSION }}-armv6" + build_flag_templates: + - "--target=gnofaucet" + - "--platform=linux/arm/v6" + - "--label=org.opencontainers.image.created={{.Date}}" + - "--label=org.opencontainers.image.title={{.ProjectName}}/gnofaucet" + - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.version={{.Version}}" + ids: + - gnofaucet + - use: buildx + dockerfile: Dockerfile.release + goos: linux + goarch: arm + goarm: 7 + image_templates: + - "ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Version }}-armv7" + - "ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Env.TAG_VERSION }}-armv7" + build_flag_templates: + - "--target=gnofaucet" + - "--platform=linux/arm/v7" + - "--label=org.opencontainers.image.created={{.Date}}" + - "--label=org.opencontainers.image.title={{.ProjectName}}/gnofaucet" + - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.version={{.Version}}" + ids: + - gnofaucet + docker_manifests: # https://goreleaser.com/customization/docker_manifest/ @@ -409,12 +499,12 @@ docker_manifests: - ghcr.io/gnolang/{{ .ProjectName }}:{{ .Version }}-arm64v8 - ghcr.io/gnolang/{{ .ProjectName }}:{{ .Version }}-armv6 - ghcr.io/gnolang/{{ .ProjectName }}:{{ .Version }}-armv7 - - name_template: ghcr.io/gnolang/{{ .ProjectName }}:latest + - name_template: ghcr.io/gnolang/{{ .ProjectName }}:{{ .Env.TAG_VERSION }} image_templates: - - ghcr.io/gnolang/{{ .ProjectName }}:latest-amd64 - - ghcr.io/gnolang/{{ .ProjectName }}:latest-arm64v8 - - ghcr.io/gnolang/{{ .ProjectName }}:latest-armv6 - - ghcr.io/gnolang/{{ .ProjectName }}:latest-armv7 + - ghcr.io/gnolang/{{ .ProjectName }}:{{ .Env.TAG_VERSION }}-amd64 + - ghcr.io/gnolang/{{ .ProjectName }}:{{ .Env.TAG_VERSION }}-arm64v8 + - ghcr.io/gnolang/{{ .ProjectName }}:{{ .Env.TAG_VERSION }}-armv6 + - ghcr.io/gnolang/{{ .ProjectName }}:{{ .Env.TAG_VERSION }}-armv7 # gnoland - name_template: ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Version }} @@ -423,12 +513,12 @@ docker_manifests: - ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Version }}-arm64v8 - ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Version }}-armv6 - ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Version }}-armv7 - - name_template: ghcr.io/gnolang/{{ .ProjectName }}/gnoland:latest + - name_template: ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Env.TAG_VERSION }} image_templates: - - ghcr.io/gnolang/{{ .ProjectName }}/gnoland:latest-amd64 - - ghcr.io/gnolang/{{ .ProjectName }}/gnoland:latest-arm64v8 - - ghcr.io/gnolang/{{ .ProjectName }}/gnoland:latest-armv6 - - ghcr.io/gnolang/{{ .ProjectName }}/gnoland:latest-armv7 + - ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Env.TAG_VERSION }}-amd64 + - ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Env.TAG_VERSION }}-arm64v8 + - ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Env.TAG_VERSION }}-armv6 + - ghcr.io/gnolang/{{ .ProjectName }}/gnoland:{{ .Env.TAG_VERSION }}-armv7 # gnokey - name_template: ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Version }} @@ -437,13 +527,13 @@ docker_manifests: - ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Version }}-arm64v8 - ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Version }}-armv6 - ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Version }}-armv7 - - name_template: ghcr.io/gnolang/{{ .ProjectName }}/gnokey:latest + - name_template: ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Env.TAG_VERSION }} image_templates: - - ghcr.io/gnolang/{{ .ProjectName }}/gnokey:latest-amd64 - - ghcr.io/gnolang/{{ .ProjectName }}/gnokey:latest-arm64v8 - - ghcr.io/gnolang/{{ .ProjectName }}/gnokey:latest-armv6 - - ghcr.io/gnolang/{{ .ProjectName }}/gnokey:latest-armv7 - + - ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Env.TAG_VERSION }}-amd64 + - ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Env.TAG_VERSION }}-arm64v8 + - ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Env.TAG_VERSION }}-armv6 + - ghcr.io/gnolang/{{ .ProjectName }}/gnokey:{{ .Env.TAG_VERSION }}-armv7 + # gnoweb - name_template: ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Version }} image_templates: @@ -451,12 +541,26 @@ docker_manifests: - ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Version }}-arm64v8 - ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Version }}-armv6 - ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Version }}-armv7 - - name_template: ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:latest + - name_template: ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Env.TAG_VERSION }} image_templates: - - ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:latest-amd64 - - ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:latest-arm64v8 - - ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:latest-armv6 - - ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:latest-armv7 + - ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Env.TAG_VERSION }}-amd64 + - ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Env.TAG_VERSION }}-arm64v8 + - ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Env.TAG_VERSION }}-armv6 + - ghcr.io/gnolang/{{ .ProjectName }}/gnoweb:{{ .Env.TAG_VERSION }}-armv7 + + # gnofaucet + - name_template: ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Version }} + image_templates: + - ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Version }}-amd64 + - ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Version }}-arm64v8 + - ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Version }}-armv6 + - ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Version }}-armv7 + - name_template: ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Env.TAG_VERSION }} + image_templates: + - ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Env.TAG_VERSION }}-amd64 + - ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Env.TAG_VERSION }}-arm64v8 + - ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Env.TAG_VERSION }}-armv6 + - ghcr.io/gnolang/{{ .ProjectName }}/gnofaucet:{{ .Env.TAG_VERSION }}-armv7 docker_signs: - cmd: cosign @@ -484,6 +588,8 @@ sboms: artifacts: source release: + disable: '{{ if eq .Env.TAG_VERSION "master" }}true{{ else }}false{{ end }}' + skip_upload: '{{ if eq .Env.TAG_VERSION "master" }}true{{ else }}false{{ end }}' draft: true replace_existing_draft: true prerelease: auto @@ -493,4 +599,11 @@ release: You can find all docker images at: - https://github.com/orgs/gnolang/packages?repo_name={{ .ProjectName }} \ No newline at end of file + https://github.com/orgs/gnolang/packages?repo_name={{ .ProjectName }} + +# Only valid for nightly build +nightly: + tag_name: nightly + publish_release: true + keep_single_release: true + name_template: "{{ incpatch .Version }}-{{ .ShortCommit }}-{{ .Env.TAG_VERSION }}" diff --git a/.github/workflows/lint_template.yml b/.github/workflows/lint_template.yml index 098650c1df2..65679633240 100644 --- a/.github/workflows/lint_template.yml +++ b/.github/workflows/lint_template.yml @@ -1,12 +1,12 @@ on: workflow_call: - inputs: - modulepath: - required: true - type: string - go-version: - required: true - type: string + inputs: + modulepath: + required: true + type: string + go-version: + required: true + type: string jobs: @@ -25,3 +25,4 @@ jobs: working-directory: ${{ inputs.modulepath }} args: --config=${{ github.workspace }}/.github/golangci.yml + version: v1.59 # sync with misc/devdeps \ No newline at end of file diff --git a/.github/workflows/nightlies.yml b/.github/workflows/nightlies.yml index 6e3e3e58935..0110801dc93 100644 --- a/.github/workflows/nightlies.yml +++ b/.github/workflows/nightlies.yml @@ -38,8 +38,9 @@ jobs: - uses: goreleaser/goreleaser-action@v6 with: distribution: goreleaser-pro - version: v1.26.2-pro - args: release --clean --nightly --config ./.github/goreleaser-nightly.yaml + version: ~> v2 + args: release --clean --nightly --config ./.github/goreleaser.yaml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} + TAG_VERSION: nightly diff --git a/.github/workflows/releaser-master.yml b/.github/workflows/releaser-master.yml index 3ed5353ec89..96a622e3272 100644 --- a/.github/workflows/releaser-master.yml +++ b/.github/workflows/releaser-master.yml @@ -39,8 +39,9 @@ jobs: - uses: goreleaser/goreleaser-action@v6 with: distribution: goreleaser-pro - version: v1.26.2-pro - args: release --clean --nightly --config ./.github/goreleaser-master.yaml + version: ~> v2 + args: release --clean --nightly --config ./.github/goreleaser.yaml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }} + TAG_VERSION: master diff --git a/.github/workflows/releaser.yml b/.github/workflows/releaser.yml index 9148d9ac15c..f3317419510 100644 --- a/.github/workflows/releaser.yml +++ b/.github/workflows/releaser.yml @@ -38,7 +38,7 @@ jobs: - uses: goreleaser/goreleaser-action@v6 with: distribution: goreleaser-pro - version: v1.26.2-pro + version: ~> v2 args: release --clean --config ./.github/goreleaser.yaml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Dockerfile.release b/Dockerfile.release index 2e36453382e..644f8cb5de9 100644 --- a/Dockerfile.release +++ b/Dockerfile.release @@ -35,6 +35,13 @@ COPY ./gnoweb /usr/bin/gnoweb EXPOSE 8888 ENTRYPOINT [ "/usr/bin/gnoweb" ] +# +## ghcr.io/gnolang/gno/gnofaucet +FROM base as gnofaucet + +COPY ./gnofaucet /usr/bin/gnofaucet +EXPOSE 5050 +ENTRYPOINT [ "/usr/bin/gnofaucet" ] # ## ghcr.io/gnolang/gno diff --git a/contribs/gnofaucet/go.mod b/contribs/gnofaucet/go.mod index 0662de5f82f..c56c0b7d425 100644 --- a/contribs/gnofaucet/go.mod +++ b/contribs/gnofaucet/go.mod @@ -5,8 +5,8 @@ go 1.22 toolchain go1.22.4 require ( - github.com/gnolang/faucet v0.2.1 - github.com/gnolang/gno v0.1.0-nightly.20240627 + github.com/gnolang/faucet v0.3.2 + github.com/gnolang/gno v0.1.1 github.com/stretchr/testify v1.9.0 go.uber.org/zap v1.27.0 golang.org/x/time v0.5.0 @@ -19,9 +19,10 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect - github.com/go-chi/chi/v5 v5.0.12 // indirect - github.com/go-logr/logr v1.4.1 // indirect + github.com/go-chi/chi/v5 v5.1.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect @@ -29,27 +30,27 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rs/cors v1.11.0 // indirect github.com/rs/xid v1.5.0 // indirect - go.opentelemetry.io/otel v1.27.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.26.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 // indirect - go.opentelemetry.io/otel/metric v1.27.0 // indirect - go.opentelemetry.io/otel/sdk v1.27.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.27.0 // indirect - go.opentelemetry.io/otel/trace v1.27.0 // indirect - go.opentelemetry.io/proto/otlp v1.2.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap/exp v0.2.0 // indirect - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.25.0 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect + golang.org/x/mod v0.19.0 // indirect + golang.org/x/net v0.27.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/term v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect - google.golang.org/grpc v1.64.0 // indirect - google.golang.org/protobuf v1.34.1 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/term v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/contribs/gnofaucet/go.sum b/contribs/gnofaucet/go.sum index 29dc3efcb76..1508cdae1e6 100644 --- a/contribs/gnofaucet/go.sum +++ b/contribs/gnofaucet/go.sum @@ -45,17 +45,17 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8 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/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/gnolang/faucet v0.2.1 h1:ohYzZEuweqwTJBiutZbWFgb4w2slI6/JKobGPmiwG0g= -github.com/gnolang/faucet v0.2.1/go.mod h1:cOWkLBbgJ9mjOiYW4DBrZ0P5m0+14pTLaERF7v/5b+4= -github.com/gnolang/gno v0.1.0-nightly.20240627 h1:ZfVzzr2nadX5NLsZ76WN2CLb7TTuMJMwCdqTBZXVLGo= -github.com/gnolang/gno v0.1.0-nightly.20240627/go.mod h1:WCOCLF55BgFd2cw/Rrqa4zk9nK4AVVvbrPKprhkG4Wo= +github.com/gnolang/faucet v0.3.2 h1:3QBrdmnQszRaAZbxgO5xDDm3czNa0L/RFmhnCkbxy5I= +github.com/gnolang/faucet v0.3.2/go.mod h1:/wbw9h4ooMzzyNBuM0X+ol7CiPH2OFjAFF3bYAXqA7U= +github.com/gnolang/gno v0.1.1 h1:t41S0SWIUa3syI7XpRAuCneCgRc8gOJ2g8DkUedF72U= +github.com/gnolang/gno v0.1.1/go.mod h1:BTaBNeaoY/W95NN6QA4RCoQ6Z7mi8M+Zb1I1wMWGg2w= 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/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= -github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -74,6 +74,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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= @@ -119,22 +121,22 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= -go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= -go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.26.0 h1:+hm+I+KigBy3M24/h1p/NHkUx/evbLH0PNcjpMyCHc4= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.26.0/go.mod h1:NjC8142mLvvNT6biDpaMjyz78kyEHIwAJlSX0N9P5KI= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 h1:CIHWikMsN3wO+wq1Tp5VGdVRTcON+DmOJSfDjXypKOc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0/go.mod h1:TNupZ6cxqyFEpLXAZW7On+mLFL0/g0TE3unIYL91xWc= -go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= -go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= -go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= -go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= -go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= -go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= -go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= -go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= -go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= -go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 h1:U2guen0GhqH8o/G2un8f/aG/y++OuW6MyCo6hT9prXk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0/go.mod h1:yeGZANgEcpdx/WK0IvvRFC+2oLiMS2u4L/0Rj2M2Qr0= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 h1:aLmmtjRke7LPDQ3lvpFz+kNEH43faFhzW7v8BFIEydg= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0/go.mod h1:TC1pyCt6G9Sjb4bQpShH+P5R53pO6ZuGnHuuln9xMeE= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08= +go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -146,19 +148,19 @@ 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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-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.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -171,34 +173,34 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w 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.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= 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.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ= -google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= 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.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/docs/gno-tooling/cli/gnodev.md b/docs/gno-tooling/cli/gnodev.md index 4a1880822fc..f9491fea803 100644 --- a/docs/gno-tooling/cli/gnodev.md +++ b/docs/gno-tooling/cli/gnodev.md @@ -105,7 +105,7 @@ A specific deposit amount can also be set with the following pattern: gnodev ./myrealm?deposit=42ugnot ``` -This patten can be expanded to accommodate both options: +This pattern can be expanded to accommodate both options: ``` gnodev ./myrealm?creator=&deposit= diff --git a/docs/reference/stdlibs/std/chain.md b/docs/reference/stdlibs/std/chain.md index f8d3cba13bb..089de682cfd 100644 --- a/docs/reference/stdlibs/std/chain.md +++ b/docs/reference/stdlibs/std/chain.md @@ -85,7 +85,7 @@ Returns the original signer of the transaction. #### Usage ```go -caller := std.GetOrigSend() +caller := std.GetOrigCaller() ``` --- diff --git a/examples/gno.land/p/demo/entropy/entropy.gno b/examples/gno.land/p/demo/entropy/entropy.gno new file mode 100644 index 00000000000..5e35b8c7227 --- /dev/null +++ b/examples/gno.land/p/demo/entropy/entropy.gno @@ -0,0 +1,89 @@ +// Entropy generates fully deterministic, cost-effective, and hard to guess +// numbers. +// +// It is designed both for single-usage, like seeding math/rand or for being +// reused which increases the entropy and its cost effectiveness. +// +// Disclaimer: this package is unsafe and won't prevent others to guess values +// in advance. +// +// It uses the Bernstein's hash djb2 to be CPU-cycle efficient. +package entropy + +import ( + "math" + "std" + "time" +) + +type Instance struct { + value uint32 +} + +func New() *Instance { + r := Instance{value: 5381} + r.addEntropy() + return &r +} + +func FromSeed(seed uint32) *Instance { + r := Instance{value: seed} + r.addEntropy() + return &r +} + +func (i *Instance) Seed() uint32 { + return i.value +} + +func (i *Instance) djb2String(input string) { + for _, c := range input { + i.djb2Uint32(uint32(c)) + } +} + +// super fast random algorithm. +// http://www.cse.yorku.ca/~oz/hash.html +func (i *Instance) djb2Uint32(input uint32) { + i.value = (i.value << 5) + i.value + input +} + +// AddEntropy uses various runtime variables to add entropy to the existing seed. +func (i *Instance) addEntropy() { + // FIXME: reapply the 5381 initial value? + + // inherit previous entropy + // nothing to do + + // handle callers + { + caller1 := std.GetCallerAt(1).String() + i.djb2String(caller1) + caller2 := std.GetCallerAt(2).String() + i.djb2String(caller2) + } + + // height + { + height := std.GetHeight() + if height >= math.MaxUint32 { + height -= math.MaxUint32 + } + i.djb2Uint32(uint32(height)) + } + + // time + { + secs := time.Now().Second() + i.djb2Uint32(uint32(secs)) + nsecs := time.Now().Nanosecond() + i.djb2Uint32(uint32(nsecs)) + } + + // FIXME: compute other hard-to-guess but deterministic variables, like real gas? +} + +func (i *Instance) Value() uint32 { + i.addEntropy() + return i.value +} diff --git a/examples/gno.land/p/demo/entropy/entropy_test.gno b/examples/gno.land/p/demo/entropy/entropy_test.gno new file mode 100644 index 00000000000..0deb3ab9aa2 --- /dev/null +++ b/examples/gno.land/p/demo/entropy/entropy_test.gno @@ -0,0 +1,46 @@ +package entropy + +import ( + "std" + "strconv" + "testing" +) + +func TestInstance(t *testing.T) { + instance := New() + if instance == nil { + t.Errorf("instance should not be nil") + } +} + +func TestInstanceValue(t *testing.T) { + baseEntropy := New() + baseResult := computeValue(t, baseEntropy) + + sameHeightEntropy := New() + sameHeightResult := computeValue(t, sameHeightEntropy) + + if baseResult != sameHeightResult { + t.Errorf("should have the same result: new=%s, base=%s", sameHeightResult, baseResult) + } + + std.TestSkipHeights(1) + differentHeightEntropy := New() + differentHeightResult := computeValue(t, differentHeightEntropy) + + if baseResult == differentHeightResult { + t.Errorf("should have different result: new=%s, base=%s", differentHeightResult, baseResult) + } +} + +func computeValue(t *testing.T, r *Instance) string { + t.Helper() + + out := "" + for i := 0; i < 10; i++ { + val := int(r.Value()) + out += strconv.Itoa(val) + " " + } + + return out +} diff --git a/examples/gno.land/p/demo/entropy/gno.mod b/examples/gno.land/p/demo/entropy/gno.mod new file mode 100644 index 00000000000..9a6db8f5b61 --- /dev/null +++ b/examples/gno.land/p/demo/entropy/gno.mod @@ -0,0 +1 @@ +module gno.land/p/demo/entropy diff --git a/examples/gno.land/p/demo/entropy/z_filetest.gno b/examples/gno.land/p/demo/entropy/z_filetest.gno new file mode 100644 index 00000000000..85ed1b10a3d --- /dev/null +++ b/examples/gno.land/p/demo/entropy/z_filetest.gno @@ -0,0 +1,56 @@ +package main + +import ( + "std" + + "gno.land/p/demo/entropy" +) + +func main() { + // initial + println("---") + r := entropy.New() + println(r.Value()) + println(r.Value()) + println(r.Value()) + println(r.Value()) + println(r.Value()) + + // should be the same + println("---") + r = entropy.New() + println(r.Value()) + println(r.Value()) + println(r.Value()) + println(r.Value()) + println(r.Value()) + + std.TestSkipHeights(1) + println("---") + r = entropy.New() + println(r.Value()) + println(r.Value()) + println(r.Value()) + println(r.Value()) + println(r.Value()) +} + +// Output: +// --- +// 4129293727 +// 2141104956 +// 1950222777 +// 3348280598 +// 438354259 +// --- +// 4129293727 +// 2141104956 +// 1950222777 +// 3348280598 +// 438354259 +// --- +// 49506731 +// 1539580078 +// 2695928529 +// 1895482388 +// 3462727799 diff --git a/examples/gno.land/p/demo/ownable/errors.gno b/examples/gno.land/p/demo/ownable/errors.gno index ffbf6ab3f6f..89776a6cf12 100644 --- a/examples/gno.land/p/demo/ownable/errors.gno +++ b/examples/gno.land/p/demo/ownable/errors.gno @@ -3,6 +3,6 @@ package ownable import "errors" var ( - ErrUnauthorized = errors.New("unauthorized; caller is not owner") - ErrInvalidAddress = errors.New("new owner address is invalid") + ErrUnauthorized = errors.New("ownable: caller is not owner") + ErrInvalidAddress = errors.New("ownable: new owner address is invalid") ) diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno new file mode 100644 index 00000000000..f9f0ea15dd9 --- /dev/null +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable.gno @@ -0,0 +1,90 @@ +// Package authorizable is an extension of p/demo/ownable; +// It allows the user to instantiate an Authorizable struct, which extends +// p/demo/ownable with a list of users that are authorized for something. +// By using authorizable, you have a superuser (ownable), as well as another +// authorization level, which can be used for adding moderators or similar to your realm. +package authorizable + +import ( + "std" + + "gno.land/p/demo/avl" + "gno.land/p/demo/ownable" + "gno.land/p/demo/ufmt" +) + +type Authorizable struct { + *ownable.Ownable // owner in ownable is superuser + authorized *avl.Tree // std.Addr > struct{}{} +} + +func NewAuthorizable() *Authorizable { + a := &Authorizable{ + ownable.New(), + avl.NewTree(), + } + + // Add owner to auth list + a.authorized.Set(a.Owner().String(), struct{}{}) + return a +} + +func NewAuthorizableWithAddress(addr std.Address) *Authorizable { + a := &Authorizable{ + ownable.NewWithAddress(addr), + avl.NewTree(), + } + + // Add owner to auth list + a.authorized.Set(a.Owner().String(), struct{}{}) + return a +} + +func (a *Authorizable) AddToAuthList(addr std.Address) error { + if err := a.CallerIsOwner(); err != nil { + return ErrNotSuperuser + } + + if _, exists := a.authorized.Get(addr.String()); exists { + return ErrAlreadyInList + } + + a.authorized.Set(addr.String(), struct{}{}) + + return nil +} + +func (a *Authorizable) DeleteFromAuthList(addr std.Address) error { + if err := a.CallerIsOwner(); err != nil { + return ErrNotSuperuser + } + + if !a.authorized.Has(addr.String()) { + return ErrNotInAuthList + } + + if _, removed := a.authorized.Remove(addr.String()); !removed { + str := ufmt.Sprintf("authorizable: could not remove %s from auth list", addr.String()) + panic(str) + } + + return nil +} + +func (a Authorizable) CallerOnAuthList() error { + caller := std.PrevRealm().Addr() + + if !a.authorized.Has(caller.String()) { + return ErrNotInAuthList + } + + return nil +} + +func (a Authorizable) AssertOnAuthList() { + caller := std.PrevRealm().Addr() + + if !a.authorized.Has(caller.String()) { + panic(ErrNotInAuthList) + } +} diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno new file mode 100644 index 00000000000..10a5e411bdb --- /dev/null +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/authorizable_test.gno @@ -0,0 +1,116 @@ +package authorizable + +import ( + "std" + "testing" + + "gno.land/p/demo/testutils" + "gno.land/p/demo/uassert" +) + +var ( + alice = testutils.TestAddress("alice") + bob = testutils.TestAddress("bob") + charlie = testutils.TestAddress("charlie") +) + +func TestNewAuthorizable(t *testing.T) { + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) // TODO(bug, issue #2371): should not be needed + + a := NewAuthorizable() + got := a.Owner() + + if alice != got { + t.Fatalf("Expected %s, got: %s", alice, got) + } +} + +func TestNewAuthorizableWithAddress(t *testing.T) { + a := NewAuthorizableWithAddress(alice) + + got := a.Owner() + + if alice != got { + t.Fatalf("Expected %s, got: %s", alice, got) + } +} + +func TestCallerOnAuthList(t *testing.T) { + a := NewAuthorizableWithAddress(alice) + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) + + if err := a.CallerOnAuthList(); err == ErrNotInAuthList { + t.Fatalf("expected alice to be on the list") + } +} + +func TestNotCallerOnAuthList(t *testing.T) { + a := NewAuthorizableWithAddress(alice) + std.TestSetRealm(std.NewUserRealm(bob)) + std.TestSetOrigCaller(bob) + + if err := a.CallerOnAuthList(); err == nil { + t.Fatalf("expected bob to not be on the list") + } +} + +func TestAddToAuthList(t *testing.T) { + a := NewAuthorizableWithAddress(alice) + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) + + if err := a.AddToAuthList(bob); err != nil { + t.Fatalf("Expected no error, got %v", err) + } + + std.TestSetRealm(std.NewUserRealm(bob)) + std.TestSetOrigCaller(bob) + + if err := a.AddToAuthList(bob); err == nil { + t.Fatalf("Expected AddToAuth to error while bob called it, but it didn't") + } +} + +func TestDeleteFromList(t *testing.T) { + a := NewAuthorizableWithAddress(alice) + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) + + if err := a.AddToAuthList(bob); err != nil { + t.Fatalf("Expected no error, got %v", err) + } + + if err := a.AddToAuthList(charlie); err != nil { + t.Fatalf("Expected no error, got %v", err) + } + + std.TestSetRealm(std.NewUserRealm(bob)) + std.TestSetOrigCaller(bob) + + // Try an unauthorized deletion + if err := a.DeleteFromAuthList(alice); err == nil { + t.Fatalf("Expected DelFromAuth to error with %v", err) + } + + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) + + if err := a.DeleteFromAuthList(charlie); err != nil { + t.Fatalf("Expected no error, got %v", err) + } +} + +func TestAssertOnList(t *testing.T) { + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) + a := NewAuthorizableWithAddress(alice) + + std.TestSetRealm(std.NewUserRealm(bob)) + std.TestSetOrigCaller(bob) + + uassert.PanicsWithMessage(t, ErrNotInAuthList.Error(), func() { + a.AssertOnAuthList() + }) +} diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno b/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno new file mode 100644 index 00000000000..4ba5983bccb --- /dev/null +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/errors.gno @@ -0,0 +1,9 @@ +package authorizable + +import "errors" + +var ( + ErrNotInAuthList = errors.New("authorizable: caller is not in authorized list") + ErrNotSuperuser = errors.New("authorizable: caller is not superuser") + ErrAlreadyInList = errors.New("authorizable: address is already in authorized list") +) diff --git a/examples/gno.land/p/demo/ownable/exts/authorizable/gno.mod b/examples/gno.land/p/demo/ownable/exts/authorizable/gno.mod new file mode 100644 index 00000000000..f36823f3f71 --- /dev/null +++ b/examples/gno.land/p/demo/ownable/exts/authorizable/gno.mod @@ -0,0 +1,9 @@ +module gno.land/p/demo/ownable/exts/authorizable + +require ( + gno.land/p/demo/avl v0.0.0-latest + gno.land/p/demo/ownable v0.0.0-latest + gno.land/p/demo/testutils v0.0.0-latest + gno.land/p/demo/uassert v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest +) diff --git a/examples/gno.land/p/demo/ownable/ownable.gno b/examples/gno.land/p/demo/ownable/ownable.gno index 75ebcde0a28..a77b22461a9 100644 --- a/examples/gno.land/p/demo/ownable/ownable.gno +++ b/examples/gno.land/p/demo/ownable/ownable.gno @@ -1,8 +1,6 @@ package ownable -import ( - "std" -) +import "std" const OwnershipTransferEvent = "OwnershipTransfer" @@ -19,7 +17,9 @@ func New() *Ownable { } func NewWithAddress(addr std.Address) *Ownable { - return &Ownable{owner: addr} + return &Ownable{ + owner: addr, + } } // TransferOwnership transfers ownership of the Ownable struct to a new address @@ -40,6 +40,7 @@ func (o *Ownable) TransferOwnership(newOwner std.Address) error { "from", string(prevOwner), "to", string(newOwner), ) + return nil } @@ -64,6 +65,7 @@ func (o *Ownable) DropOwnership() error { return nil } +// Owner returns the owner address from Ownable func (o Ownable) Owner() std.Address { return o.owner } @@ -73,9 +75,11 @@ func (o Ownable) CallerIsOwner() error { if std.PrevRealm().Addr() == o.owner { return nil } + return ErrUnauthorized } +// AssertCallerIsOwner panics if the caller is not the owner func (o Ownable) AssertCallerIsOwner() { if std.PrevRealm().Addr() != o.owner { panic(ErrUnauthorized) diff --git a/examples/gno.land/p/demo/ownable/ownable_test.gno b/examples/gno.land/p/demo/ownable/ownable_test.gno index 6217948d587..a9d97154f45 100644 --- a/examples/gno.land/p/demo/ownable/ownable_test.gno +++ b/examples/gno.land/p/demo/ownable/ownable_test.gno @@ -9,52 +9,60 @@ import ( ) var ( - firstCaller = testutils.TestAddress("first") - secondCaller = testutils.TestAddress("second") + alice = testutils.TestAddress("alice") + bob = testutils.TestAddress("bob") ) func TestNew(t *testing.T) { - std.TestSetRealm(std.NewUserRealm(firstCaller)) - std.TestSetOrigCaller(firstCaller) // TODO(bug): should not be needed + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) // TODO(bug): should not be needed o := New() got := o.Owner() - uassert.Equal(t, firstCaller, got) + if alice != got { + t.Fatalf("Expected %s, got: %s", alice, got) + } } func TestNewWithAddress(t *testing.T) { - o := NewWithAddress(firstCaller) + o := NewWithAddress(alice) got := o.Owner() - uassert.Equal(t, firstCaller, got) + if alice != got { + t.Fatalf("Expected %s, got: %s", alice, got) + } } func TestOwner(t *testing.T) { - std.TestSetRealm(std.NewUserRealm(firstCaller)) + std.TestSetRealm(std.NewUserRealm(alice)) o := New() - expected := firstCaller + expected := alice got := o.Owner() uassert.Equal(t, expected, got) } func TestTransferOwnership(t *testing.T) { - std.TestSetRealm(std.NewUserRealm(firstCaller)) + std.TestSetRealm(std.NewUserRealm(alice)) o := New() - err := o.TransferOwnership(secondCaller) - uassert.NoError(t, err, "TransferOwnership failed") + err := o.TransferOwnership(bob) + if err != nil { + t.Fatalf("TransferOwnership failed, %v", err) + } got := o.Owner() - uassert.Equal(t, secondCaller, got) + if bob != got { + t.Fatalf("Expected: %s, got: %s", bob, got) + } } func TestCallerIsOwner(t *testing.T) { - std.TestSetRealm(std.NewUserRealm(firstCaller)) + std.TestSetRealm(std.NewUserRealm(alice)) o := New() - unauthorizedCaller := secondCaller + unauthorizedCaller := bob std.TestSetRealm(std.NewUserRealm(unauthorizedCaller)) std.TestSetOrigCaller(unauthorizedCaller) // TODO(bug): should not be needed @@ -64,7 +72,7 @@ func TestCallerIsOwner(t *testing.T) { } func TestDropOwnership(t *testing.T) { - std.TestSetRealm(std.NewUserRealm(firstCaller)) + std.TestSetRealm(std.NewUserRealm(alice)) o := New() @@ -78,23 +86,25 @@ func TestDropOwnership(t *testing.T) { // Errors func TestErrUnauthorized(t *testing.T) { - std.TestSetRealm(std.NewUserRealm(firstCaller)) - std.TestSetOrigCaller(firstCaller) // TODO(bug): should not be needed + std.TestSetRealm(std.NewUserRealm(alice)) + std.TestSetOrigCaller(alice) // TODO(bug): should not be needed o := New() - std.TestSetRealm(std.NewUserRealm(secondCaller)) - std.TestSetOrigCaller(secondCaller) // TODO(bug): should not be needed + std.TestSetRealm(std.NewUserRealm(bob)) + std.TestSetOrigCaller(bob) // TODO(bug): should not be needed - err := o.TransferOwnership(firstCaller) - uassert.ErrorContains(t, err, ErrUnauthorized.Error()) + err := o.TransferOwnership(alice) + if err != ErrUnauthorized { + t.Fatalf("Should've been ErrUnauthorized, was %v", err) + } err = o.DropOwnership() uassert.ErrorContains(t, err, ErrUnauthorized.Error()) } func TestErrInvalidAddress(t *testing.T) { - std.TestSetRealm(std.NewUserRealm(firstCaller)) + std.TestSetRealm(std.NewUserRealm(alice)) o := New() diff --git a/examples/gno.land/r/demo/art/gnoface/gno.mod b/examples/gno.land/r/demo/art/gnoface/gno.mod index eb6f44d4026..072c98f3bd6 100644 --- a/examples/gno.land/r/demo/art/gnoface/gno.mod +++ b/examples/gno.land/r/demo/art/gnoface/gno.mod @@ -1,6 +1,7 @@ module gno.land/r/demo/art/gnoface require ( + gno.land/p/demo/entropy v0.0.0-latest gno.land/p/demo/uassert v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest ) diff --git a/examples/gno.land/r/demo/art/gnoface/gnoface.gno b/examples/gno.land/r/demo/art/gnoface/gnoface.gno index 9e85c5c7387..b4bc8e222e5 100644 --- a/examples/gno.land/r/demo/art/gnoface/gnoface.gno +++ b/examples/gno.land/r/demo/art/gnoface/gnoface.gno @@ -2,15 +2,15 @@ package gnoface import ( "math/rand" - "std" "strconv" "strings" + "gno.land/p/demo/entropy" "gno.land/p/demo/ufmt" ) func Render(path string) string { - seed := uint64(std.GetHeight()) + seed := uint64(entropy.New().Value()) path = strings.TrimSpace(path) if path != "" { diff --git a/examples/gno.land/r/gnoland/events/administration.gno b/examples/gno.land/r/gnoland/events/administration.gno new file mode 100644 index 00000000000..02914adee69 --- /dev/null +++ b/examples/gno.land/r/gnoland/events/administration.gno @@ -0,0 +1,26 @@ +package events + +import ( + "std" + + "gno.land/p/demo/ownable/exts/authorizable" +) + +var ( + su = std.Address("g125em6arxsnj49vx35f0n0z34putv5ty3376fg5") // @leohhhn + auth = authorizable.NewAuthorizableWithAddress(su) +) + +// GetOwner gets the owner of the events realm +func GetOwner() std.Address { + return auth.Owner() +} + +// AddModerator adds a moderator to the events realm +func AddModerator(mod std.Address) { + auth.AssertCallerIsOwner() + + if err := auth.AddToAuthList(mod); err != nil { + panic(err) + } +} diff --git a/examples/gno.land/r/gnoland/events/errors.gno b/examples/gno.land/r/gnoland/events/errors.gno new file mode 100644 index 00000000000..fb44d3c9f82 --- /dev/null +++ b/examples/gno.land/r/gnoland/events/errors.gno @@ -0,0 +1,18 @@ +package events + +import ( + "errors" + "strconv" +) + +var ( + ErrEmptyName = errors.New("event name cannot be empty") + ErrNoSuchID = errors.New("event with specified ID does not exist") + ErrMinWidgetSize = errors.New("you need to request at least 1 event to render") + ErrMaxWidgetSize = errors.New("maximum number of events in widget is" + strconv.Itoa(MaxWidgetSize)) + ErrDescriptionTooLong = errors.New("event description is too long") + ErrInvalidStartTime = errors.New("invalid start time format") + ErrInvalidEndTime = errors.New("invalid end time format") + ErrEndBeforeStart = errors.New("end time cannot be before start time") + ErrStartEndTimezonemMismatch = errors.New("start and end timezones are not the same") +) diff --git a/examples/gno.land/r/gnoland/events/events.gno b/examples/gno.land/r/gnoland/events/events.gno index 9c2708a112e..0984edf75a9 100644 --- a/examples/gno.land/r/gnoland/events/events.gno +++ b/examples/gno.land/r/gnoland/events/events.gno @@ -1,240 +1,199 @@ +// Package events allows you to upload data about specific IRL/online events +// It includes dynamic support for updating rendering events based on their +// status, ie if they are upcoming, in progress, or in the past. package events import ( - "gno.land/p/demo/ui" -) - -// 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/events:"} - dom.Title = "Gno.land Core Team Attends Industry Events & Meetups" - dom.Classes = []string{"gno-tmpl-section"} + "sort" + "std" + "strings" + "time" - // body - dom.Body.Append(introSection()...) - dom.Body.Append(ui.HR{}) - dom.Body.Append(upcomingEvents()...) - dom.Body.Append(ui.HR{}) - dom.Body.Append(pastEvents()...) - - return dom.String() -} + "gno.land/p/demo/seqid" + "gno.land/p/demo/ufmt" +) -func introSection() ui.Element { - return ui.Element{ - ui.Paragraph("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."), +type ( + Event struct { + id string + name string // name of event + description string // short description of event + link string // link to auth corresponding web2 page, ie eventbrite/luma or conference page + location string // location of the event + startTime time.Time // given in RFC3339 + endTime time.Time // end time of the event, given in RFC3339 } -} - -func upcomingEvents() ui.Element { - return ui.Element{ - ui.H2("Upcoming Events"), - ui.Text(`
-
- -### GopherCon EU -- Come Meet Us at our Booth -- Berlin, June 17 - 20, 2024 - -[Learn More](https://gophercon.eu/) -
-
- -### GopherCon US -- Come Meet Us at our Booth -- Chicago, July 7 - 10, 2024 - -[Learn More](https://www.gophercon.com/) - -
- -
- -### Nebular Summit -- Join our workshop -- Brussels, July 12 - 13, 2024 + eventsSlice []*Event +) -[Learn More](https://nebular.builders/) -
+var ( + events = make(eventsSlice, 0) // sorted + idCounter seqid.ID +) -
+const ( + maxDescLength = 100 + EventAdded = "EventAdded" + EventDeleted = "EventDeleted" + EventEdited = "EventEdited" +) -
-
+// AddEvent adds auth new event +// Start time & end time need to be specified in RFC3339, ie 2024-08-08T12:00:00+02:00 +func AddEvent(name, description, link, location, startTime, endTime string) (string, error) { + auth.AssertOnAuthList() -
-
`), + if strings.TrimSpace(name) == "" { + return "", ErrEmptyName } -} - -func pastEvents() ui.Element { - return ui.Element{ - ui.H2("Past Events"), - ui.Text(`
- -
- -### Gno @ Golang Serbia - -- **Join the meetup** -- Belgrade, May 23, 2024 - -[Learn more](https://gno.land/r/gnoland/blog:p/gnomes-in-serbia) - -
- -
- -### Intro to Gno Tokyo - -- **Join the meetup** -- Tokyo, April 11, 2024 - -[Learn more](https://gno.land/r/gnoland/blog:p/gno-tokyo) - -
- -
- -### Go to Gno Seoul - -- **Join the workshop** -- Seoul, March 23, 2024 - -[Learn more](https://medium.com/onbloc/go-to-gno-recap-intro-to-the-gno-stack-with-memeland-284a43d7f620) - -
- -
- -### GopherCon US - -- **Come Meet Us at our Booth** -- San Diego, September 26 - 29, 2023 - -[Learn more](https://www.gophercon.com/) - -
-
- -### GopherCon EU - -- **Come Meet Us at our Booth** -- Berlin, July 26 - 29, 2023 - -[Learn more](https://gophercon.eu/) - -
- -
- -### Nebular Summit Gno.land for Developers - -- Paris, July 24 - 25, 2023 -- Manfred Touron - -[Learn more](https://www.nebular.builders/) - -
- -
- -### EthCC - -- **Come Meet Us at our Booth** -- Paris, July 17 - 20, 2023 -- Manfred Touron - -[Learn more](https://www.ethcc.io/) - -
- -
- -### Eth Seoul - -- **The Evolution of Smart Contracts: A Journey into Gno.land** -- Seoul, June 3, 2023 -- Manfred Touron - -[Learn more](https://2023.ethseoul.org/) + if len(description) > maxDescLength { + return "", ufmt.Errorf("%s: provided length is %d, maximum is %d", ErrDescriptionTooLong, len(description), maxDescLength) + } -
-
+ // Parse times + st, et, err := parseTimes(startTime, endTime) + if err != nil { + return "", err + } -### BUIDL Asia + id := idCounter.Next().String() + e := &Event{ + id: id, + name: name, + description: description, + link: link, + location: location, + startTime: st, + endTime: et, + } -- **Proof of Contribution in Gno.land** -- Seoul, June 6, 2023 -- Manfred Touron + events = append(events, e) + sort.Sort(events) -[Learn more](https://www.buidl.asia/) + std.Emit(EventAdded, + "id", + e.id, + ) -
-
+ return id, nil +} -### Game Developer Conference +// DeleteEvent deletes an event with auth given ID +func DeleteEvent(id string) { + auth.AssertOnAuthList() -- **Side Event: Web3 Gaming Apps Powered by Gno** -- San Francisco, Mach 23, 2023 -- Jae Kwon + e, idx, err := GetEventByID(id) + if err != nil { + panic(err) + } -[Watch the talk](https://www.youtube.com/watch?v=IJ0xel8lr4c) + events = append(events[:idx], events[idx+1:]...) -
-
+ std.Emit(EventDeleted, + "id", + e.id, + ) +} -### EthDenver +// EditEvent edits an event with auth given ID +// It only updates values corresponding to non-empty arguments sent with the call +// Note: if you need to update the start time or end time, you need to provide both every time +func EditEvent(id string, name, description, link, location, startTime, endTime string) { + auth.AssertOnAuthList() -- **Side Event: Discover Gno.land** -- Denver, Feb 24 - Mar 5, 2023 -- Jae Kwon + e, _, err := GetEventByID(id) + if err != nil { + panic(err) + } -[Watch the talk](https://www.youtube.com/watch?v=IJ0xel8lr4c) + // Set only valid values + if strings.TrimSpace(name) != "" { + e.name = name + } -
-
+ if strings.TrimSpace(description) != "" { + e.description = description + } -### Istanbul Blockchain Week + if strings.TrimSpace(link) != "" { + e.link = link + } -- Istanbul, Nov 14 - 17, 2022 -- Manfred Touron + if strings.TrimSpace(location) != "" { + e.location = location + } -[Watch the talk](https://www.youtube.com/watch?v=JX0gdWT0Cg4) + if strings.TrimSpace(startTime) != "" || strings.TrimSpace(endTime) != "" { + st, et, err := parseTimes(startTime, endTime) + if err != nil { + panic(err) // need to also revert other state changes + } -
-
+ oldStartTime := e.startTime + e.startTime = st + e.endTime = et -### Web Summit Buckle Up and Build with Cosmos + // If sort order was disrupted, sort again + if oldStartTime != e.startTime { + sort.Sort(events) + } + } -- Lisbon, Nov 1 - 4, 2022 -- Manfred Touron + std.Emit(EventEdited, + "id", + e.id, + ) +} -
-
+func GetEventByID(id string) (*Event, int, error) { + for i, event := range events { + if event.id == id { + return event, i, nil + } + } -### Cosmoverse + return nil, -1, ErrNoSuchID +} -- Medallin, Sept 26 - 28, 2022 -- Manfred Touron +// Len returns the length of the slice +func (m eventsSlice) Len() int { + return len(m) +} -[Watch the talk](https://www.youtube.com/watch?v=6s1zG7hgxMk) +// Less compares the startTime fields of two elements +// In this case, events will be sorted by largest startTime first (upcoming > past) +func (m eventsSlice) Less(i, j int) bool { + return m[i].startTime.After(m[j].startTime) +} -
-
+// Swap swaps two elements in the slice +func (m eventsSlice) Swap(i, j int) { + m[i], m[j] = m[j], m[i] +} -### Berlin Blockchain Week Buckle Up and Build with Cosmos +// parseTimes parses the start and end time for an event and checks for possible errors +func parseTimes(startTime, endTime string) (time.Time, time.Time, error) { + st, err := time.Parse(time.RFC3339, startTime) + if err != nil { + return time.Time{}, time.Time{}, ufmt.Errorf("%s: %s", ErrInvalidStartTime, err.Error()) + } -- Berlin, Sept 11 - 18, 2022 + et, err := time.Parse(time.RFC3339, endTime) + if err != nil { + return time.Time{}, time.Time{}, ufmt.Errorf("%s: %s", ErrInvalidEndTime, err.Error()) + } -[Watch the talk](https://www.youtube.com/watch?v=hCLErPgnavI) + if et.Before(st) { + return time.Time{}, time.Time{}, ErrEndBeforeStart + } -
-
`), + _, stOffset := st.Zone() + _, etOffset := et.Zone() + if stOffset != etOffset { + return time.Time{}, time.Time{}, ErrStartEndTimezonemMismatch } + + return st, et, nil } diff --git a/examples/gno.land/r/gnoland/events/events_filetest.gno b/examples/gno.land/r/gnoland/events/events_filetest.gno deleted file mode 100644 index 46ee273414d..00000000000 --- a/examples/gno.land/r/gnoland/events/events_filetest.gno +++ /dev/null @@ -1,226 +0,0 @@ -package main - -import "gno.land/r/gnoland/events" - -func main() { - println(events.Render("")) -} - -// Output: -//
-// -// # Gno.land Core Team Attends Industry Events & Meetups -// -// -// 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 -// -//
-//
-// -// ### GopherCon EU -// - Come Meet Us at our Booth -// - Berlin, June 17 - 20, 2024 -// -// [Learn More](https://gophercon.eu/) -//
-// -//
-// -// ### GopherCon US -// - Come Meet Us at our Booth -// - Chicago, July 7 - 10, 2024 -// -// [Learn More](https://www.gophercon.com/) -// -//
-// -//
-// -// ### Nebular Summit -// - Join our workshop -// - Brussels, July 12 - 13, 2024 -// -// [Learn More](https://nebular.builders/) -//
-// -//
-// -//
-//
-// -//
-//
-// -// --- -// -// ## Past Events -// -//
-// -//
-// -// ### Gno @ Golang Serbia -// -// - **Join the meetup** -// - Belgrade, May 23, 2024 -// -// [Learn more](https://gno.land/r/gnoland/blog:p/gnomes-in-serbia) -// -//
-// -//
-// -// ### Intro to Gno Tokyo -// -// - **Join the meetup** -// - Tokyo, April 11, 2024 -// -// [Learn more](https://gno.land/r/gnoland/blog:p/gno-tokyo) -// -//
-// -//
-// -// ### Go to Gno Seoul -// -// - **Join the workshop** -// - Seoul, March 23, 2024 -// -// [Learn more](https://medium.com/onbloc/go-to-gno-recap-intro-to-the-gno-stack-with-memeland-284a43d7f620) -// -//
-// -//
-// -// ### GopherCon US -// -// - **Come Meet Us at our Booth** -// - San Diego, September 26 - 29, 2023 -// -// [Learn more](https://www.gophercon.com/) -// -//
-// -//
-// -// ### GopherCon EU -// -// - **Come Meet Us at our Booth** -// - Berlin, July 26 - 29, 2023 -// -// [Learn more](https://gophercon.eu/) -// -//
-// -//
-// -// ### Nebular Summit Gno.land for Developers -// -// - Paris, July 24 - 25, 2023 -// - Manfred Touron -// -// [Learn more](https://www.nebular.builders/) -// -//
-// -//
-// -// ### EthCC -// -// - **Come Meet Us at our Booth** -// - Paris, July 17 - 20, 2023 -// - Manfred Touron -// -// [Learn more](https://www.ethcc.io/) -// -//
-// -//
-// -// ### 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) -// -//
-//
-// -//
diff --git a/examples/gno.land/r/gnoland/events/events_test.gno b/examples/gno.land/r/gnoland/events/events_test.gno new file mode 100644 index 00000000000..357857352d8 --- /dev/null +++ b/examples/gno.land/r/gnoland/events/events_test.gno @@ -0,0 +1,200 @@ +package events + +import ( + "std" + "strings" + "testing" + "time" + + "gno.land/p/demo/uassert" + "gno.land/p/demo/urequire" +) + +var ( + suRealm = std.NewUserRealm(su) + + now = "2009-02-13T23:31:30Z" // time.Now() is hardcoded to this value in the gno test machine currently + parsedTimeNow, _ = time.Parse(time.RFC3339, now) +) + +func TestAddEvent(t *testing.T) { + std.TestSetOrigCaller(su) + std.TestSetRealm(suRealm) + + e1Start := parsedTimeNow.Add(time.Hour * 24 * 5) + e1End := e1Start.Add(time.Hour * 4) + + AddEvent("Event 1", "this event is upcoming", "gno.land", "gnome land", e1Start.Format(time.RFC3339), e1End.Format(time.RFC3339)) + + got := renderHome(false) + + if !strings.Contains(got, "Event 1") { + t.Fatalf("Expected to find Event 1 in render") + } + + e2Start := parsedTimeNow.Add(-time.Hour * 24 * 5) + e2End := e2Start.Add(time.Hour * 4) + + AddEvent("Event 2", "this event is in the past", "gno.land", "gnome land", e2Start.Format(time.RFC3339), e2End.Format(time.RFC3339)) + + got = renderHome(false) + + upcomingPos := strings.Index(got, "## Upcoming events") + pastPos := strings.Index(got, "## Past events") + + e1Pos := strings.Index(got, "Event 1") + e2Pos := strings.Index(got, "Event 2") + + // expected index ordering: upcoming < e1 < past < e2 + if e1Pos < upcomingPos || e1Pos > pastPos { + t.Fatalf("Expected to find Event 1 in Upcoming events") + } + + if e2Pos < upcomingPos || e2Pos < pastPos || e2Pos < e1Pos { + t.Fatalf("Expected to find Event 2 on auth different pos") + } + + // larger index => smaller startTime (future => past) + if events[0].startTime.Unix() < events[1].startTime.Unix() { + t.Fatalf("expected ordering to be different") + } +} + +func TestAddEventErrors(t *testing.T) { + std.TestSetOrigCaller(su) + std.TestSetRealm(suRealm) + + _, err := AddEvent("", "sample desc", "gno.land", "gnome land", "2009-02-13T23:31:31Z", "2009-02-13T23:33:31Z") + uassert.ErrorIs(t, err, ErrEmptyName) + + _, err = AddEvent("sample name", "sample desc", "gno.land", "gnome land", "", "2009-02-13T23:33:31Z") + uassert.ErrorContains(t, err, ErrInvalidStartTime.Error()) + + _, err = AddEvent("sample name", "sample desc", "gno.land", "gnome land", "2009-02-13T23:31:31Z", "") + uassert.ErrorContains(t, err, ErrInvalidEndTime.Error()) + + _, err = AddEvent("sample name", "sample desc", "gno.land", "gnome land", "2009-02-13T23:31:31Z", "2009-02-13T23:30:31Z") + uassert.ErrorIs(t, err, ErrEndBeforeStart) + + _, err = AddEvent("sample name", "sample desc", "gno.land", "gnome land", "2009-02-13T23:31:31+06:00", "2009-02-13T23:33:31+02:00") + uassert.ErrorIs(t, err, ErrStartEndTimezonemMismatch) + + tooLongDesc := `Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean ma` + _, err = AddEvent("sample name", tooLongDesc, "gno.land", "gnome land", "2009-02-13T23:31:31Z", "2009-02-13T23:33:31Z") + uassert.ErrorContains(t, err, ErrDescriptionTooLong.Error()) +} + +func TestDeleteEvent(t *testing.T) { + events = nil // remove elements from previous tests - see issue #1982 + + e1Start := parsedTimeNow.Add(time.Hour * 24 * 5) + e1End := e1Start.Add(time.Hour * 4) + + id, _ := AddEvent("ToDelete", "description", "gno.land", "gnome land", e1Start.Format(time.RFC3339), e1End.Format(time.RFC3339)) + + got := renderHome(false) + + if !strings.Contains(got, "ToDelete") { + t.Fatalf("Expected to find ToDelete event in render") + } + + DeleteEvent(id) + got = renderHome(false) + + if strings.Contains(got, "ToDelete") { + t.Fatalf("Did not expect to find ToDelete event in render") + } +} + +func TestEditEvent(t *testing.T) { + events = nil // remove elements from previous tests - see issue #1982 + + e1Start := parsedTimeNow.Add(time.Hour * 24 * 5) + e1End := e1Start.Add(time.Hour * 4) + loc := "gnome land" + + id, _ := AddEvent("ToDelete", "description", "gno.land", loc, e1Start.Format(time.RFC3339), e1End.Format(time.RFC3339)) + + newName := "New Name" + newDesc := "Normal description" + newLink := "new Link" + newST := e1Start.Add(time.Hour) + newET := newST.Add(time.Hour) + + EditEvent(id, newName, newDesc, newLink, "", newST.Format(time.RFC3339), newET.Format(time.RFC3339)) + edited, _, _ := GetEventByID(id) + + // Check updated values + uassert.Equal(t, edited.name, newName) + uassert.Equal(t, edited.description, newDesc) + uassert.Equal(t, edited.link, newLink) + uassert.True(t, edited.startTime.Equal(newST)) + uassert.True(t, edited.endTime.Equal(newET)) + + // Check if the old values are the same + uassert.Equal(t, edited.location, loc) +} + +func TestInvalidEdit(t *testing.T) { + events = nil // remove elements from previous tests - see issue #1982 + + uassert.PanicsWithMessage(t, ErrNoSuchID.Error(), func() { + EditEvent("123123", "", "", "", "", "", "") + }) +} + +func TestParseTimes(t *testing.T) { + // times not provided + // end time before start time + // timezone Missmatch + + _, _, err := parseTimes("", "") + uassert.ErrorContains(t, err, ErrInvalidStartTime.Error()) + + _, _, err = parseTimes(now, "") + uassert.ErrorContains(t, err, ErrInvalidEndTime.Error()) + + _, _, err = parseTimes("2009-02-13T23:30:30Z", "2009-02-13T21:30:30Z") + uassert.ErrorContains(t, err, ErrEndBeforeStart.Error()) + + _, _, err = parseTimes("2009-02-10T23:30:30+02:00", "2009-02-13T21:30:33+05:00") + uassert.ErrorContains(t, err, ErrStartEndTimezonemMismatch.Error()) +} + +func TestRenderEventWidget(t *testing.T) { + events = nil // remove elements from previous tests - see issue #1982 + + // No events yet + out, err := RenderEventWidget(1) + uassert.NoError(t, err) + uassert.Equal(t, out, "No events.") + + // Too many events + out, err = RenderEventWidget(MaxWidgetSize + 1) + uassert.ErrorIs(t, err, ErrMaxWidgetSize) + + // Too little events + out, err = RenderEventWidget(0) + uassert.ErrorIs(t, err, ErrMinWidgetSize) + + // Ordering & if requested amt is larger than the num of events that exist + e1Start := parsedTimeNow.Add(time.Hour * 24 * 5) + e1End := e1Start.Add(time.Hour * 4) + + e2Start := parsedTimeNow.Add(time.Hour * 24 * 10) // event 2 is after event 1 + e2End := e2Start.Add(time.Hour * 4) + + _, err = AddEvent("Event 1", "description", "gno.land", "loc", e1Start.Format(time.RFC3339), e1End.Format(time.RFC3339)) + urequire.NoError(t, err) + + _, err = AddEvent("Event 2", "description", "gno.land", "loc", e2Start.Format(time.RFC3339), e2End.Format(time.RFC3339)) + urequire.NoError(t, err) + + out, err = RenderEventWidget(MaxWidgetSize) + urequire.NoError(t, err) + + uniqueSequence := "- [" // sequence that is displayed once per each event as per the RenderEventWidget function + uassert.Equal(t, 2, strings.Count(out, uniqueSequence)) + + uassert.True(t, strings.Index(out, "Event 1") > strings.Index(out, "Event 2")) +} diff --git a/examples/gno.land/r/gnoland/events/gno.mod b/examples/gno.land/r/gnoland/events/gno.mod index ec781c7cf10..bd3e4652b04 100644 --- a/examples/gno.land/r/gnoland/events/gno.mod +++ b/examples/gno.land/r/gnoland/events/gno.mod @@ -1,3 +1,9 @@ module gno.land/r/gnoland/events -require gno.land/p/demo/ui v0.0.0-latest +require ( + gno.land/p/demo/ownable/exts/authorizable v0.0.0-latest + gno.land/p/demo/seqid v0.0.0-latest + gno.land/p/demo/uassert v0.0.0-latest + gno.land/p/demo/ufmt v0.0.0-latest + gno.land/p/demo/urequire v0.0.0-latest +) diff --git a/examples/gno.land/r/gnoland/events/rendering.gno b/examples/gno.land/r/gnoland/events/rendering.gno new file mode 100644 index 00000000000..d98879c68f6 --- /dev/null +++ b/examples/gno.land/r/gnoland/events/rendering.gno @@ -0,0 +1,145 @@ +package events + +import ( + "bytes" + "time" + + "gno.land/p/demo/ufmt" +) + +const ( + MaxWidgetSize = 5 +) + +// RenderEventWidget shows up to eventsToRender of the latest events to a caller +func RenderEventWidget(eventsToRender int) (string, error) { + numOfEvents := len(events) + if numOfEvents == 0 { + return "No events.", nil + } + + if eventsToRender > MaxWidgetSize { + return "", ErrMaxWidgetSize + } + + if eventsToRender < 1 { + return "", ErrMinWidgetSize + } + + if eventsToRender > numOfEvents { + eventsToRender = numOfEvents + } + + output := "" + + for _, event := range events[:eventsToRender] { + output += ufmt.Sprintf("- [%s](%s)\n", event.name, event.link) + } + + return output, nil +} + +// renderHome renders the home page of the events realm +func renderHome(admin bool) string { + output := "# gno.land events\n\n" + + if len(events) == 0 { + output += "No upcoming or past events." + return output + } + + output += "Below is a list of all gno.land events, including in progress, upcoming, and past ones.\n\n" + output += "---\n\n" + + var ( + inProgress = "" + upcoming = "" + past = "" + now = time.Now() + ) + + for _, e := range events { + if now.Before(e.startTime) { + upcoming += e.Render(admin) + } else if now.After(e.endTime) { + past += e.Render(admin) + } else { + inProgress += e.Render(admin) + } + } + + if upcoming != "" { + // Add upcoming events + output += "## Upcoming events\n\n" + output += "
" + + output += upcoming + + output += "
\n\n" + output += "---\n\n" + } + + if inProgress != "" { + output += "## Currently in progress\n\n" + output += "
" + + output += inProgress + + output += "
\n\n" + output += "---\n\n" + } + + if past != "" { + // Add past events + output += "## Past events\n\n" + output += "
" + + output += past + + output += "
\n\n" + } + + return output +} + +// Render returns the markdown representation of a single event instance +func (e Event) Render(admin bool) string { + var buf bytes.Buffer + + buf.WriteString("
\n\n") + buf.WriteString(ufmt.Sprintf("### %s\n\n", e.name)) + buf.WriteString(ufmt.Sprintf("%s\n\n", e.description)) + buf.WriteString(ufmt.Sprintf("**Location:** %s\n\n", e.location)) + + _, offset := e.startTime.Zone() // offset is in seconds + hoursOffset := offset / (60 * 60) + sign := "" + if offset >= 0 { + sign = "+" + } + + buf.WriteString(ufmt.Sprintf("**Starts:** %s UTC%s%d\n\n", e.startTime.Format("02 Jan 2006, 03:04 PM"), sign, hoursOffset)) + buf.WriteString(ufmt.Sprintf("**Ends:** %s UTC%s%d\n\n", e.endTime.Format("02 Jan 2006, 03:04 PM"), sign, hoursOffset)) + + if admin { + buf.WriteString(ufmt.Sprintf("[EDIT](/r/gnoland/events?help&__func=EditEvent&id=%s)\n\n", e.id)) + buf.WriteString(ufmt.Sprintf("[DELETE](/r/gnoland/events?help&__func=DeleteEvent&id=%s)\n\n", e.id)) + } + + if e.link != "" { + buf.WriteString(ufmt.Sprintf("[See more](%s)\n\n", e.link)) + } + + buf.WriteString("
") + + return buf.String() +} + +// Render is the main rendering entry point +func Render(path string) string { + if path == "admin" { + return renderHome(true) + } + + return renderHome(false) +} diff --git a/examples/gno.land/r/gnoland/home/gno.mod b/examples/gno.land/r/gnoland/home/gno.mod index cb2ec58b665..c208ad421c9 100644 --- a/examples/gno.land/r/gnoland/home/gno.mod +++ b/examples/gno.land/r/gnoland/home/gno.mod @@ -5,4 +5,5 @@ 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 + gno.land/r/gnoland/events v0.0.0-latest ) diff --git a/examples/gno.land/r/gnoland/home/home.gno b/examples/gno.land/r/gnoland/home/home.gno index 62984711d79..921492d81b4 100644 --- a/examples/gno.land/r/gnoland/home/home.gno +++ b/examples/gno.land/r/gnoland/home/home.gno @@ -7,6 +7,7 @@ import ( "gno.land/p/demo/ufmt" "gno.land/p/demo/ui" blog "gno.land/r/gnoland/blog" + events "gno.land/r/gnoland/events" ) // XXX: p/demo/ui API is crappy, we need to make it more idiomatic @@ -35,7 +36,7 @@ func Render(_ string) string { dom.Body.Append( ui.Columns{3, []ui.Element{ lastBlogposts(4), - upcomingEvents(4), + upcomingEvents(), lastContributions(4), }}, ) @@ -68,7 +69,7 @@ func Render(_ string) string { func lastBlogposts(limit int) ui.Element { posts := blog.RenderLastPostsWidget(limit) return ui.Element{ - ui.H3("Latest Blogposts"), + ui.H3("[Latest Blogposts](/r/gnoland/blog)"), ui.Text(posts), } } @@ -81,11 +82,11 @@ func lastContributions(limit int) ui.Element { } } -func upcomingEvents(limit int) ui.Element { +func upcomingEvents() ui.Element { + out, _ := events.RenderEventWidget(events.MaxWidgetSize) return ui.Element{ - ui.H3("Upcoming Events"), - // TODO: replace with r/gnoland/events - ui.Text("[View upcoming events](/events)"), + ui.H3("[Latest Events](/r/gnoland/events)"), + ui.Text(out), } } @@ -268,7 +269,7 @@ func discoverLinks() ui.Element { - [Discover demo packages](https://github.com/gnolang/gno/tree/master/examples) - [Gnoscan](https://gnoscan.io) - [Portal Loop](https://docs.gno.land/concepts/portal-loop) -- Testnet 4 (upcoming) +- [Testnet 4](https://test4.gno.land/) (Launched July 2024!) - [Testnet 3](https://test3.gno.land/) (archive) - [Testnet 2](https://test2.gno.land/) (archive) - Testnet Faucet Hub (soon) diff --git a/examples/gno.land/r/gnoland/home/home_filetest.gno b/examples/gno.land/r/gnoland/home/home_filetest.gno index 2b0a802718f..b70b22c80af 100644 --- a/examples/gno.land/r/gnoland/home/home_filetest.gno +++ b/examples/gno.land/r/gnoland/home/home_filetest.gno @@ -56,7 +56,7 @@ func main() { // - [Discover demo packages](https://github.com/gnolang/gno/tree/master/examples) // - [Gnoscan](https://gnoscan.io) // - [Portal Loop](https://docs.gno.land/concepts/portal-loop) -// - Testnet 4 (upcoming) +// - [Testnet 4](https://test4.gno.land/) (Launched July 2024!) // - [Testnet 3](https://test3.gno.land/) (archive) // - [Testnet 2](https://test2.gno.land/) (archive) // - Testnet Faucet Hub (soon) @@ -68,15 +68,15 @@ func main() { //
//
// -// ### Latest Blogposts +// ### [Latest Blogposts](/r/gnoland/blog) // // No posts. //
//
// -// ### Upcoming Events +// ### [Latest Events](/r/gnoland/events) // -// [View upcoming events](/events) +// No events. //
//
// diff --git a/examples/gno.land/r/gnoland/home/overide_filetest.gno b/examples/gno.land/r/gnoland/home/overide_filetest.gno index 34356b93349..4f21b90a3c2 100644 --- a/examples/gno.land/r/gnoland/home/overide_filetest.gno +++ b/examples/gno.land/r/gnoland/home/overide_filetest.gno @@ -21,4 +21,4 @@ func main() { // Output: // Hello World! -// r: unauthorized; caller is not owner +// r: ownable: caller is not owner diff --git a/gno.land/cmd/gnoland/config_get.go b/gno.land/cmd/gnoland/config_get.go index 1fd4027ec60..796ae9da5e9 100644 --- a/gno.land/cmd/gnoland/config_get.go +++ b/gno.land/cmd/gnoland/config_get.go @@ -36,6 +36,19 @@ func newConfigGetCmd(io commands.IO) *commands.Command { }, ) + // Add subcommand helpers + helperGen := metadataHelperGenerator{ + MetaUpdate: func(meta *commands.Metadata, inputType string) { + meta.ShortUsage = fmt.Sprintf("config get %s <%s>", meta.Name, inputType) + }, + TagNameSelector: "json", + TreeDisplay: true, + } + subs := generateSubCommandHelper(helperGen, config.Config{}, func(_ context.Context, args []string) error { + return execConfigGet(cfg, io, args) + }) + + cmd.AddSubCommands(subs...) return cmd } diff --git a/gno.land/cmd/gnoland/config_help.go b/gno.land/cmd/gnoland/config_help.go new file mode 100644 index 00000000000..97d43953bba --- /dev/null +++ b/gno.land/cmd/gnoland/config_help.go @@ -0,0 +1,127 @@ +package main + +import ( + "context" + "fmt" + "reflect" + "strings" + "unicode" + + "github.com/gnolang/gno/tm2/pkg/commands" +) + +type metadataHelperGenerator struct { + // Optional callback to edit metadata + MetaUpdate func(meta *commands.Metadata, inputType string) + // Tag to select for name, if empty will use the field Name + TagNameSelector string + // Will display description with tree representation + TreeDisplay bool +} + +// generateSubCommandHelper generates subcommands based on `s` structure fields and their respective tag descriptions +func generateSubCommandHelper(gen metadataHelperGenerator, s any, exec commands.ExecMethod) []*commands.Command { + rv := reflect.ValueOf(s) + metas := gen.generateFields(rv, "", 0) + + cmds := make([]*commands.Command, len(metas)) + for i := 0; i < len(metas); i++ { + meta := metas[i] + exec := func(ctx context.Context, args []string) error { + args = append([]string{meta.Name}, args...) + return exec(ctx, args) + } + cmds[i] = commands.NewCommand(meta, nil, exec) + } + + return cmds +} + +func (g *metadataHelperGenerator) generateFields(rv reflect.Value, parent string, depth int) []commands.Metadata { + if parent != "" { + parent += "." + } + + // Unwrap pointer if needed + if rv.Kind() == reflect.Ptr { + if rv.IsNil() { + // Create a new non-nil instance of the original type that was nil + rv = reflect.New(rv.Type().Elem()) + } + rv = rv.Elem() // Dereference to struct value + } + + metas := []commands.Metadata{} + if rv.Kind() != reflect.Struct { + return metas + } + + rt := rv.Type() + for i := 0; i < rv.NumField(); i++ { + field := rt.Field(i) + if !field.IsExported() { + continue + } + + fieldValue := rv.Field(i) + name := field.Name + // Get JSON tag name + if g.TagNameSelector != "" { + name, _, _ = strings.Cut(field.Tag.Get(g.TagNameSelector), ",") + if name == "" || name == "-" { + continue + } + } + + // Recursive call for nested struct + var childs []commands.Metadata + if k := fieldValue.Kind(); k == reflect.Ptr || k == reflect.Struct { + childs = g.generateFields(fieldValue, name, depth+1) + } + + // Generate metadata + var meta commands.Metadata + + // Name + meta.Name = parent + name + + // Create a tree-like display to see nested field + if g.TreeDisplay && depth > 0 { + meta.ShortHelp += strings.Repeat(" ", depth*2) + if i == rv.NumField()-1 { + meta.ShortHelp += "└─" + } else { + meta.ShortHelp += "├─" + } + } + meta.ShortHelp += fmt.Sprintf("<%s>", field.Type) + + // Get Short/Long Help Message from comment tag + comment := field.Tag.Get("comment") + comment = strings.TrimFunc(comment, func(r rune) bool { + return unicode.IsSpace(r) || r == '#' + }) + + if comment != "" { + // Use the first line as short help + meta.ShortHelp += " " + meta.ShortHelp += strings.Split(comment, "\n")[0] + + // Display full comment as Long Help + meta.LongHelp = comment + } else { + // If the comment is empty, it mostly means that there is no help. + // Use a blank space to avoid falling back on short help. + meta.LongHelp = " " + } + + if g.MetaUpdate != nil { + g.MetaUpdate(&meta, field.Type.String()) + } + + metas = append(metas, meta) + metas = append(metas, childs...) + } + + return metas +} diff --git a/gno.land/cmd/gnoland/config_set.go b/gno.land/cmd/gnoland/config_set.go index dd171970bf6..de96aa35c7d 100644 --- a/gno.land/cmd/gnoland/config_set.go +++ b/gno.land/cmd/gnoland/config_set.go @@ -34,6 +34,18 @@ func newConfigSetCmd(io commands.IO) *commands.Command { }, ) + // Add subcommand helpers + helperGen := metadataHelperGenerator{ + MetaUpdate: func(meta *commands.Metadata, inputType string) { + meta.ShortUsage = fmt.Sprintf("config set %s <%s>", meta.Name, inputType) + }, + TagNameSelector: "json", + TreeDisplay: true, + } + cmd.AddSubCommands(generateSubCommandHelper(helperGen, config.Config{}, func(_ context.Context, args []string) error { + return execConfigEdit(cfg, io, args) + })...) + return cmd } diff --git a/gno.land/cmd/gnoland/secrets_get.go b/gno.land/cmd/gnoland/secrets_get.go index 47de7a46283..8d111516816 100644 --- a/gno.land/cmd/gnoland/secrets_get.go +++ b/gno.land/cmd/gnoland/secrets_get.go @@ -41,6 +41,18 @@ func newSecretsGetCmd(io commands.IO) *commands.Command { }, ) + // Add subcommand helpers + helperGen := metadataHelperGenerator{ + MetaUpdate: func(meta *commands.Metadata, inputType string) { + meta.ShortUsage = fmt.Sprintf("secrets get %s <%s>", meta.Name, inputType) + }, + TagNameSelector: "json", + TreeDisplay: false, + } + cmd.AddSubCommands(generateSubCommandHelper(helperGen, secrets{}, func(_ context.Context, args []string) error { + return execSecretsGet(cfg, args, io) + })...) + return cmd } diff --git a/gno.land/pkg/gnoweb/gnoweb.go b/gno.land/pkg/gnoweb/gnoweb.go index b997de7840d..1cd0d664193 100644 --- a/gno.land/pkg/gnoweb/gnoweb.go +++ b/gno.land/pkg/gnoweb/gnoweb.go @@ -1,6 +1,7 @@ package gnoweb import ( + "bytes" "embed" "encoding/json" "errors" @@ -335,6 +336,15 @@ func handleRealmRender(logger *slog.Logger, app gotuna.App, cfg *Config, w http. return } } + + dirdata := []byte(rlmpath) + dirres, err := makeRequest(logger, cfg, qFileStr, dirdata) + if err != nil { + writeError(logger, w, err) + return + } + hasReadme := bytes.Contains(append(dirres.Data, '\n'), []byte("README.md\n")) + // linkify querystr. queryParts := strings.Split(querystr, "/") pathLinks := []pathLink{} @@ -354,6 +364,7 @@ func handleRealmRender(logger *slog.Logger, app gotuna.App, cfg *Config, w http. tmpl.Set("PathLinks", pathLinks) tmpl.Set("Contents", string(res.Data)) tmpl.Set("Config", cfg) + tmpl.Set("HasReadme", hasReadme) tmpl.Render(w, r, "realm_render.html", "funcs.html") } diff --git a/gno.land/pkg/gnoweb/views/realm_render.html b/gno.land/pkg/gnoweb/views/realm_render.html index 9a4507777a6..924ef2b414f 100644 --- a/gno.land/pkg/gnoweb/views/realm_render.html +++ b/gno.land/pkg/gnoweb/views/realm_render.html @@ -16,6 +16,9 @@ {{- end -}} + {{ if .Data.HasReadme }} + [readme] + {{ end }} [source] [help]