From d9da6f87e5508daf8aa333872f2d230d228f9e50 Mon Sep 17 00:00:00 2001 From: freeelancer Date: Fri, 27 Dec 2024 11:54:06 +0800 Subject: [PATCH 01/14] added local e2e tests --- Dockerfile | 39 +- Makefile | 25 +- app/params/doc.go | 2 +- app/test_helpers.go | 26 + go.mod | 11 +- go.sum | 17 + tests/connect/connect_integration_test.go | 2 +- tests/e2e/address.go | 33 + tests/e2e/chain.go | 185 +++++ tests/e2e/e2e_bank_test.go | 98 +++ tests/e2e/e2e_distribution_test.go | 87 ++ tests/e2e/e2e_encode_test.go | 50 ++ tests/e2e/e2e_evidence_test.go | 55 ++ tests/e2e/e2e_exec_test.go | 956 ++++++++++++++++++++++ tests/e2e/e2e_gov_test.go | 305 +++++++ tests/e2e/e2e_ibc_test.go | 379 +++++++++ tests/e2e/e2e_rest_regression_test.go | 90 ++ tests/e2e/e2e_setup_test.go | 888 ++++++++++++++++++++ tests/e2e/e2e_slashing_test.go | 23 + tests/e2e/e2e_staking_test.go | 143 ++++ tests/e2e/e2e_test.go | 108 +++ tests/e2e/e2e_vesting_test.go | 338 ++++++++ tests/e2e/genesis.go | 153 ++++ tests/e2e/http_util.go | 40 + tests/e2e/io.go | 41 + tests/e2e/keys.go | 20 + tests/e2e/query.go | 361 ++++++++ tests/e2e/scripts/hermes_bootstrap.sh | 83 ++ tests/e2e/util.go | 52 ++ tests/e2e/validator.go | 321 ++++++++ 30 files changed, 4899 insertions(+), 32 deletions(-) create mode 100644 tests/e2e/address.go create mode 100644 tests/e2e/chain.go create mode 100644 tests/e2e/e2e_bank_test.go create mode 100644 tests/e2e/e2e_distribution_test.go create mode 100644 tests/e2e/e2e_encode_test.go create mode 100644 tests/e2e/e2e_evidence_test.go create mode 100644 tests/e2e/e2e_exec_test.go create mode 100644 tests/e2e/e2e_gov_test.go create mode 100644 tests/e2e/e2e_ibc_test.go create mode 100644 tests/e2e/e2e_rest_regression_test.go create mode 100644 tests/e2e/e2e_setup_test.go create mode 100644 tests/e2e/e2e_slashing_test.go create mode 100644 tests/e2e/e2e_staking_test.go create mode 100644 tests/e2e/e2e_test.go create mode 100644 tests/e2e/e2e_vesting_test.go create mode 100644 tests/e2e/genesis.go create mode 100644 tests/e2e/http_util.go create mode 100644 tests/e2e/io.go create mode 100644 tests/e2e/keys.go create mode 100644 tests/e2e/query.go create mode 100755 tests/e2e/scripts/hermes_bootstrap.sh create mode 100644 tests/e2e/util.go create mode 100644 tests/e2e/validator.go diff --git a/Dockerfile b/Dockerfile index 5776d09c..74e0f4e1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,15 @@ # syntax=docker/dockerfile:1 ARG GO_VERSION="1.23" -ARG FINAL_IMAGE="alpine:latest" +ARG IMG_TAG="latest" ARG BUILD_TAGS="netgo,ledger,muslc" # -------------------------------------------------------- # Builder # -------------------------------------------------------- -FROM golang:${GO_VERSION}-alpine3.20 as builder - -ARG GIT_VERSION -ARG GIT_COMMIT -ARG BUILD_TAGS -ARG CMT_VERSION - +FROM golang:${GO_VERSION}-alpine3.20 AS mantra-builder +WORKDIR /src/app/ RUN apk add --no-cache \ ca-certificates \ build-base \ @@ -23,7 +18,6 @@ RUN apk add --no-cache \ git # Download go dependencies -WORKDIR /mantrachain COPY go.mod go.sum ./ RUN --mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/root/go/pkg/mod \ @@ -42,26 +36,21 @@ COPY . . # Build mantrachaind binary # build tag info: https://github.com/cosmos/wasmd/blob/master/README.md#supported-systems -RUN --mount=type=cache,target=/root/.cache/go-build \ - --mount=type=cache,target=/root/go/pkg/mod \ +RUN --mount=type=cache,target=/nonroot/.cache/go-build \ + --mount=type=cache,target=/nonroot/go/pkg/mod \ LEDGER_ENABLED=true BUILD_TAGS='muslc osusergo' LINK_STATICALLY=true make build # -------------------------------------------------------- # Runner # -------------------------------------------------------- -FROM ${FINAL_IMAGE} - -COPY --from=builder /mantrachain/build/mantrachaind /bin/mantrachaind - -ENV HOME /mantrachain -WORKDIR $HOME - -EXPOSE 26656 -EXPOSE 26657 -EXPOSE 1317 -# Note: uncomment the line below if you need pprof in local mantrachain -# We disable it by default in out main Dockerfile for security reasons -# EXPOSE 6060 +FROM alpine:$IMG_TAG +RUN apk add --no-cache build-base jq +RUN addgroup -g 1025 nonroot +RUN adduser -D nonroot -u 1025 -G nonroot +ARG IMG_TAG +COPY --from=mantra-builder /src/app/build/mantrachaind /usr/local/bin/ +EXPOSE 26656 26657 1317 9090 +USER nonroot -ENTRYPOINT ["mantrachaind"] \ No newline at end of file +ENTRYPOINT ["mantrachaind", "start"] \ No newline at end of file diff --git a/Makefile b/Makefile index 0ad0cdc0..f53417d6 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,6 @@ LEDGER_ENABLED ?= true BINDIR ?= $(GOPATH)/bin BUILDDIR ?= $(CURDIR)/build DOCKER := $(shell which docker) -PACKAGES_NOSIMULATION=$(shell go list ./... | grep -v '/simulation') BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2> /dev/null) BRANCH_PRETTY := $(subst /,-,$(BRANCH)) @@ -124,7 +123,7 @@ build-arm: build-linux: GOOS=linux GOARCH=$(if $(findstring aarch64,$(shell uname -m)) || $(findstring arm64,$(shell uname -m)),arm64,amd64) $(MAKE) build build-image: - docker build -f Dockerfile -t mantra-chain/mantrachain:local . + docker build -f Dockerfile -t mantra-chain/mantrachain . $(BUILD_TARGETS): go.sum $(BUILDDIR)/ go $@ -mod=readonly $(BUILD_FLAGS) $(BUILD_ARGS) $(GO_MODULE)/cmd/mantrachaind @@ -132,10 +131,28 @@ $(BUILDDIR)/: mkdir -p $(BUILDDIR)/ ############################################################################### -### Tests ### +### Tests ### ############################################################################### -test: test-unit +PACKAGES_UNIT=$(shell go list ./... | grep -v -e '/tests/e2e') +PACKAGES_E2E=$(shell cd tests/e2e && go list ./... | grep '/e2e') +TEST_PACKAGES=./... +TEST_TARGETS := test-unit test-e2e + +test-unit: ARGS=-timeout=5m -tags='norace' +test-unit: TEST_PACKAGES=$(PACKAGES_UNIT) +test-e2e: ARGS=-timeout=35m -v +test-e2e: TEST_PACKAGES=$(PACKAGES_E2E) +$(TEST_TARGETS): run-tests + +run-tests: +ifneq (,$(shell which tparse 2>/dev/null)) + @echo "--> Running tests" + @go test -mod=readonly -json $(ARGS) $(TEST_PACKAGES) | tparse +else + @echo "--> Running tests" + @go test -mod=readonly $(ARGS) $(TEST_PACKAGES) +endif test-unit: @VERSION=$(VERSION) go test ./x/... -mod=readonly -vet=all -tags='norace' $(PACKAGES_NOSIMULATION) diff --git a/app/params/doc.go b/app/params/doc.go index 7c6035ca..eef428e6 100644 --- a/app/params/doc.go +++ b/app/params/doc.go @@ -1,5 +1,5 @@ /* -Package params defines the simulation parameters in the gaia. +Package params defines the simulation parameters in the mantra. It contains the default weights used for each transaction used on the module's simulation. These weights define the chance for a transaction to be simulated at diff --git a/app/test_helpers.go b/app/test_helpers.go index 729c21e4..48ef020a 100644 --- a/app/test_helpers.go +++ b/app/test_helpers.go @@ -392,3 +392,29 @@ func GenesisStateWithValSet( println(string(genesisState[banktypes.ModuleName])) return genesisState, nil } + +// EmptyAppOptions is a stub implementing AppOptions +type EmptyAppOptions struct{} + +// Get implements AppOptions +func (ao EmptyAppOptions) Get(o string) interface{} { + return nil +} + +// AppOptionsMap is a stub implementing AppOptions which can get data from a map +type AppOptionsMap map[string]interface{} + +func (m AppOptionsMap) Get(key string) interface{} { + v, ok := m[key] + if !ok { + return interface{}(nil) + } + + return v +} + +func NewAppOptionsWithFlagHome(homePath string) servertypes.AppOptions { + return AppOptionsMap{ + flags.FlagHome: homePath, + } +} diff --git a/go.mod b/go.mod index 7f260c21..c01ac564 100644 --- a/go.mod +++ b/go.mod @@ -44,6 +44,7 @@ require ( github.com/cosmos/cosmos-db v1.1.0 github.com/cosmos/cosmos-proto v1.0.0-beta.5 github.com/cosmos/cosmos-sdk v0.50.10 + github.com/cosmos/go-bip39 v1.0.0 github.com/cosmos/gogoproto v1.7.0 github.com/cosmos/ibc-apps/modules/ibc-hooks/v8 v8.0.0-20240904212233-8cb681e31589 github.com/cosmos/ibc-apps/modules/rate-limiting/v8 v8.0.0 @@ -54,6 +55,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 + github.com/ory/dockertest/v3 v3.11.0 github.com/prometheus/client_golang v1.20.5 github.com/rakyll/statik v0.1.7 github.com/skip-mev/connect/v2 v2.1.2 @@ -87,6 +89,7 @@ require ( cloud.google.com/go/storage v1.41.0 // indirect connectrpc.com/connect v1.17.0 // indirect connectrpc.com/otelconnect v0.7.1 // indirect + dario.cat/mergo v1.0.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/99designs/keyring v1.2.2 // indirect @@ -95,6 +98,7 @@ require ( github.com/DataDog/zstd v1.5.5 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.12.7 // indirect + github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/aws/aws-sdk-go v1.55.5 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -129,7 +133,6 @@ require ( github.com/containerd/ttrpc v1.2.5 // indirect github.com/containerd/typeurl/v2 v2.2.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect - github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/iavl v1.2.2 // indirect github.com/cosmos/ics23/go v0.11.0 // indirect @@ -167,6 +170,7 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/go-viper/mapstructure/v2 v2.1.0 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gofrs/flock v0.12.1 // indirect github.com/gogo/googleapis v1.4.1 // indirect @@ -183,6 +187,7 @@ require ( github.com/google/orderedcode v0.0.1 // indirect github.com/google/pprof v0.0.0-20241017200806-017d972448fc // indirect github.com/google/s2a-go v0.1.8 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.13.0 // indirect @@ -242,6 +247,7 @@ require ( github.com/onsi/ginkgo/v2 v2.20.2 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/opencontainers/runc v1.1.14 // indirect github.com/opencontainers/runtime-spec v1.2.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect @@ -279,6 +285,9 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ulikunitz/xz v0.5.11 // indirect github.com/vbatts/tar-split v0.11.6 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 // indirect diff --git a/go.sum b/go.sum index 65b6eee4..390edb82 100644 --- a/go.sum +++ b/go.sum @@ -238,6 +238,8 @@ cosmossdk.io/x/tx v0.13.7 h1:8WSk6B/OHJLYjiZeMKhq7DK7lHDMyK0UfDbBMxVmeOI= cosmossdk.io/x/tx v0.13.7/go.mod h1:V6DImnwJMTq5qFjeGWpXNiT/fjgE4HtmclRmTqRVM3w= cosmossdk.io/x/upgrade v0.1.4 h1:/BWJim24QHoXde8Bc64/2BSEB6W4eTydq0X/2f8+g38= cosmossdk.io/x/upgrade v0.1.4/go.mod h1:9v0Aj+fs97O+Ztw+tG3/tp5JSlrmT7IcFhAebQHmOPo= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= @@ -575,9 +577,13 @@ github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXS github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w= +github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= @@ -704,6 +710,8 @@ github.com/google/pprof v0.0.0-20241017200806-017d972448fc/go.mod h1:vavhavw2zAx github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -1008,6 +1016,8 @@ github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= +github.com/ory/dockertest/v3 v3.11.0 h1:OiHcxKAvSDUwsEVh2BjxQQc/5EHz9n0va9awCtNGuyA= +github.com/ory/dockertest/v3 v3.11.0/go.mod h1:VIPxS1gwT9NpPOrfD3rACs8Y9Z7yhzO4SB194iUDnUI= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= @@ -1193,6 +1203,13 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs= github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/tests/connect/connect_integration_test.go b/tests/connect/connect_integration_test.go index 4875c47b..9f4c019e 100644 --- a/tests/connect/connect_integration_test.go +++ b/tests/connect/connect_integration_test.go @@ -80,7 +80,7 @@ var ( Name: "slinky", NumValidators: &numValidators, NumFullNodes: &numFullNodes, - Version: "local", + Version: "latest", NoHostMount: &noHostMount, ChainConfig: ibc.ChainConfig{ EncodingConfig: &encodingConfig, diff --git a/tests/e2e/address.go b/tests/e2e/address.go new file mode 100644 index 00000000..33079aa8 --- /dev/null +++ b/tests/e2e/address.go @@ -0,0 +1,33 @@ +package e2e + +import ( + "fmt" + "math/rand" + "strconv" + + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + crypto "github.com/cosmos/cosmos-sdk/crypto/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// HDPath generates an HD path based on the wallet index +func HDPath(index int) string { + return fmt.Sprintf("m/44'/118'/0'/0/%d", index) +} + +// PubKey returns a sample account PubKey +func PubKey() crypto.PubKey { + seed := []byte(strconv.Itoa(rand.Int())) + return ed25519.GenPrivKeyFromSecret(seed).PubKey() +} + +// AccAddress returns a sample account address +func AccAddress() sdk.AccAddress { + addr := PubKey().Address() + return sdk.AccAddress(addr) +} + +// Address returns a sample string account address +func Address() string { + return AccAddress().String() +} diff --git a/tests/e2e/chain.go b/tests/e2e/chain.go new file mode 100644 index 00000000..1fc1ed45 --- /dev/null +++ b/tests/e2e/chain.go @@ -0,0 +1,185 @@ +package e2e + +import ( + "fmt" + "os" + + tmrand "github.com/cometbft/cometbft/libs/rand" + + dbm "github.com/cosmos/cosmos-db" + ratelimittypes "github.com/cosmos/ibc-apps/modules/rate-limiting/v8/types" + + "cosmossdk.io/log" + evidencetypes "cosmossdk.io/x/evidence/types" + upgradetypes "cosmossdk.io/x/upgrade/types" + + wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distribtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + govv1types "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govv1beta1types "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + paramsproptypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "github.com/MANTRA-Chain/mantrachain/app" + "github.com/MANTRA-Chain/mantrachain/app/params" +) + +const ( + keyringPassphrase = "testpassphrase" + keyringAppName = "testnet" +) + +var ( + encodingConfig params.EncodingConfig + cdc codec.Codec + txConfig client.TxConfig +) + +func init() { + encodingConfig = params.MakeEncodingConfig() + banktypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) + authtypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) + authvesting.RegisterInterfaces(encodingConfig.InterfaceRegistry) + stakingtypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) + evidencetypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) + cryptocodec.RegisterInterfaces(encodingConfig.InterfaceRegistry) + govv1types.RegisterInterfaces(encodingConfig.InterfaceRegistry) + govv1beta1types.RegisterInterfaces(encodingConfig.InterfaceRegistry) + paramsproptypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) + paramsproptypes.RegisterLegacyAminoCodec(encodingConfig.Amino) + + upgradetypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) + distribtypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) + ratelimittypes.RegisterInterfaces(encodingConfig.InterfaceRegistry) + + cdc = encodingConfig.Codec + txConfig = encodingConfig.TxConfig +} + +type chain struct { + dataDir string + id string + validators []*validator + accounts []*account //nolint:unused + // initial accounts in genesis + genesisAccounts []*account + genesisVestingAccounts map[string]sdk.AccAddress +} + +func newChain() (*chain, error) { + tmpDir, err := os.MkdirTemp("", "app-e2e-testnet-") + if err != nil { + return nil, err + } + + return &chain{ + id: "chain-" + tmrand.Str(6), + dataDir: tmpDir, + }, nil +} + +func (c *chain) configDir() string { + return fmt.Sprintf("%s/%s", c.dataDir, c.id) +} + +func (c *chain) createAndInitValidators(count int) error { + var emptyWasmOpts []wasmkeeper.Option + tempApplication := app.New( + log.NewNopLogger(), + dbm.NewMemDB(), + nil, + true, + app.EmptyAppOptions{}, + emptyWasmOpts, + ) + defer func() { + if err := tempApplication.Close(); err != nil { + panic(err) + } + }() + + genesisState := tempApplication.DefaultGenesis() + + for i := 0; i < count; i++ { + node := c.createValidator(i) + + // generate genesis files + if err := node.init(genesisState); err != nil { + return err + } + + c.validators = append(c.validators, node) + + // create keys + if err := node.createKey("val"); err != nil { + return err + } + if err := node.createNodeKey(); err != nil { + return err + } + if err := node.createConsensusKey(); err != nil { + return err + } + } + + return nil +} + +func (c *chain) createAndInitValidatorsWithMnemonics(count int, mnemonics []string) error { //nolint:unused // this is called during e2e tests + var emptyWasmOpts []wasmkeeper.Option + tempApplication := app.New( + log.NewNopLogger(), + dbm.NewMemDB(), + nil, + true, + app.EmptyAppOptions{}, + emptyWasmOpts, + ) + defer func() { + if err := tempApplication.Close(); err != nil { + panic(err) + } + }() + + genesisState := tempApplication.DefaultGenesis() + + for i := 0; i < count; i++ { + // create node + node := c.createValidator(i) + + // generate genesis files + if err := node.init(genesisState); err != nil { + return err + } + + c.validators = append(c.validators, node) + + // create keys + if err := node.createKeyFromMnemonic("val", mnemonics[i]); err != nil { + return err + } + if err := node.createNodeKey(); err != nil { + return err + } + if err := node.createConsensusKey(); err != nil { + return err + } + } + + return nil +} + +func (c *chain) createValidator(index int) *validator { + return &validator{ + chain: c, + index: index, + moniker: fmt.Sprintf("%s-app-%d", c.id, index), + } +} diff --git a/tests/e2e/e2e_bank_test.go b/tests/e2e/e2e_bank_test.go new file mode 100644 index 00000000..80b425c2 --- /dev/null +++ b/tests/e2e/e2e_bank_test.go @@ -0,0 +1,98 @@ +package e2e + +import ( + "fmt" + "time" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (s *IntegrationTestSuite) testBankTokenTransfer() { + s.Run("send_tokens_between_accounts", func() { + var ( + err error + valIdx = 0 + c = s.chainA + chainEndpoint = fmt.Sprintf("http://%s", s.valResources[c.id][valIdx].GetHostPort("1317/tcp")) + ) + + // define one sender and two recipient accounts + alice, _ := c.genesisAccounts[1].keyInfo.GetAddress() + bob, _ := c.genesisAccounts[2].keyInfo.GetAddress() + charlie, _ := c.genesisAccounts[3].keyInfo.GetAddress() + + var beforeAliceUomBalance, + beforeBobUomBalance, + beforeCharlieUomBalance, + afterAliceUomBalance, + afterBobUomBalance, + afterCharlieUomBalance sdk.Coin + + // get balances of sender and recipient accounts + s.Require().Eventually( + func() bool { + beforeAliceUomBalance, err = getSpecificBalance(chainEndpoint, alice.String(), uomDenom) + s.Require().NoError(err) + + beforeBobUomBalance, err = getSpecificBalance(chainEndpoint, bob.String(), uomDenom) + s.Require().NoError(err) + + beforeCharlieUomBalance, err = getSpecificBalance(chainEndpoint, charlie.String(), uomDenom) + s.Require().NoError(err) + + return beforeAliceUomBalance.IsValid() && beforeBobUomBalance.IsValid() && beforeCharlieUomBalance.IsValid() + }, + 10*time.Second, + 5*time.Second, + ) + + // alice sends tokens to bob + s.execBankSend(s.chainA, valIdx, alice.String(), bob.String(), tokenAmount.String(), standardFees.String(), false) + + // check that the transfer was successful + s.Require().Eventually( + func() bool { + afterAliceUomBalance, err = getSpecificBalance(chainEndpoint, alice.String(), uomDenom) + s.Require().NoError(err) + + afterBobUomBalance, err = getSpecificBalance(chainEndpoint, bob.String(), uomDenom) + s.Require().NoError(err) + + decremented := beforeAliceUomBalance.Sub(tokenAmount).Sub(sdk.NewCoin(uomDenom, math.NewInt(1170))).IsEqual(afterAliceUomBalance) + incremented := beforeBobUomBalance.Add(tokenAmount).IsEqual(afterBobUomBalance) + + return decremented && incremented + }, + 10*time.Second, + 5*time.Second, + ) + + // save the updated account balances of alice and bob + beforeAliceUomBalance, beforeBobUomBalance = afterAliceUomBalance, afterBobUomBalance + + // alice sends tokens to bob and charlie, at once + s.execBankMultiSend(s.chainA, valIdx, alice.String(), []string{bob.String(), charlie.String()}, tokenAmount.String(), standardFees.String(), false) + + s.Require().Eventually( + func() bool { + afterAliceUomBalance, err = getSpecificBalance(chainEndpoint, alice.String(), uomDenom) + s.Require().NoError(err) + + afterBobUomBalance, err = getSpecificBalance(chainEndpoint, bob.String(), uomDenom) + s.Require().NoError(err) + + afterCharlieUomBalance, err = getSpecificBalance(chainEndpoint, charlie.String(), uomDenom) + s.Require().NoError(err) + + decremented := beforeAliceUomBalance.Sub(tokenAmount).Sub(tokenAmount).Sub(sdk.NewCoin(uomDenom, math.NewInt(963))).IsEqual(afterAliceUomBalance) + incremented := beforeBobUomBalance.Add(tokenAmount).IsEqual(afterBobUomBalance) && + beforeCharlieUomBalance.Add(tokenAmount).IsEqual(afterCharlieUomBalance) + + return decremented && incremented + }, + 10*time.Second, + 5*time.Second, + ) + }) +} diff --git a/tests/e2e/e2e_distribution_test.go b/tests/e2e/e2e_distribution_test.go new file mode 100644 index 00000000..6322e5f6 --- /dev/null +++ b/tests/e2e/e2e_distribution_test.go @@ -0,0 +1,87 @@ +package e2e + +import ( + "fmt" + "time" + + "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (s *IntegrationTestSuite) testDistribution() { + chainEndpoint := fmt.Sprintf("http://%s", s.valResources[s.chainA.id][0].GetHostPort("1317/tcp")) + + validatorB := s.chainA.validators[1] + validatorBAddr, _ := validatorB.keyInfo.GetAddress() + + valOperAddressA := sdk.ValAddress(validatorBAddr).String() + + delegatorAddress, _ := s.chainA.genesisAccounts[2].keyInfo.GetAddress() + + newWithdrawalAddress, _ := s.chainA.genesisAccounts[3].keyInfo.GetAddress() + + beforeBalance, err := getSpecificBalance(chainEndpoint, newWithdrawalAddress.String(), uomDenom) + s.Require().NoError(err) + if beforeBalance.IsNil() { + beforeBalance = sdk.NewCoin(uomDenom, math.NewInt(0)) + } + + s.execSetWithdrawAddress(s.chainA, 0, standardFees.String(), delegatorAddress.String(), newWithdrawalAddress.String(), mantraHomePath) + + // Verify + s.Require().Eventually( + func() bool { + res, err := queryDelegatorWithdrawalAddress(chainEndpoint, delegatorAddress.String()) + s.Require().NoError(err) + + return res.WithdrawAddress == newWithdrawalAddress.String() + }, + 10*time.Second, + 5*time.Second, + ) + + s.execWithdrawReward(s.chainA, 0, delegatorAddress.String(), valOperAddressA, mantraHomePath) + s.Require().Eventually( + func() bool { + afterBalance, err := getSpecificBalance(chainEndpoint, newWithdrawalAddress.String(), uomDenom) + s.Require().NoError(err) + + return afterBalance.IsGTE(beforeBalance) + }, + 10*time.Second, + 5*time.Second, + ) +} + +/* +fundCommunityPool tests the funding of the community pool on behalf of the distribution module. +Test Benchmarks: +1. Validation that balance of the distribution module account before funding +2. Execution funding the community pool +3. Verification that correct funds have been deposited to distribution module account +*/ +func (s *IntegrationTestSuite) fundCommunityPool() { + chainAAPIEndpoint := fmt.Sprintf("http://%s", s.valResources[s.chainA.id][0].GetHostPort("1317/tcp")) + sender, _ := s.chainA.validators[0].keyInfo.GetAddress() + + beforeDistUomBalance, _ := getSpecificBalance(chainAAPIEndpoint, distModuleAddress, tokenAmount.Denom) + if beforeDistUomBalance.IsNil() { + // Set balance to 0 if previous balance does not exist + beforeDistUomBalance = sdk.NewInt64Coin(uomDenom, 0) + } + + s.execDistributionFundCommunityPool(s.chainA, 0, sender.String(), tokenAmount.String(), standardFees.String()) + + s.Require().Eventually( + func() bool { + afterDistUomBalance, err := getSpecificBalance(chainAAPIEndpoint, distModuleAddress, tokenAmount.Denom) + s.Require().NoErrorf(err, "Error getting balance: %s", afterDistUomBalance) + + // check if the balance is increased by the tokenAmount + return beforeDistUomBalance.Add(tokenAmount).IsEqual(afterDistUomBalance) + }, + 15*time.Second, + 5*time.Second, + ) +} diff --git a/tests/e2e/e2e_encode_test.go b/tests/e2e/e2e_encode_test.go new file mode 100644 index 00000000..7476a33b --- /dev/null +++ b/tests/e2e/e2e_encode_test.go @@ -0,0 +1,50 @@ +package e2e + +import ( + "encoding/base64" + "path/filepath" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + rawTxFile = "tx_raw.json" +) + +func (s *IntegrationTestSuite) testEncode() { + chain := s.chainA + _, encoded, err := buildRawTx() + s.Require().NoError(err) + + got := s.execEncode(chain, filepath.Join(mantraHomePath, rawTxFile)) + s.T().Logf("encoded tx: %s", got) + s.Require().Equal(encoded, got) +} + +func (s *IntegrationTestSuite) testDecode() { + chain := s.chainA + rawTx, encoded, err := buildRawTx() + s.Require().NoError(err) + + got := s.execDecode(chain, encoded) + s.T().Logf("raw tx: %s", got) + s.Require().Equal(string(rawTx), got) +} + +// buildRawTx build a dummy tx using the TxBuilder and +// return the JSON and encoded tx's +func buildRawTx() ([]byte, string, error) { + builder := txConfig.NewTxBuilder() + builder.SetGasLimit(gas) + builder.SetFeeAmount(sdk.NewCoins(standardFees)) + builder.SetMemo("foomemo") + tx, err := txConfig.TxJSONEncoder()(builder.GetTx()) + if err != nil { + return nil, "", err + } + txBytes, err := txConfig.TxEncoder()(builder.GetTx()) + if err != nil { + return nil, "", err + } + return tx, base64.StdEncoding.EncodeToString(txBytes), err +} diff --git a/tests/e2e/e2e_evidence_test.go b/tests/e2e/e2e_evidence_test.go new file mode 100644 index 00000000..d81bee74 --- /dev/null +++ b/tests/e2e/e2e_evidence_test.go @@ -0,0 +1,55 @@ +package e2e + +import ( + "context" + "encoding/hex" + "fmt" + "strings" + "time" + + "cosmossdk.io/x/evidence/exported" + evidencetypes "cosmossdk.io/x/evidence/types" +) + +func (s *IntegrationTestSuite) testEvidence() { + s.Run("test evidence queries", func() { + var ( + valIdx = 0 + chain = s.chainA + chainAPI = fmt.Sprintf("http://%s", s.valResources[chain.id][valIdx].GetHostPort("1317/tcp")) + ) + res, err := queryAllEvidence(chainAPI) + s.Require().NoError(err) + s.Require().Equal(numberOfEvidences, len(res.Evidence)) + for _, evidence := range res.Evidence { + var exportedEvidence exported.Evidence + err := cdc.UnpackAny(evidence, &exportedEvidence) + s.Require().NoError(err) + eq, ok := exportedEvidence.(*evidencetypes.Equivocation) + s.Require().True(ok) + s.execQueryEvidence(chain, valIdx, strings.ToUpper(hex.EncodeToString(eq.Hash()))) + } + }) +} + +func (s *IntegrationTestSuite) execQueryEvidence(c *chain, valIdx int, hash string) (res evidencetypes.Equivocation) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("querying evidence %s on chain %s", hash, c.id) + + command := []string{ + mantrachaindBinary, + queryCommand, + evidencetypes.ModuleName, + hash, + } + + s.executeTxCommand(ctx, c, command, valIdx, func(stdOut []byte, stdErr []byte) bool { + // TODO parse evidence after fix the SDK + // https://github.com/cosmos/cosmos-sdk/issues/13444 + // s.Require().NoError(yaml.Unmarshal(stdOut, &res)) + return true + }) + return res +} diff --git a/tests/e2e/e2e_exec_test.go b/tests/e2e/e2e_exec_test.go new file mode 100644 index 00000000..59e82afc --- /dev/null +++ b/tests/e2e/e2e_exec_test.go @@ -0,0 +1,956 @@ +package e2e + +import ( + "bufio" + "bytes" + "context" + "encoding/json" + "fmt" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/ory/dockertest/v3/docker" + + "cosmossdk.io/x/feegrant" + + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +const ( + flagFrom = "from" + flagHome = "home" + flagFees = "fees" + flagGas = "gas" + flagOutput = "output" + flagChainID = "chain-id" + flagSpendLimit = "spend-limit" + flagGasAdjustment = "gas-adjustment" + flagFeeGranter = "fee-granter" + flagBroadcastMode = "broadcast-mode" + flagKeyringBackend = "keyring-backend" + flagAllowedMessages = "allowed-messages" +) + +type flagOption func(map[string]interface{}) + +// withKeyValue add a new flag to command + +func withKeyValue(key string, value interface{}) flagOption { + return func(o map[string]interface{}) { + o[key] = value + } +} + +func applyOptions(chainID string, options []flagOption) map[string]interface{} { + opts := map[string]interface{}{ + flagKeyringBackend: "test", + flagOutput: "json", + flagGas: "auto", + flagFrom: "alice", + flagBroadcastMode: "sync", + flagGasAdjustment: "1.5", + flagChainID: chainID, + flagHome: mantraHomePath, + flagFees: standardFees.String(), + } + for _, apply := range options { + apply(opts) + } + return opts +} + +func (s *IntegrationTestSuite) execEncode( + c *chain, + txPath string, + opt ...flagOption, +) string { + opts := applyOptions(c.id, opt) + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("%s - Executing mantrachaind encoding with %v", c.id, txPath) + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + "encode", + txPath, + } + for flag, value := range opts { + mantraCommand = append(mantraCommand, fmt.Sprintf("--%s=%v", flag, value)) + } + + var encoded string + s.executeTxCommand(ctx, c, mantraCommand, 0, func(stdOut []byte, stdErr []byte) bool { + if stdErr != nil { + return false + } + encoded = strings.TrimSuffix(string(stdOut), "\n") + return true + }) + s.T().Logf("successfully encode with %v", txPath) + return encoded +} + +func (s *IntegrationTestSuite) execDecode( + c *chain, + txPath string, + opt ...flagOption, +) string { + opts := applyOptions(c.id, opt) + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("%s - Executing mantrachaind decoding with %v", c.id, txPath) + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + "decode", + txPath, + } + for flag, value := range opts { + mantraCommand = append(mantraCommand, fmt.Sprintf("--%s=%v", flag, value)) + } + + var decoded string + s.executeTxCommand(ctx, c, mantraCommand, 0, func(stdOut []byte, stdErr []byte) bool { + if stdErr != nil { + return false + } + decoded = strings.TrimSuffix(string(stdOut), "\n") + return true + }) + s.T().Logf("successfully decode %v", txPath) + return decoded +} + +func (s *IntegrationTestSuite) execVestingTx( //nolint:unused + + c *chain, + method string, + args []string, + opt ...flagOption, +) { + opts := applyOptions(c.id, opt) + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("%s - Executing mantrachaind %s with %v", c.id, method, args) + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + vestingtypes.ModuleName, + method, + "-y", + } + mantraCommand = append(mantraCommand, args...) + + for flag, value := range opts { + mantraCommand = append(mantraCommand, fmt.Sprintf("--%s=%v", flag, value)) + } + + s.executeTxCommand(ctx, c, mantraCommand, 0, s.defaultExecValidation(c, 0)) + s.T().Logf("successfully %s with %v", method, args) +} + +func (s *IntegrationTestSuite) execCreatePeriodicVestingAccount( //nolint:unused + + c *chain, + address, + jsonPath string, + opt ...flagOption, +) { + s.T().Logf("Executing mantrachaind create periodic vesting account %s", c.id) + s.execVestingTx(c, "create-periodic-vesting-account", []string{address, jsonPath}, opt...) + s.T().Logf("successfully created periodic vesting account %s with %s", address, jsonPath) +} + +func (s *IntegrationTestSuite) execUnjail( + c *chain, + opt ...flagOption, +) { + opts := applyOptions(c.id, opt) + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("Executing mantrachaind slashing unjail %s with options: %v", c.id, opt) + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + slashingtypes.ModuleName, + "unjail", + "-y", + } + + for flag, value := range opts { + mantraCommand = append(mantraCommand, fmt.Sprintf("--%s=%v", flag, value)) + } + + s.executeTxCommand(ctx, c, mantraCommand, 0, s.defaultExecValidation(c, 0)) + s.T().Logf("successfully unjail with options %v", opt) +} + +func (s *IntegrationTestSuite) execFeeGrant(c *chain, valIdx int, granter, grantee, spendLimit string, opt ...flagOption) { + opt = append(opt, withKeyValue(flagFrom, granter)) + opt = append(opt, withKeyValue(flagSpendLimit, spendLimit)) + opts := applyOptions(c.id, opt) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("granting %s fee from %s on chain %s", grantee, granter, c.id) + + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + feegrant.ModuleName, + "grant", + granter, + grantee, + fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), + fmt.Sprintf("--%s=%s", flags.FlagGas, "300000"), // default 200000 isn't enough + "--keyring-backend=test", + "--output=json", + "-y", + } + for flag, value := range opts { + mantraCommand = append(mantraCommand, fmt.Sprintf("--%s=%s", flag, value)) + } + s.T().Logf("running feegrant on chain: %s - Tx %v", c.id, mantraCommand) + + s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) +} + +func (s *IntegrationTestSuite) execFeeGrantRevoke(c *chain, valIdx int, granter, grantee string, opt ...flagOption) { + opt = append(opt, withKeyValue(flagFrom, granter)) + opts := applyOptions(c.id, opt) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("revoking %s fee grant from %s on chain %s", grantee, granter, c.id) + + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + feegrant.ModuleName, + "revoke", + granter, + grantee, + "-y", + } + for flag, value := range opts { + mantraCommand = append(mantraCommand, fmt.Sprintf("--%s=%v", flag, value)) + } + + s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) +} + +func (s *IntegrationTestSuite) execBankSend( + c *chain, + valIdx int, + from, + to, + amt, + fees string, + expectErr bool, + opt ...flagOption, +) { + // TODO remove the hardcode opt after refactor, all methods should accept custom flags + opt = append(opt, withKeyValue(flagFees, fees)) + opt = append(opt, withKeyValue(flagFrom, from)) + opts := applyOptions(c.id, opt) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("sending %s tokens from %s to %s on chain %s", amt, from, to, c.id) + + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + banktypes.ModuleName, + "send", + from, + to, + amt, + "-y", + } + for flag, value := range opts { + mantraCommand = append(mantraCommand, fmt.Sprintf("--%s=%v", flag, value)) + } + + s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.expectErrExecValidation(c, valIdx, expectErr)) +} + +func (s *IntegrationTestSuite) execBankMultiSend( + c *chain, + valIdx int, + from string, + to []string, + amt string, + fees string, + expectErr bool, + opt ...flagOption, +) { + // TODO remove the hardcode opt after refactor, all methods should accept custom flags + opt = append(opt, withKeyValue(flagFees, fees)) + opt = append(opt, withKeyValue(flagFrom, from)) + opts := applyOptions(c.id, opt) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("sending %s tokens from %s to %s on chain %s", amt, from, to, c.id) + + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + banktypes.ModuleName, + "multi-send", + from, + } + + mantraCommand = append(mantraCommand, to...) + mantraCommand = append(mantraCommand, amt, "-y") + + for flag, value := range opts { + mantraCommand = append(mantraCommand, fmt.Sprintf("--%s=%v", flag, value)) + } + + s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.expectErrExecValidation(c, valIdx, expectErr)) +} + +func (s *IntegrationTestSuite) execDistributionFundCommunityPool(c *chain, valIdx int, from, amt, fees string) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("Executing mantrachaind tx distribution fund-community-pool on chain %s", c.id) + + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + distributiontypes.ModuleName, + "fund-community-pool", + amt, + fmt.Sprintf("--%s=%s", flags.FlagFrom, from), + fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), + fmt.Sprintf("--%s=%s", flags.FlagFees, fees), + "--keyring-backend=test", + "--output=json", + "-y", + } + + s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.T().Logf("Successfully funded community pool") +} + +func (s *IntegrationTestSuite) runGovExec(c *chain, valIdx int, submitterAddr, govCommand string, proposalFlags []string, fees string, validationFunc func([]byte, []byte) bool) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + validateResponse := s.defaultExecValidation(c, valIdx) + if validationFunc != nil { + validateResponse = validationFunc + } + + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + govtypes.ModuleName, + govCommand, + } + + generalFlags := []string{ + fmt.Sprintf("--%s=%s", flags.FlagFrom, submitterAddr), + fmt.Sprintf("--%s=%s", flags.FlagGas, "400000"), // default 200000 isn't enough + fmt.Sprintf("--%s=%s", flags.FlagGasPrices, fees), + fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), + "--keyring-backend=test", + "--output=json", + "-y", + } + + mantraCommand = concatFlags(mantraCommand, proposalFlags, generalFlags) + s.T().Logf("Executing mantrachaind tx gov %s on chain %s", govCommand, c.id) + s.executeTxCommand(ctx, c, mantraCommand, valIdx, validateResponse) + s.T().Logf("Successfully executed %s", govCommand) +} + +// NOTE: Tx unused, left here for future reference +// func (s *IntegrationTestSuite) executeGKeysAddCommand(c *chain, valIdx int, name string, home string) string { +// ctx, cancel := context.WithTimeout(context.Background(), time.Minute) +// defer cancel() + +// mantraCommand := []string{ +// mantrachaindBinary, +// keysCommand, +// "add", +// name, +// fmt.Sprintf("--%s=%s", flags.FlagHome, home), +// "--keyring-backend=test", +// "--output=json", +// } + +// var addrRecord AddressResponse +// s.executeTxCommand(ctx, c, mantraCommand, valIdx, func(stdOut []byte, stdErr []byte) bool { +// // Mantrachaind keys add by default returns payload to stdErr +// if err := json.Unmarshal(stdErr, &addrRecord); err != nil { +// return false +// } +// return strings.Contains(addrRecord.Address, "cosmos") +// }) +// return addrRecord.Address +// } + +// NOTE: Tx unused, left here for future reference +// func (s *IntegrationTestSuite) executeKeysList(c *chain, valIdx int, home string) { // nolint:U1000 +// ctx, cancel := context.WithTimeout(context.Background(), time.Minute) +// defer cancel() + +// mantraCommand := []string{ +// mantrachaindBinary, +// keysCommand, +// "list", +// "--keyring-backend=test", +// fmt.Sprintf("--%s=%s", flags.FlagHome, home), +// "--output=json", +// } + +// s.executeTxCommand(ctx, c, mantraCommand, valIdx, func([]byte, []byte) bool { +// return true +// }) +// } + +func (s *IntegrationTestSuite) execDelegate(c *chain, valIdx int, amount, valOperAddress, delegatorAddr, home, delegateFees string) { //nolint:unparam + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("Executing mantrachaind tx staking delegate %s", c.id) + + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + stakingtypes.ModuleName, + "delegate", + valOperAddress, + amount, + fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddr), + fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), + fmt.Sprintf("--%s=%s", flags.FlagGasPrices, delegateFees), + fmt.Sprintf("--%s=%s", flags.FlagGas, "250000"), // default 200_000 is not enough + "--keyring-backend=test", + fmt.Sprintf("--%s=%s", flags.FlagHome, home), + "--output=json", + "-y", + } + + s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.T().Logf("%s successfully delegated %s to %s", delegatorAddr, amount, valOperAddress) +} + +func (s *IntegrationTestSuite) execUnbondDelegation(c *chain, valIdx int, amount, valOperAddress, delegatorAddr, home, delegateFees string) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("Executing mantrachaind tx staking unbond %s", c.id) + + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + stakingtypes.ModuleName, + "unbond", + valOperAddress, + amount, + fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddr), + fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), + fmt.Sprintf("--%s=%s", flags.FlagGasPrices, delegateFees), + "--gas=300000", // default 200_000 is not enough; gas fees are higher when unbonding is done after LSM operations + "--keyring-backend=test", + fmt.Sprintf("--%s=%s", flags.FlagHome, home), + "--output=json", + "-y", + } + + s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.T().Logf("%s successfully undelegated %s to %s", delegatorAddr, amount, valOperAddress) +} + +func (s *IntegrationTestSuite) execCancelUnbondingDelegation(c *chain, valIdx int, amount, valOperAddress, creationHeight, delegatorAddr, home, delegateFees string) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("Executing mantrachaind tx staking cancel-unbond %s", c.id) + + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + stakingtypes.ModuleName, + "cancel-unbond", + valOperAddress, + amount, + creationHeight, + fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddr), + fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), + fmt.Sprintf("--%s=%s", flags.FlagGasPrices, delegateFees), + "--keyring-backend=test", + fmt.Sprintf("--%s=%s", flags.FlagHome, home), + "--output=json", + "-y", + } + + s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.T().Logf("%s successfully canceled unbonding %s to %s", delegatorAddr, amount, valOperAddress) +} + +func (s *IntegrationTestSuite) execRedelegate(c *chain, valIdx int, amount, originalValOperAddress, + newValOperAddress, delegatorAddr, home, delegateFees string, +) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("Executing mantrachaind tx staking redelegate %s", c.id) + + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + stakingtypes.ModuleName, + "redelegate", + originalValOperAddress, + newValOperAddress, + amount, + fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddr), + fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), + fmt.Sprintf("--%s=%s", flags.FlagGas, "350000"), // default 200000 isn't enough + fmt.Sprintf("--%s=%s", flags.FlagGasPrices, delegateFees), + "--keyring-backend=test", + fmt.Sprintf("--%s=%s", flags.FlagHome, home), + "--output=json", + "-y", + } + + s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.T().Logf("%s successfully redelegated %s from %s to %s", delegatorAddr, amount, originalValOperAddress, newValOperAddress) +} + +func (s *IntegrationTestSuite) getLatestBlockHeight(c *chain, valIdx int) int { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + type syncInfo struct { + SyncInfo struct { + LatestHeight string `json:"latest_block_height"` + } `json:"sync_info"` + } + + var currentHeight int + mantraCommand := []string{mantrachaindBinary, "status"} + s.executeTxCommand(ctx, c, mantraCommand, valIdx, func(stdOut []byte, stdErr []byte) bool { + var ( + err error + block syncInfo + ) + s.Require().NoError(json.Unmarshal(stdOut, &block)) + currentHeight, err = strconv.Atoi(block.SyncInfo.LatestHeight) + s.Require().NoError(err) + return currentHeight > 0 + }) + return currentHeight +} + +// func (s *IntegrationTestSuite) verifyBalanceChange(endpoint string, expectedAmount sdk.Coin, recipientAddress string) { +// s.Require().Eventually( +// func() bool { +// afterOmBalance, err := getSpecificBalance(endpoint, recipientAddress, uomDenom) +// s.Require().NoError(err) + +// return afterOmBalance.IsEqual(expectedAmount) +// }, +// 20*time.Second, +// 5*time.Second, +// ) +// } + +func (s *IntegrationTestSuite) execSetWithdrawAddress( + c *chain, + valIdx int, + fees, + delegatorAddress, + newWithdrawalAddress, + homePath string, +) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("Setting distribution withdrawal address on chain %s for %s to %s", c.id, delegatorAddress, newWithdrawalAddress) + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + distributiontypes.ModuleName, + "set-withdraw-addr", + newWithdrawalAddress, + fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddress), + fmt.Sprintf("--%s=%s", flags.FlagFees, fees), + fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), + fmt.Sprintf("--%s=%s", flags.FlagHome, homePath), + "--keyring-backend=test", + "--output=json", + "-y", + } + + s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.T().Logf("Successfully set new distribution withdrawal address for %s to %s", delegatorAddress, newWithdrawalAddress) +} + +func (s *IntegrationTestSuite) execWithdrawReward( + c *chain, + valIdx int, + delegatorAddress, + validatorAddress, + homePath string, +) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("Withdrawing distribution rewards on chain %s for delegator %s from %s validator", c.id, delegatorAddress, validatorAddress) + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + distributiontypes.ModuleName, + "withdraw-rewards", + validatorAddress, + fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddress), + fmt.Sprintf("--%s=%s", flags.FlagGasPrices, "300uom"), + fmt.Sprintf("--%s=%s", flags.FlagGas, "auto"), + fmt.Sprintf("--%s=%s", flags.FlagGasAdjustment, "1.5"), + fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), + fmt.Sprintf("--%s=%s", flags.FlagHome, homePath), + "--keyring-backend=test", + "--output=json", + "-y", + } + + s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.T().Logf("Successfully withdrew distribution rewards for delegator %s from validator %s", delegatorAddress, validatorAddress) +} + +func (s *IntegrationTestSuite) executeTxCommand(ctx context.Context, c *chain, mantraCommand []string, valIdx int, validation func([]byte, []byte) bool) { + if validation == nil { + validation = s.defaultExecValidation(s.chainA, 0) + } + var ( + outBuf bytes.Buffer + errBuf bytes.Buffer + ) + exec, err := s.dkrPool.Client.CreateExec(docker.CreateExecOptions{ + Context: ctx, + AttachStdout: true, + AttachStderr: true, + Container: s.valResources[c.id][valIdx].Container.ID, + User: "nonroot", + Cmd: mantraCommand, + }) + s.Require().NoError(err) + + err = s.dkrPool.Client.StartExec(exec.ID, docker.StartExecOptions{ + Context: ctx, + Detach: false, + OutputStream: &outBuf, + ErrorStream: &errBuf, + }) + s.Require().NoError(err) + + stdOut := outBuf.Bytes() + stdErr := errBuf.Bytes() + if !validation(stdOut, stdErr) { + s.Require().FailNowf("Exec validation failed", "stdout: %s, stderr: %s", + string(stdOut), string(stdErr)) + } +} + +func (s *IntegrationTestSuite) executeHermesCommand(ctx context.Context, hermesCmd []string) ([]byte, error) { //nolint:unparam + var outBuf bytes.Buffer + exec, err := s.dkrPool.Client.CreateExec(docker.CreateExecOptions{ + Context: ctx, + AttachStdout: true, + AttachStderr: true, + Container: s.hermesResource.Container.ID, + User: "root", + Cmd: hermesCmd, + }) + s.Require().NoError(err) + + err = s.dkrPool.Client.StartExec(exec.ID, docker.StartExecOptions{ + Context: ctx, + Detach: false, + OutputStream: &outBuf, + }) + s.Require().NoError(err) + + // Check that the stdout output contains the expected status + // and look for errors, e.g "insufficient fees" + stdOut := []byte{} + scanner := bufio.NewScanner(&outBuf) + for scanner.Scan() { + stdOut = scanner.Bytes() + var out map[string]interface{} + err = json.Unmarshal(stdOut, &out) + s.Require().NoError(err) + if err != nil { + return nil, fmt.Errorf("hermes relayer command returned failed with error: %s", err) + } + // errors are caught by observing the logs level in the stderr output + if lvl := out["level"]; lvl != nil && strings.ToLower(lvl.(string)) == "error" { + errMsg := out["fields"].(map[string]interface{})["message"] + return nil, fmt.Errorf("hermes relayer command failed: %s", errMsg) + } + if s := out["status"]; s != nil && s != "success" { + return nil, fmt.Errorf("hermes relayer command returned failed with status: %s", s) + } + } + + return stdOut, nil +} + +func (s *IntegrationTestSuite) expectErrExecValidation(chain *chain, valIdx int, expectErr bool) func([]byte, []byte) bool { + return func(stdOut []byte, stdErr []byte) bool { + var txResp sdk.TxResponse + gotErr := cdc.UnmarshalJSON(stdOut, &txResp) != nil + if gotErr { + s.Require().True(expectErr) + } + + endpoint := fmt.Sprintf("http://%s", s.valResources[chain.id][valIdx].GetHostPort("1317/tcp")) + // wait for the tx to be committed on chain + s.Require().Eventuallyf( + func() bool { + gotErr := queryTx(endpoint, txResp.TxHash) != nil + return gotErr == expectErr + }, + time.Minute, + 5*time.Second, + "stdOut: %s, stdErr: %s", + string(stdOut), string(stdErr), + ) + return true + } +} + +func (s *IntegrationTestSuite) defaultExecValidation(chain *chain, valIdx int) func([]byte, []byte) bool { + return func(stdOut []byte, stdErr []byte) bool { + var txResp sdk.TxResponse + if err := cdc.UnmarshalJSON(stdOut, &txResp); err != nil { + return false + } + if strings.Contains(txResp.String(), "code: 0") || txResp.Code == 0 { + endpoint := fmt.Sprintf("http://%s", s.valResources[chain.id][valIdx].GetHostPort("1317/tcp")) + s.Require().Eventually( + func() bool { + return queryTx(endpoint, txResp.TxHash) == nil + }, + time.Minute, + 5*time.Second, + "stdOut: %s, stdErr: %s", + string(stdOut), string(stdErr), + ) + return true + } + return false + } +} + +func (s *IntegrationTestSuite) expectTxSubmitError(expectErrString string) func([]byte, []byte) bool { + return func(stdOut []byte, stdErr []byte) bool { + var txResp sdk.TxResponse + if err := cdc.UnmarshalJSON(stdOut, &txResp); err != nil { + return false + } + if strings.Contains(txResp.RawLog, expectErrString) { + return true + } + return false + } +} + +func (s *IntegrationTestSuite) executeValidatorBond(c *chain, valIdx int, valOperAddress, delegatorAddr, home, delegateFees string) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("Executing mantrachaind tx staking validator-bond %s", c.id) + + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + stakingtypes.ModuleName, + "validator-bond", + valOperAddress, + fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddr), + fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), + fmt.Sprintf("--%s=%s", flags.FlagGasPrices, delegateFees), + "--keyring-backend=test", + fmt.Sprintf("--%s=%s", flags.FlagHome, home), + "--output=json", + "-y", + } + + s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.T().Logf("%s successfully executed validator bond tx to %s", delegatorAddr, valOperAddress) +} + +func (s *IntegrationTestSuite) executeTokenizeShares(c *chain, valIdx int, amount, valOperAddress, delegatorAddr, home, delegateFees string) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("Executing mantrachaind tx staking tokenize-share %s", c.id) + + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + stakingtypes.ModuleName, + "tokenize-share", + valOperAddress, + amount, + delegatorAddr, + fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddr), + fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), + fmt.Sprintf("--%s=%s", flags.FlagGasPrices, delegateFees), + fmt.Sprintf("--%s=%d", flags.FlagGas, 1000000), + "--keyring-backend=test", + fmt.Sprintf("--%s=%s", flags.FlagHome, home), + "--output=json", + "-y", + } + + s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.T().Logf("%s successfully executed tokenize share tx from %s", delegatorAddr, valOperAddress) +} + +func (s *IntegrationTestSuite) executeRedeemShares(c *chain, valIdx int, amount, delegatorAddr, home, delegateFees string) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("Executing mantrachaind tx staking redeem-tokens %s", c.id) + + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + stakingtypes.ModuleName, + "redeem-tokens", + amount, + fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddr), + fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), + fmt.Sprintf("--%s=%s", flags.FlagGasPrices, delegateFees), + fmt.Sprintf("--%s=%d", flags.FlagGas, 1000000), + "--keyring-backend=test", + fmt.Sprintf("--%s=%s", flags.FlagHome, home), + "--output=json", + "-y", + } + + s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.T().Logf("%s successfully executed redeem share tx for %s", delegatorAddr, amount) +} + +func (s *IntegrationTestSuite) executeTransferTokenizeShareRecord(c *chain, valIdx int, recordID, owner, newOwner, home, txFees string) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + s.T().Logf("Executing mantrachaind tx staking transfer-tokenize-share-record %s", c.id) + + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + stakingtypes.ModuleName, + "transfer-tokenize-share-record", + recordID, + newOwner, + fmt.Sprintf("--%s=%s", flags.FlagFrom, owner), + fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), + fmt.Sprintf("--%s=%s", flags.FlagGasPrices, txFees), + "--keyring-backend=test", + fmt.Sprintf("--%s=%s", flags.FlagHome, home), + "--output=json", + "-y", + } + + s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) + s.T().Logf("%s successfully executed transfer tokenize share record for %s", owner, recordID) +} + +// signTxFileOnline signs a transaction file using the mantracli tx sign command +// the from flag is used to specify the keyring account to sign the transaction +// the from account must be registered in the keyring and exist on chain (have a balance or be a genesis account) +func (s *IntegrationTestSuite) signTxFileOnline(chain *chain, valIdx int, from string, txFilePath string) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + mantraCommand := []string{ + mantrachaindBinary, + txCommand, + "sign", + filepath.Join(mantraHomePath, txFilePath), + fmt.Sprintf("--%s=%s", flags.FlagChainID, chain.id), + fmt.Sprintf("--%s=%s", flags.FlagHome, mantraHomePath), + fmt.Sprintf("--%s=%s", flags.FlagFrom, from), + "--keyring-backend=test", + "--output=json", + "-y", + } + + var output []byte + var erroutput []byte + captureOutput := func(stdout []byte, stderr []byte) bool { + output = stdout + erroutput = stderr + return true + } + + s.executeTxCommand(ctx, chain, mantraCommand, valIdx, captureOutput) + if len(erroutput) > 0 { + return nil, fmt.Errorf("failed to sign tx: %s", string(erroutput)) + } + return output, nil +} + +// broadcastTxFile broadcasts a signed transaction file using the mantracli tx broadcast command +// the from flag is used to specify the keyring account to sign the transaction +// the from account must be registered in the keyring and exist on chain (have a balance or be a genesis account) +func (s *IntegrationTestSuite) broadcastTxFile(chain *chain, valIdx int, from string, txFilePath string) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + broadcastTxCmd := []string{ + mantrachaindBinary, + txCommand, + "broadcast", + filepath.Join(mantraHomePath, txFilePath), + fmt.Sprintf("--%s=%s", flags.FlagChainID, chain.id), + fmt.Sprintf("--%s=%s", flags.FlagHome, mantraHomePath), + fmt.Sprintf("--%s=%s", flags.FlagFrom, from), + "--keyring-backend=test", + "--output=json", + "-y", + } + + var output []byte + var erroutput []byte + captureOutput := func(stdout []byte, stderr []byte) bool { + output = stdout + erroutput = stderr + return true + } + + s.executeTxCommand(ctx, chain, broadcastTxCmd, valIdx, captureOutput) + if len(erroutput) > 0 { + return nil, fmt.Errorf("failed to sign tx: %s", string(erroutput)) + } + return output, nil +} diff --git a/tests/e2e/e2e_gov_test.go b/tests/e2e/e2e_gov_test.go new file mode 100644 index 00000000..816cd19b --- /dev/null +++ b/tests/e2e/e2e_gov_test.go @@ -0,0 +1,305 @@ +package e2e + +import ( + "fmt" + "strconv" + "time" + + "cosmossdk.io/math" + upgradetypes "cosmossdk.io/x/upgrade/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govtypesv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" +) + +/* +GovSoftwareUpgrade tests passing a gov proposal to upgrade the chain at a given height. +Test Benchmarks: +1. Submission, deposit and vote of message based proposal to upgrade the chain at a height (current height + buffer) +2. Validation that chain halted at upgrade height +3. Teardown & restart chains +4. Reset proposalCounter so subsequent tests have the correct last effective proposal id for chainA +TODO: Perform upgrade in place of chain restart +*/ +func (s *IntegrationTestSuite) GovSoftwareUpgrade() { + chainAAPIEndpoint := fmt.Sprintf("http://%s", s.valResources[s.chainA.id][0].GetHostPort("1317/tcp")) + senderAddress, _ := s.chainA.validators[0].keyInfo.GetAddress() + sender := senderAddress.String() + height := s.getLatestBlockHeight(s.chainA, 0) + proposalHeight := height + govProposalBlockBuffer + // Gov tests may be run in arbitrary order, each test must increment proposalCounter to have the correct proposal id to submit and query + proposalCounter++ + + s.writeSoftwareUpgradeProposal(s.chainA, int64(proposalHeight), "upgrade-v0") + + submitGovFlags := []string{configFile(proposalSoftwareUpgrade)} + + depositGovFlags := []string{strconv.Itoa(proposalCounter), depositAmount.String()} + voteGovFlags := []string{strconv.Itoa(proposalCounter), "yes=0.8,no=0.1,abstain=0.05,no_with_veto=0.05"} + s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, upgradetypes.ProposalTypeSoftwareUpgrade, submitGovFlags, depositGovFlags, voteGovFlags, "weighted-vote") + + s.verifyChainHaltedAtUpgradeHeight(s.chainA, 0, proposalHeight) + s.T().Logf("Successfully halted chain at height %d", proposalHeight) + + s.TearDownSuite() + + s.T().Logf("Restarting containers") + s.SetupSuite() + + s.Require().Eventually( + func() bool { + h := s.getLatestBlockHeight(s.chainA, 0) + return h > 0 + }, + 30*time.Second, + 5*time.Second, + ) + + proposalCounter = 0 +} + +/* +GovCancelSoftwareUpgrade tests passing a gov proposal that cancels a pending upgrade. +Test Benchmarks: +1. Submission, deposit and vote of message based proposal to upgrade the chain at a height (current height + buffer) +2. Submission, deposit and vote of message based proposal to cancel the pending upgrade +3. Validation that the chain produced blocks past the intended upgrade height +*/ +func (s *IntegrationTestSuite) GovCancelSoftwareUpgrade() { + chainAAPIEndpoint := fmt.Sprintf("http://%s", s.valResources[s.chainA.id][0].GetHostPort("1317/tcp")) + senderAddress, _ := s.chainA.validators[0].keyInfo.GetAddress() + + sender := senderAddress.String() + height := s.getLatestBlockHeight(s.chainA, 0) + proposalHeight := height + 50 + s.writeSoftwareUpgradeProposal(s.chainA, int64(proposalHeight), "upgrade-v1") + + // Gov tests may be run in arbitrary order, each test must increment proposalCounter to have the correct proposal id to submit and query + proposalCounter++ + submitGovFlags := []string{configFile(proposalSoftwareUpgrade)} + + depositGovFlags := []string{strconv.Itoa(proposalCounter), depositAmount.String()} + voteGovFlags := []string{strconv.Itoa(proposalCounter), "yes"} + s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, upgradetypes.ProposalTypeSoftwareUpgrade, submitGovFlags, depositGovFlags, voteGovFlags, "vote") + + proposalCounter++ + s.writeCancelSoftwareUpgradeProposal(s.chainA) + submitGovFlags = []string{configFile(proposalCancelSoftwareUpgrade)} + depositGovFlags = []string{strconv.Itoa(proposalCounter), depositAmount.String()} + voteGovFlags = []string{strconv.Itoa(proposalCounter), "yes"} + s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, upgradetypes.ProposalTypeCancelSoftwareUpgrade, submitGovFlags, depositGovFlags, voteGovFlags, "vote") + + s.verifyChainPassesUpgradeHeight(s.chainA, 0, proposalHeight) + s.T().Logf("Successfully canceled upgrade at height %d", proposalHeight) +} + +/* +GovCommunityPoolSpend tests passing a community spend proposal. +Test Benchmarks: +1. Fund Community Pool +2. Submission, deposit and vote of proposal to spend from the community pool to send oms to a recipient +3. Validation that the recipient balance has increased by proposal amount +*/ +func (s *IntegrationTestSuite) GovCommunityPoolSpend() { + s.fundCommunityPool() + chainAAPIEndpoint := fmt.Sprintf("http://%s", s.valResources[s.chainA.id][0].GetHostPort("1317/tcp")) + senderAddress, _ := s.chainA.validators[0].keyInfo.GetAddress() + sender := senderAddress.String() + recipientAddress, _ := s.chainA.validators[1].keyInfo.GetAddress() + recipient := recipientAddress.String() + sendAmount := sdk.NewCoin(uomDenom, math.NewInt(10000000)) // 10uom + s.writeGovCommunitySpendProposal(s.chainA, sendAmount, recipient) + + beforeRecipientBalance, err := getSpecificBalance(chainAAPIEndpoint, recipient, uomDenom) + s.Require().NoError(err) + + // Gov tests may be run in arbitrary order, each test must increment proposalCounter to have the correct proposal id to submit and query + proposalCounter++ + submitGovFlags := []string{configFile(proposalCommunitySpendFilename)} + depositGovFlags := []string{strconv.Itoa(proposalCounter), depositAmount.String()} + voteGovFlags := []string{strconv.Itoa(proposalCounter), "yes"} + s.submitGovProposal(chainAAPIEndpoint, sender, proposalCounter, "CommunityPoolSpend", submitGovFlags, depositGovFlags, voteGovFlags, "vote") + + s.Require().Eventually( + func() bool { + afterRecipientBalance, err := getSpecificBalance(chainAAPIEndpoint, recipient, uomDenom) + s.Require().NoError(err) + + return afterRecipientBalance.Sub(sendAmount).IsEqual(beforeRecipientBalance) + }, + 10*time.Second, + 5*time.Second, + ) +} + +// NOTE: in SDK >= v0.47 the submit-proposal does not have a --deposit flag +// Instead, the depoist is added to the "deposit" field of the proposal JSON (usually stored as a file) +// you can use `gaiad tx gov draft-proposal` to create a proposal file that you can use +// min initial deposit of 100uom is required in e2e tests, otherwise the proposal would be dropped +func (s *IntegrationTestSuite) submitGovProposal(chainAAPIEndpoint, sender string, proposalID int, proposalType string, submitFlags []string, depositFlags []string, voteFlags []string, voteCommand string) { + s.T().Logf("Submitting Gov Proposal: %s", proposalType) + sflags := submitFlags + s.submitGovCommand(chainAAPIEndpoint, sender, proposalID, "submit-proposal", sflags, govtypesv1beta1.StatusDepositPeriod) + s.T().Logf("Depositing Gov Proposal: %s", proposalType) + s.submitGovCommand(chainAAPIEndpoint, sender, proposalID, "deposit", depositFlags, govtypesv1beta1.StatusVotingPeriod) + s.T().Logf("Voting Gov Proposal: %s", proposalType) + s.submitGovCommand(chainAAPIEndpoint, sender, proposalID, voteCommand, voteFlags, govtypesv1beta1.StatusPassed) +} + +func (s *IntegrationTestSuite) verifyChainHaltedAtUpgradeHeight(c *chain, valIdx, upgradeHeight int) { + s.Require().Eventually( + func() bool { + currentHeight := s.getLatestBlockHeight(c, valIdx) + + return currentHeight == upgradeHeight + }, + 30*time.Second, + 5*time.Second, + ) + + counter := 0 + s.Require().Eventually( + func() bool { + currentHeight := s.getLatestBlockHeight(c, valIdx) + + if currentHeight > upgradeHeight { + return false + } + if currentHeight == upgradeHeight { + counter++ + } + return counter >= 2 + }, + 8*time.Second, + 2*time.Second, + ) +} + +func (s *IntegrationTestSuite) verifyChainPassesUpgradeHeight(c *chain, valIdx, upgradeHeight int) { + s.Require().Eventually( + func() bool { + currentHeight := s.getLatestBlockHeight(c, valIdx) + + return currentHeight > upgradeHeight + }, + 30*time.Second, + 5*time.Second, + ) +} + +func (s *IntegrationTestSuite) submitGovCommand(chainAAPIEndpoint, sender string, proposalID int, govCommand string, proposalFlags []string, expectedSuccessStatus govtypesv1beta1.ProposalStatus) { + s.Run(fmt.Sprintf("Running tx gov %s", govCommand), func() { + s.runGovExec(s.chainA, 0, sender, govCommand, proposalFlags, standardFees.String(), nil) + + s.Require().Eventually( + func() bool { + proposal, err := queryGovProposal(chainAAPIEndpoint, proposalID) + s.Require().NoError(err) + return proposal.GetProposal().Status == expectedSuccessStatus + }, + 15*time.Second, + 5*time.Second, + ) + }) +} + +func (s *IntegrationTestSuite) submitGovCommandExpectingFailure(sender string, govCommand string, proposalFlags []string) { + s.Run(fmt.Sprintf("Running failing expedited tx gov %s -- expecting error", govCommand), func() { + // should return an error -- the Tx fails at the ante handler + s.runGovExec(s.chainA, 0, sender, govCommand, proposalFlags, standardFees.String(), s.expectTxSubmitError("unsupported expedited proposal type")) + }) +} + +// TODO: Add back in future when we have CCV module +// // testSetBlocksPerEpoch tests that we can change `BlocksPerEpoch` through a governance proposal +// func (s *IntegrationTestSuite) testSetBlocksPerEpoch() { +// chainEndpoint := fmt.Sprintf("http://%s", s.valResources[s.chainA.id][0].GetHostPort("1317/tcp")) + +// providerParams := providertypes.DefaultParams() + +// // assert that initially, the actual blocks per epoch are the default blocks per epoch +// s.Require().Eventually( +// func() bool { +// blocksPerEpoch, err := queryBlocksPerEpoch(chainEndpoint) +// s.T().Logf("Initial BlocksPerEpoch param: %v", blocksPerEpoch) +// s.Require().NoError(err) + +// s.Require().Equal(blocksPerEpoch, providerParams.BlocksPerEpoch) +// return true +// }, +// 15*time.Second, +// 5*time.Second, +// ) + +// // create a governance proposal to change blocks per epoch to the default blocks per epoch plus one +// expectedBlocksPerEpoch := providerParams.BlocksPerEpoch + 1 +// providerParams.BlocksPerEpoch = expectedBlocksPerEpoch +// paramsJSON := cdc.MustMarshalJSON(&providerParams) +// s.writeGovParamChangeProposalBlocksPerEpoch(s.chainA, string(paramsJSON)) + +// validatorAAddr, _ := s.chainA.validators[0].keyInfo.GetAddress() +// proposalCounter++ +// submitGovFlags := []string{configFile(proposalBlocksPerEpochFilename)} +// depositGovFlags := []string{strconv.Itoa(proposalCounter), depositAmount.String()} +// voteGovFlags := []string{strconv.Itoa(proposalCounter), "yes"} + +// s.T().Logf("Proposal number: %d", proposalCounter) +// s.T().Logf("Submitting, deposit and vote Gov Proposal: Change BlocksPerEpoch parameter") +// s.submitGovProposal(chainEndpoint, validatorAAddr.String(), proposalCounter, paramtypes.ProposalTypeChange, submitGovFlags, depositGovFlags, voteGovFlags, "vote") + +// s.Require().Eventually( +// func() bool { +// blocksPerEpoch, err := queryBlocksPerEpoch(chainEndpoint) +// s.Require().NoError(err) + +// s.T().Logf("Newly set blocks per epoch: %d", blocksPerEpoch) +// s.Require().Equal(expectedBlocksPerEpoch, blocksPerEpoch) +// return true +// }, +// 15*time.Second, +// 5*time.Second, +// ) +// } + +// MsgSoftwareUpgrade can be expedited but it can only be submitted using "tx gov submit-proposal" command. +// Messages submitted using "tx gov submit-legacy-proposal" command cannot be expedited.// submit but vote no so that the proposal is not passed +func (s *IntegrationTestSuite) GovSoftwareUpgradeExpedited() { + chainAAPIEndpoint := fmt.Sprintf("http://%s", s.valResources[s.chainA.id][0].GetHostPort("1317/tcp")) + senderAddress, _ := s.chainA.validators[0].keyInfo.GetAddress() + sender := senderAddress.String() + + proposalCounter++ + s.writeExpeditedSoftwareUpgradeProp(s.chainA) + submitGovFlags := []string{configFile(proposalExpeditedSoftwareUpgrade)} + + depositGovFlags := []string{strconv.Itoa(proposalCounter), depositAmount.String()} + voteGovFlags := []string{strconv.Itoa(proposalCounter), "yes=0.1,no=0.8,abstain=0.05,no_with_veto=0.05"} + + s.Run(fmt.Sprintf("Running expedited tx gov %s", "submit-proposal"), func() { + s.submitGovCommand(chainAAPIEndpoint, sender, proposalCounter, "submit-proposal", submitGovFlags, govtypesv1beta1.StatusDepositPeriod) + + s.Require().Eventually( + func() bool { + proposal, err := queryGovProposalV1(chainAAPIEndpoint, proposalCounter) + s.Require().NoError(err) + return proposal.Proposal.Expedited && proposal.GetProposal().Status == govtypesv1.ProposalStatus_PROPOSAL_STATUS_DEPOSIT_PERIOD + }, + 15*time.Second, + 5*time.Second, + ) + s.submitGovCommand(chainAAPIEndpoint, sender, proposalCounter, "deposit", depositGovFlags, govtypesv1beta1.StatusVotingPeriod) + s.submitGovCommand(chainAAPIEndpoint, sender, proposalCounter, "weighted-vote", voteGovFlags, govtypesv1beta1.StatusRejected) // voting no on prop + + // confirm that the proposal was moved from expedited + s.Require().Eventually( + func() bool { + proposal, err := queryGovProposalV1(chainAAPIEndpoint, proposalCounter) + s.Require().NoError(err) + return proposal.Proposal.Expedited == false + }, + 15*time.Second, + 5*time.Second, + ) + }) +} diff --git a/tests/e2e/e2e_ibc_test.go b/tests/e2e/e2e_ibc_test.go new file mode 100644 index 00000000..f9e22212 --- /dev/null +++ b/tests/e2e/e2e_ibc_test.go @@ -0,0 +1,379 @@ +package e2e + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type ForwardMetadata struct { + Receiver string `json:"receiver"` + Port string `json:"port"` + Channel string `json:"channel"` + // Timeout time.Duration `json:"timeout"` + // Retries *uint8 `json:"retries,omitempty"` + // Next *string `json:"next,omitempty"` + // RefundSequence *uint64 `json:"refund_sequence,omitempty"` +} + +type PacketMetadata struct { + Forward *ForwardMetadata `json:"forward"` +} + +//nolint:unparam +func (s *IntegrationTestSuite) sendIBC(c *chain, valIdx int, sender, recipient, token, fees, note string, expErr bool) { + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + ibcCmd := []string{ + mantrachaindBinary, + txCommand, + "ibc-transfer", + "transfer", + "transfer", + "channel-0", + recipient, + token, + fmt.Sprintf("--from=%s", sender), + fmt.Sprintf("--%s=%s", flags.FlagFees, fees), + fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), + // fmt.Sprintf("--%s=%s", flags.FlagNote, note), + fmt.Sprintf("--memo=%s", note), + "--keyring-backend=test", + "--broadcast-mode=sync", + "--output=json", + "-y", + } + s.T().Logf("sending %s from %s (%s) to %s (%s) with memo %s", token, s.chainA.id, sender, s.chainB.id, recipient, note) + if expErr { + s.executeTxCommand(ctx, c, ibcCmd, valIdx, s.expectErrExecValidation(c, valIdx, true)) + s.T().Log("unsuccessfully sent IBC tokens") + } else { + s.executeTxCommand(ctx, c, ibcCmd, valIdx, s.defaultExecValidation(c, valIdx)) + s.T().Log("successfully sent IBC tokens") + } +} + +func (s *IntegrationTestSuite) hermesClearPacket(configPath, chainID, portID, channelID string) (success bool) { //nolint:unparam + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + hermesCmd := []string{ + hermesBinary, + "--json", + fmt.Sprintf("--config=%s", configPath), + "clear", + "packets", + fmt.Sprintf("--chain=%s", chainID), + fmt.Sprintf("--channel=%s", channelID), + fmt.Sprintf("--port=%s", portID), + } + + if _, err := s.executeHermesCommand(ctx, hermesCmd); err != nil { + s.T().Logf("failed to clear packets: %s", err) + return false + } + + return true +} + +type RelayerPacketsOutput struct { + Result struct { + Dst struct { + UnreceivedPackets []uint64 `json:"unreceived_packets"` + } `json:"dst"` + Src struct { + UnreceivedPackets []uint64 `json:"unreceived_packets"` + } `json:"src"` + } `json:"result"` + Status string `json:"status"` +} + +func (s *IntegrationTestSuite) createConnection() { + s.T().Logf("connecting %s and %s chains via IBC", s.chainA.id, s.chainB.id) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + hermesCmd := []string{ + hermesBinary, + "--json", + "create", + "connection", + "--a-chain", + s.chainA.id, + "--b-chain", + s.chainB.id, + } + + _, err := s.executeHermesCommand(ctx, hermesCmd) + s.Require().NoError(err, "failed to connect chains: %s", err) + + s.T().Logf("connected %s and %s chains via IBC", s.chainA.id, s.chainB.id) +} + +func (s *IntegrationTestSuite) createChannel() { + s.T().Logf("creating IBC transfer channel created between chains %s and %s", s.chainA.id, s.chainB.id) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + hermesCmd := []string{ + hermesBinary, + "--json", + "create", + "channel", + "--a-chain", s.chainA.id, + "--a-connection", "connection-0", + "--a-port", "transfer", + "--b-port", "transfer", + "--channel-version", "ics20-1", + "--order", "unordered", + } + + _, err := s.executeHermesCommand(ctx, hermesCmd) + s.Require().NoError(err, "failed to create IBC transfer channel between chains: %s", err) + + s.T().Logf("IBC transfer channel created between chains %s and %s", s.chainA.id, s.chainB.id) +} + +func (s *IntegrationTestSuite) testIBCTokenTransfer() { + s.Run("send_uom_to_chainB", func() { + // require the recipient account receives the IBC tokens (IBC packets ACKd) + var ( + balances sdk.Coins + err error + beforeBalance int64 + ibcStakeDenom string + ) + + address, _ := s.chainA.validators[0].keyInfo.GetAddress() + sender := address.String() + + address, _ = s.chainB.validators[0].keyInfo.GetAddress() + recipient := address.String() + + chainBAPIEndpoint := fmt.Sprintf("http://%s", s.valResources[s.chainB.id][0].GetHostPort("1317/tcp")) + + s.Require().Eventually( + func() bool { + balances, err = queryAllBalances(chainBAPIEndpoint, recipient) + s.Require().NoError(err) + return balances.Len() != 0 + }, + time.Minute, + 5*time.Second, + ) + for _, c := range balances { + if strings.Contains(c.Denom, "ibc/") { + beforeBalance = c.Amount.Int64() + break + } + } + + tokenAmt := 3300000000 + s.sendIBC(s.chainA, 0, sender, recipient, strconv.Itoa(tokenAmt)+uomDenom, standardFees.String(), "", false) + + pass := s.hermesClearPacket(hermesConfigWithGasPrices, s.chainA.id, transferPort, transferChannel) + s.Require().True(pass) + + s.Require().Eventually( + func() bool { + balances, err = queryAllBalances(chainBAPIEndpoint, recipient) + s.Require().NoError(err) + return balances.Len() != 0 + }, + time.Minute, + 5*time.Second, + ) + for _, c := range balances { + if strings.Contains(c.Denom, "ibc/") { + ibcStakeDenom = c.Denom + s.Require().Equal((int64(tokenAmt) + beforeBalance), c.Amount.Int64()) + break + } + } + + s.Require().NotEmpty(ibcStakeDenom) + }) +} + +/* +TestMultihopIBCTokenTransfer tests that sending an IBC transfer using the IBC Packet Forward Middleware accepts a port, channel and account address + +Steps: +1. Check balance of Account 1 on Chain 1 +2. Check balance of Account 2 on Chain 1 +3. Account 1 on Chain 1 sends x tokens to Account 2 on Chain 1 via Account 1 on Chain 2 +4. Check Balance of Account 1 on Chain 1, confirm it is original minus x tokens +5. Check Balance of Account 2 on Chain 1, confirm it is original plus x tokens + +*/ +// TODO: Add back only if packet forward middleware has a working version compatible with IBC v3.0.x +func (s *IntegrationTestSuite) testMultihopIBCTokenTransfer() { + time.Sleep(30 * time.Second) + + s.Run("send_successful_multihop_uom_to_chainA_from_chainA", func() { + // require the recipient account receives the IBC tokens (IBC packets ACKd) + var ( + err error + ) + + address, _ := s.chainA.validators[0].keyInfo.GetAddress() + sender := address.String() + + address, _ = s.chainB.validators[0].keyInfo.GetAddress() + middlehop := address.String() + + address, _ = s.chainA.validators[1].keyInfo.GetAddress() + recipient := address.String() + + forwardPort := "transfer" + forwardChannel := "channel-0" + + tokenAmt := 3300000000 + + chainAAPIEndpoint := fmt.Sprintf("http://%s", s.valResources[s.chainA.id][0].GetHostPort("1317/tcp")) + + var ( + beforeSenderUOmBalance sdk.Coin + beforeRecipientUOmBalance sdk.Coin + ) + + s.Require().Eventually( + func() bool { + beforeSenderUOmBalance, err = getSpecificBalance(chainAAPIEndpoint, sender, uomDenom) + s.Require().NoError(err) + + beforeRecipientUOmBalance, err = getSpecificBalance(chainAAPIEndpoint, recipient, uomDenom) + s.Require().NoError(err) + + return beforeSenderUOmBalance.IsValid() && beforeRecipientUOmBalance.IsValid() + }, + 1*time.Minute, + 5*time.Second, + ) + + firstHopMetadata := &PacketMetadata{ + Forward: &ForwardMetadata{ + Receiver: recipient, + Channel: forwardChannel, + Port: forwardPort, + }, + } + + memo, err := json.Marshal(firstHopMetadata) + s.Require().NoError(err) + + s.sendIBC(s.chainA, 0, sender, middlehop, strconv.Itoa(tokenAmt)+uomDenom, standardFees.String(), string(memo), false) + + pass := s.hermesClearPacket(hermesConfigWithGasPrices, s.chainA.id, transferPort, transferChannel) + s.Require().True(pass) + + s.Require().Eventually( + func() bool { + afterSenderUOmBalance, err := getSpecificBalance(chainAAPIEndpoint, sender, uomDenom) + s.Require().NoError(err) + + afterRecipientUOmBalance, err := getSpecificBalance(chainAAPIEndpoint, recipient, uomDenom) + s.Require().NoError(err) + + decremented := beforeSenderUOmBalance.Sub(tokenAmount).Sub(standardFees).IsEqual(afterSenderUOmBalance) + incremented := beforeRecipientUOmBalance.Add(tokenAmount).IsEqual(afterRecipientUOmBalance) + + return decremented && incremented + }, + 1*time.Minute, + 5*time.Second, + ) + }) +} + +/* +TestFailedMultihopIBCTokenTransfer tests that sending a failing IBC transfer using the IBC Packet Forward +Middleware will send the tokens back to the original account after failing. +*/ +func (s *IntegrationTestSuite) testFailedMultihopIBCTokenTransfer() { + time.Sleep(30 * time.Second) + + s.Run("send_failed_multihop_uom_to_chainA_from_chainA", func() { + address, _ := s.chainA.validators[0].keyInfo.GetAddress() + sender := address.String() + + address, _ = s.chainB.validators[0].keyInfo.GetAddress() + middlehop := address.String() + + address, _ = s.chainA.validators[1].keyInfo.GetAddress() + recipient := strings.Replace(address.String(), "cosmos", "foobar", 1) // this should be an invalid recipient to force the tx to fail + + forwardPort := "transfer" + forwardChannel := "channel-0" + + tokenAmt := 3300000000 + + chainAAPIEndpoint := fmt.Sprintf("http://%s", s.valResources[s.chainA.id][0].GetHostPort("1317/tcp")) + + var ( + beforeSenderUOmBalance sdk.Coin + err error + ) + + s.Require().Eventually( + func() bool { + beforeSenderUOmBalance, err = getSpecificBalance(chainAAPIEndpoint, sender, uomDenom) + s.Require().NoError(err) + + return beforeSenderUOmBalance.IsValid() + }, + 1*time.Minute, + 5*time.Second, + ) + + firstHopMetadata := &PacketMetadata{ + Forward: &ForwardMetadata{ + Receiver: recipient, + Channel: forwardChannel, + Port: forwardPort, + }, + } + + memo, err := json.Marshal(firstHopMetadata) + s.Require().NoError(err) + + s.sendIBC(s.chainA, 0, sender, middlehop, strconv.Itoa(tokenAmt)+uomDenom, standardFees.String(), string(memo), false) + + // Sender account should be initially decremented the full amount + s.Require().Eventually( + func() bool { + afterSenderUOmBalance, err := getSpecificBalance(chainAAPIEndpoint, sender, uomDenom) + s.Require().NoError(err) + + returned := beforeSenderUOmBalance.Sub(tokenAmount).Sub(standardFees).IsEqual(afterSenderUOmBalance) + + return returned + }, + 1*time.Minute, + 5*time.Second, + ) + + // since the forward receiving account is invalid, it should be refunded to the original sender (minus the original fee) + s.Require().Eventually( + func() bool { + pass := s.hermesClearPacket(hermesConfigWithGasPrices, s.chainA.id, transferPort, transferChannel) + s.Require().True(pass) + + afterSenderUOmBalance, err := getSpecificBalance(chainAAPIEndpoint, sender, uomDenom) + s.Require().NoError(err) + returned := beforeSenderUOmBalance.Sub(standardFees).IsEqual(afterSenderUOmBalance) + return returned + }, + 5*time.Minute, + 10*time.Second, + ) + }) +} diff --git a/tests/e2e/e2e_rest_regression_test.go b/tests/e2e/e2e_rest_regression_test.go new file mode 100644 index 00000000..06301772 --- /dev/null +++ b/tests/e2e/e2e_rest_regression_test.go @@ -0,0 +1,90 @@ +package e2e + +import ( + "fmt" + "net/http" +) + +// /* +// RestRegression tests the continuity of critical endpoints that node operators, block explorers, and ecosystem participants depend on. +// Test Node REST Endpoints: +// 1. http://host:1317/validatorsets/latest +// 2. http://host:1317/validatorsets/{height} +// 3. http://host:1317/blocks/latest +// 4. http://host:1317/blocks/{height} +// 5. http://host:1317/syncing +// 6. http://host:1317/node_info +// 7. http://host:1317/txs +// Test Module REST Endpoints +// 1. Bank total +// 2. Auth params +// 3. Distribution for Community Pool +// 4. Evidence +// 5. Gov proposals +// 6. Mint params +// 7. Slashing params +// 8. Staking params +// */ +const ( + valSetLatestPath = "/cosmos/base/tendermint/v1beta1/validatorsets/latest" + valSetHeightPath = "/cosmos/base/tendermint/v1beta1/validatorsets/1" + blocksLatestPath = "/cosmos/base/tendermint/v1beta1/blocks/latest" + blocksHeightPath = "/cosmos/base/tendermint/v1beta1/blocks/1" + syncingPath = "/cosmos/base/tendermint/v1beta1/syncing" + nodeInfoPath = "/cosmos/base/tendermint/v1beta1/node_info" + transactionsPath = "/cosmos/tx/v1beta1/txs?query=tx.height=9999999999" + bankTotalModuleQueryPath = "/cosmos/bank/v1beta1/supply" + authParamsModuleQueryPath = "/cosmos/auth/v1beta1/params" + distributionCommPoolModuleQueryPath = "/cosmos/distribution/v1beta1/community_pool" + evidenceModuleQueryPath = "/cosmos/evidence/v1beta1/evidence" + govPropsModuleQueryPath = "/cosmos/gov/v1beta1/proposals" + mintParamsModuleQueryPath = "/cosmos/mint/v1beta1/params" + slashingParamsModuleQueryPath = "/cosmos/slashing/v1beta1/params" + stakingParamsModuleQueryPath = "/cosmos/staking/v1beta1/params" + missingPath = "/missing_endpoint" + localMinGasPriceQueryPath = "/cosmos/base/node/v1beta1/config" +) + +func (s *IntegrationTestSuite) testRestInterfaces() { + s.Run("test rest interfaces", func() { + var ( + valIdx = 0 + c = s.chainA + endpointURL = fmt.Sprintf("http://%s", s.valResources[c.id][valIdx].GetHostPort("1317/tcp")) + testEndpoints = []struct { + Path string + ExpectedStatus int + }{ + // Client Endpoints + {nodeInfoPath, 200}, + {syncingPath, 200}, + {valSetLatestPath, 200}, + {valSetHeightPath, 200}, + {blocksLatestPath, 200}, + {blocksHeightPath, 200}, + {transactionsPath, 200}, + // Module Endpoints + {bankTotalModuleQueryPath, 200}, + {authParamsModuleQueryPath, 200}, + {distributionCommPoolModuleQueryPath, 200}, + {evidenceModuleQueryPath, 200}, + {govPropsModuleQueryPath, 200}, + {mintParamsModuleQueryPath, 200}, + {slashingParamsModuleQueryPath, 200}, + {stakingParamsModuleQueryPath, 200}, + {missingPath, 501}, + {localMinGasPriceQueryPath, 200}, + } + ) + + for _, endpoint := range testEndpoints { + resp, err := http.Get(fmt.Sprintf("%s%s", endpointURL, endpoint.Path)) + s.NoError(err, fmt.Sprintf("failed to get endpoint: %s%s", endpointURL, endpoint.Path)) + + _, err = readJSON(resp) + s.NoError(err, fmt.Sprintf("failed to read body of endpoint: %s%s", endpointURL, endpoint.Path)) + + s.EqualValues(resp.StatusCode, endpoint.ExpectedStatus, fmt.Sprintf("invalid status from endpoint: : %s%s", endpointURL, endpoint.Path)) + } + }) +} diff --git a/tests/e2e/e2e_setup_test.go b/tests/e2e/e2e_setup_test.go new file mode 100644 index 00000000..04eab991 --- /dev/null +++ b/tests/e2e/e2e_setup_test.go @@ -0,0 +1,888 @@ +package e2e + +import ( + "context" + "encoding/json" + "fmt" + "math/rand" + "os" + "os/exec" + "path" + "path/filepath" + "strconv" + "strings" + "testing" + "time" + + "github.com/ory/dockertest/v3" + // "github.com/cosmos/cosmos-sdk/crypto/hd" + // "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/ory/dockertest/v3/docker" + "github.com/spf13/viper" + "github.com/stretchr/testify/suite" + + tmconfig "github.com/cometbft/cometbft/config" + "github.com/cometbft/cometbft/crypto/ed25519" + tmjson "github.com/cometbft/cometbft/libs/json" + rpchttp "github.com/cometbft/cometbft/rpc/client/http" + + "cosmossdk.io/math" + evidencetypes "cosmossdk.io/x/evidence/types" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/cosmos/cosmos-sdk/server" + srvconfig "github.com/cosmos/cosmos-sdk/server/config" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +const ( + mantrachaindBinary = "mantrachaind" + txCommand = "tx" + queryCommand = "query" + keysCommand = "keys" + mantraHomePath = "/home/nonroot/.mantrachain" + uomDenom = "uom" + initBalanceStr = "100000000000000000uom" + minGasPrice = "0.01" + // the test basefee in genesis is the same as minGasPrice + // global fee lower/higher than min_gas_price + initialBaseFeeAmt = "0.01" + gas = 200000 + govProposalBlockBuffer = 35 + relayerAccountIndexHermes = 0 + numberOfEvidences = 10 + slashingShares int64 = 10000 + + proposalMaxTotalBypassFilename = "proposal_max_total_bypass.json" + proposalCommunitySpendFilename = "proposal_community_spend.json" + proposalLSMParamUpdateFilename = "proposal_lsm_param_update.json" + proposalBlocksPerEpochFilename = "proposal_blocks_per_epoch.json" + proposalFailExpedited = "proposal_fail_expedited.json" + proposalExpeditedSoftwareUpgrade = "proposal_expedited_software_upgrade.json" + proposalSoftwareUpgrade = "proposal_software_upgrade.json" + proposalCancelSoftwareUpgrade = "proposal_cancel_software_upgrade.json" + + // proposalAddConsumerChainFilename = "proposal_add_consumer.json" + // proposalRemoveConsumerChainFilename = "proposal_remove_consumer.json" + + hermesBinary = "hermes" + hermesConfigWithGasPrices = "/root/.hermes/config.toml" + hermesConfigNoGasPrices = "/root/.hermes/config-zero.toml" + transferPort = "transfer" + transferChannel = "channel-0" + + govAuthority = "mantra10d07y265gmmuvt4z0w9aw880jnsr700j3fep4f" +) + +var ( + mantraConfigPath = filepath.Join(mantraHomePath, "config") + stakingAmount = math.NewInt(100000000000) + stakingAmountCoin = sdk.NewCoin(uomDenom, stakingAmount) + tokenAmount = sdk.NewCoin(uomDenom, math.NewInt(3300000000)) + standardFees = sdk.NewCoin(uomDenom, math.NewInt(330000)) + depositAmount = sdk.NewCoin(uomDenom, math.NewInt(330000000)) + distModuleAddress = authtypes.NewModuleAddress(distrtypes.ModuleName).String() + govModuleAddress = authtypes.NewModuleAddress(govtypes.ModuleName).String() + proposalCounter = 0 +) + +type IntegrationTestSuite struct { + suite.Suite + + tmpDirs []string + chainA *chain + chainB *chain + dkrPool *dockertest.Pool + dkrNet *dockertest.Network + hermesResource *dockertest.Resource + + valResources map[string][]*dockertest.Resource +} + +type AddressResponse struct { + Name string `json:"name"` + Type string `json:"type"` + Address string `json:"address"` + Mnemonic string `json:"mnemonic"` +} + +func TestIntegrationTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} + +func (s *IntegrationTestSuite) SetupSuite() { + s.T().Log("setting up e2e integration test suite...") + + var err error + s.chainA, err = newChain() + s.Require().NoError(err) + + s.chainB, err = newChain() + s.Require().NoError(err) + + s.dkrPool, err = dockertest.NewPool("") + s.Require().NoError(err) + + s.dkrNet, err = s.dkrPool.CreateNetwork(fmt.Sprintf("%s-%s-testnet", s.chainA.id, s.chainB.id)) + s.Require().NoError(err) + + s.valResources = make(map[string][]*dockertest.Resource) + + vestingMnemonic, err := createMnemonic() + s.Require().NoError(err) + + jailedValMnemonic, err := createMnemonic() + s.Require().NoError(err) + + // The bootstrapping phase is as follows: + // + // 1. Initialize mantra validator nodes. + // 2. Create and initialize mantra validator genesis files (both chains) + // 3. Start both networks. + // 4. Create and run IBC relayer (Hermes) containers. + + s.T().Logf("starting e2e infrastructure for chain A; chain-id: %s; datadir: %s", s.chainA.id, s.chainA.dataDir) + s.initNodes(s.chainA) + s.initGenesis(s.chainA, vestingMnemonic, jailedValMnemonic) + s.initValidatorConfigs(s.chainA) + s.runValidators(s.chainA, 0) + + // s.T().Logf("starting e2e infrastructure for chain B; chain-id: %s; datadir: %s", s.chainB.id, s.chainB.dataDir) + // s.initNodes(s.chainB) + // s.initGenesis(s.chainB, vestingMnemonic, jailedValMnemonic) + // s.initValidatorConfigs(s.chainB) + // s.runValidators(s.chainB, 10) + + // time.Sleep(10 * time.Second) + // s.runIBCRelayer() +} + +func (s *IntegrationTestSuite) TearDownSuite() { + if str := os.Getenv("mantra_E2E_SKIP_CLEANUP"); len(str) > 0 { + skipCleanup, err := strconv.ParseBool(str) + s.Require().NoError(err) + + if skipCleanup { + return + } + } + + s.T().Log("tearing down e2e integration test suite...") + + // s.Require().NoError(s.dkrPool.Purge(s.hermesResource)) + + for _, vr := range s.valResources { + for _, r := range vr { + s.Require().NoError(s.dkrPool.Purge(r)) + } + } + + s.Require().NoError(s.dkrPool.RemoveNetwork(s.dkrNet)) + + os.RemoveAll(s.chainA.dataDir) + os.RemoveAll(s.chainB.dataDir) + + for _, td := range s.tmpDirs { + os.RemoveAll(td) + } +} + +func (s *IntegrationTestSuite) initNodes(c *chain) { + s.Require().NoError(c.createAndInitValidators(2)) + /* Adding 4 accounts to val0 local directory + c.genesisAccounts[0]: Relayer Account + c.genesisAccounts[1]: ICA Owner + c.genesisAccounts[2]: Test Account 1 + c.genesisAccounts[3]: Test Account 2 + */ + s.Require().NoError(c.addAccountFromMnemonic(5)) + // Initialize a genesis file for the first validator + val0ConfigDir := c.validators[0].configDir() + var addrAll []sdk.AccAddress + for _, val := range c.validators { + addr, err := val.keyInfo.GetAddress() + s.Require().NoError(err) + addrAll = append(addrAll, addr) + } + + for _, addr := range c.genesisAccounts { + acctAddr, err := addr.keyInfo.GetAddress() + s.Require().NoError(err) + addrAll = append(addrAll, acctAddr) + } + + s.Require().NoError( + modifyGenesis(val0ConfigDir, "", initBalanceStr, addrAll, initialBaseFeeAmt, uomDenom), + ) + // copy the genesis file to the remaining validators + for _, val := range c.validators[1:] { + _, err := copyFile( + filepath.Join(val0ConfigDir, "config", "genesis.json"), + filepath.Join(val.configDir(), "config", "genesis.json"), + ) + s.Require().NoError(err) + } +} + +// TODO find a better way to manipulate accounts to add genesis accounts +func (s *IntegrationTestSuite) addGenesisVestingAndJailedAccounts( + c *chain, + valConfigDir, + vestingMnemonic, + jailedValMnemonic string, + appGenState map[string]json.RawMessage, +) map[string]json.RawMessage { + var ( + authGenState = authtypes.GetGenesisStateFromAppState(cdc, appGenState) + bankGenState = banktypes.GetGenesisStateFromAppState(cdc, appGenState) + stakingGenState = stakingtypes.GetGenesisStateFromAppState(cdc, appGenState) + ) + + // create genesis vesting accounts keys + kb, err := keyring.New(keyringAppName, keyring.BackendTest, valConfigDir, nil, cdc) + s.Require().NoError(err) + + keyringAlgos, _ := kb.SupportedAlgorithms() + algo, err := keyring.NewSigningAlgoFromString(string(hd.Secp256k1Type), keyringAlgos) + s.Require().NoError(err) + + // create jailed validator account keys + jailedValKey, err := kb.NewAccount(jailedValidatorKey, jailedValMnemonic, "", sdk.FullFundraiserPath, algo) + s.Require().NoError(err) + + // create genesis vesting accounts keys + c.genesisVestingAccounts = make(map[string]sdk.AccAddress) + for i, key := range genesisVestingKeys { + // Use the first wallet from the same mnemonic by HD path + acc, err := kb.NewAccount(key, vestingMnemonic, "", HDPath(i), algo) + s.Require().NoError(err) + c.genesisVestingAccounts[key], err = acc.GetAddress() + s.Require().NoError(err) + s.T().Logf("created %s genesis account %s\n", key, c.genesisVestingAccounts[key].String()) + } + var ( + continuousVestingAcc = c.genesisVestingAccounts[continuousVestingKey] + delayedVestingAcc = c.genesisVestingAccounts[delayedVestingKey] + ) + + // add jailed validator to staking store + pubKey, err := jailedValKey.GetPubKey() + s.Require().NoError(err) + + jailedValAcc, err := jailedValKey.GetAddress() + s.Require().NoError(err) + + jailedValAddr := sdk.ValAddress(jailedValAcc) + val, err := stakingtypes.NewValidator( + jailedValAddr.String(), + pubKey, + stakingtypes.NewDescription("jailed", "", "", "", ""), + ) + s.Require().NoError(err) + val.Jailed = true + val.Tokens = math.NewInt(slashingShares) + val.DelegatorShares = math.LegacyNewDec(slashingShares) + stakingGenState.Validators = append(stakingGenState.Validators, val) + + // add jailed validator delegations + stakingGenState.Delegations = append(stakingGenState.Delegations, stakingtypes.Delegation{ + DelegatorAddress: jailedValAcc.String(), + ValidatorAddress: jailedValAddr.String(), + Shares: math.LegacyNewDec(slashingShares), + }) + + appGenState[stakingtypes.ModuleName], err = cdc.MarshalJSON(stakingGenState) + s.Require().NoError(err) + + // add jailed account to the genesis + baseJailedAccount := authtypes.NewBaseAccount(jailedValAcc, pubKey, 0, 0) + s.Require().NoError(baseJailedAccount.Validate()) + + // add continuous vesting account to the genesis + baseVestingContinuousAccount := authtypes.NewBaseAccount( + continuousVestingAcc, nil, 0, 0) + baseVestingAcc, err := authvesting.NewBaseVestingAccount( + baseVestingContinuousAccount, + sdk.NewCoins(vestingAmountVested), + time.Now().Add(time.Duration(rand.Intn(80)+150)*time.Second).Unix(), + ) + s.Require().NoError(err) + vestingContinuousGenAccount := authvesting.NewContinuousVestingAccountRaw( + baseVestingAcc, + time.Now().Add(time.Duration(rand.Intn(40)+90)*time.Second).Unix(), + ) + s.Require().NoError(vestingContinuousGenAccount.Validate()) + + // add delayed vesting account to the genesis + baseVestingDelayedAccount := authtypes.NewBaseAccount( + delayedVestingAcc, nil, 0, 0) + baseVestingAcc, err = authvesting.NewBaseVestingAccount( + baseVestingDelayedAccount, + sdk.NewCoins(vestingAmountVested), + time.Now().Add(time.Duration(rand.Intn(40)+90)*time.Second).Unix(), + ) + s.Require().NoError(err) + vestingDelayedGenAccount := authvesting.NewDelayedVestingAccountRaw(baseVestingAcc) + s.Require().NoError(vestingDelayedGenAccount.Validate()) + + // unpack and append accounts + accs, err := authtypes.UnpackAccounts(authGenState.Accounts) + s.Require().NoError(err) + accs = append(accs, vestingContinuousGenAccount, vestingDelayedGenAccount, baseJailedAccount) + accs = authtypes.SanitizeGenesisAccounts(accs) + genAccs, err := authtypes.PackAccounts(accs) + s.Require().NoError(err) + authGenState.Accounts = genAccs + + // update auth module state + appGenState[authtypes.ModuleName], err = cdc.MarshalJSON(&authGenState) + s.Require().NoError(err) + + // update balances + vestingContinuousBalances := banktypes.Balance{ + Address: continuousVestingAcc.String(), + Coins: vestingBalance, + } + vestingDelayedBalances := banktypes.Balance{ + Address: delayedVestingAcc.String(), + Coins: vestingBalance, + } + jailedValidatorBalances := banktypes.Balance{ + Address: jailedValAcc.String(), + Coins: sdk.NewCoins(tokenAmount), + } + stakingModuleBalances := banktypes.Balance{ + Address: authtypes.NewModuleAddress(stakingtypes.NotBondedPoolName).String(), + Coins: sdk.NewCoins(sdk.NewCoin(uomDenom, math.NewInt(slashingShares))), + } + bankGenState.Balances = append( + bankGenState.Balances, + vestingContinuousBalances, + vestingDelayedBalances, + jailedValidatorBalances, + stakingModuleBalances, + ) + bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances) + + // update the denom metadata for the bank module + bankGenState.DenomMetadata = append(bankGenState.DenomMetadata, banktypes.Metadata{ + Description: "An example stable token", + Display: uomDenom, + Base: uomDenom, + Symbol: uomDenom, + Name: uomDenom, + DenomUnits: []*banktypes.DenomUnit{ + { + Denom: uomDenom, + Exponent: 0, + }, + }, + }) + + // update bank module state + appGenState[banktypes.ModuleName], err = cdc.MarshalJSON(bankGenState) + s.Require().NoError(err) + + return appGenState +} + +func (s *IntegrationTestSuite) initGenesis(c *chain, vestingMnemonic, jailedValMnemonic string) { + var ( + serverCtx = server.NewDefaultContext() + config = serverCtx.Config + validator = c.validators[0] + ) + + config.SetRoot(validator.configDir()) + config.Moniker = validator.moniker + + genFilePath := config.GenesisFile() + appGenState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFilePath) + s.Require().NoError(err) + + appGenState = s.addGenesisVestingAndJailedAccounts( + c, + validator.configDir(), + vestingMnemonic, + jailedValMnemonic, + appGenState, + ) + + var evidenceGenState evidencetypes.GenesisState + s.Require().NoError(cdc.UnmarshalJSON(appGenState[evidencetypes.ModuleName], &evidenceGenState)) + + evidenceGenState.Evidence = make([]*codectypes.Any, numberOfEvidences) + for i := range evidenceGenState.Evidence { + pk := ed25519.GenPrivKey() + evidence := &evidencetypes.Equivocation{ + Height: 1, + Power: 100, + Time: time.Now().UTC(), + ConsensusAddress: sdk.ConsAddress(pk.PubKey().Address().Bytes()).String(), + } + evidenceGenState.Evidence[i], err = codectypes.NewAnyWithValue(evidence) + s.Require().NoError(err) + } + + appGenState[evidencetypes.ModuleName], err = cdc.MarshalJSON(&evidenceGenState) + s.Require().NoError(err) + + var genUtilGenState genutiltypes.GenesisState + s.Require().NoError(cdc.UnmarshalJSON(appGenState[genutiltypes.ModuleName], &genUtilGenState)) + + // generate genesis txs + genTxs := make([]json.RawMessage, len(c.validators)) + for i, val := range c.validators { + createValmsg, err := val.buildCreateValidatorMsg(stakingAmountCoin) + s.Require().NoError(err) + signedTx, err := val.signMsg(createValmsg) + + s.Require().NoError(err) + + txRaw, err := cdc.MarshalJSON(signedTx) + s.Require().NoError(err) + + genTxs[i] = txRaw + } + + genUtilGenState.GenTxs = genTxs + + appGenState[genutiltypes.ModuleName], err = cdc.MarshalJSON(&genUtilGenState) + s.Require().NoError(err) + + genDoc.AppState, err = json.MarshalIndent(appGenState, "", " ") + s.Require().NoError(err) + + bz, err := tmjson.MarshalIndent(genDoc, "", " ") + s.Require().NoError(err) + + vestingPeriod, err := generateVestingPeriod() + s.Require().NoError(err) + + rawTx, _, err := buildRawTx() + s.Require().NoError(err) + + // write the updated genesis file to each validator. + for _, val := range c.validators { + err = writeFile(filepath.Join(val.configDir(), "config", "genesis.json"), bz) + s.Require().NoError(err) + + err = writeFile(filepath.Join(val.configDir(), vestingPeriodFile), vestingPeriod) + s.Require().NoError(err) + + err = writeFile(filepath.Join(val.configDir(), rawTxFile), rawTx) + s.Require().NoError(err) + } +} + +// initValidatorConfigs initializes the validator configs for the given chain. +func (s *IntegrationTestSuite) initValidatorConfigs(c *chain) { + for i, val := range c.validators { + tmCfgPath := filepath.Join(val.configDir(), "config", "config.toml") + + vpr := viper.New() + vpr.SetConfigFile(tmCfgPath) + s.Require().NoError(vpr.ReadInConfig()) + + valConfig := tmconfig.DefaultConfig() + + s.Require().NoError(vpr.Unmarshal(valConfig)) + + valConfig.P2P.ListenAddress = "tcp://0.0.0.0:26656" + valConfig.P2P.AddrBookStrict = false + valConfig.P2P.ExternalAddress = fmt.Sprintf("%s:%d", val.instanceName(), 26656) + valConfig.RPC.ListenAddress = "tcp://0.0.0.0:26657" + valConfig.StateSync.Enable = false + valConfig.LogLevel = "info" + + var peers []string + + for j := 0; j < len(c.validators); j++ { + if i == j { + continue + } + + peer := c.validators[j] + peerID := fmt.Sprintf("%s@%s%d:26656", peer.nodeKey.ID(), peer.moniker, j) + peers = append(peers, peerID) + } + + valConfig.P2P.PersistentPeers = strings.Join(peers, ",") + + tmconfig.WriteConfigFile(tmCfgPath, valConfig) + + // set application configuration + appCfgPath := filepath.Join(val.configDir(), "config", "app.toml") + + appConfig := srvconfig.DefaultConfig() + appConfig.API.Enable = true + appConfig.API.Address = "tcp://0.0.0.0:1317" + appConfig.MinGasPrices = fmt.Sprintf("%s%s", minGasPrice, uomDenom) + appConfig.GRPC.Address = "0.0.0.0:9090" + + srvconfig.SetConfigTemplate(srvconfig.DefaultConfigTemplate) + srvconfig.WriteConfigFile(appCfgPath, appConfig) + } +} + +// runValidators runs the validators in the chain +func (s *IntegrationTestSuite) runValidators(c *chain, portOffset int) { + s.T().Logf("starting mantra %s validator containers...", c.id) + + s.valResources[c.id] = make([]*dockertest.Resource, len(c.validators)) + for i, val := range c.validators { + runOpts := &dockertest.RunOptions{ + Name: val.instanceName(), + NetworkID: s.dkrNet.Network.ID, + Mounts: []string{ + fmt.Sprintf("%s/:%s", val.configDir(), mantraHomePath), + }, + Repository: "mantra-chain/mantrachain", + } + + s.Require().NoError(exec.Command("chmod", "-R", "0777", val.configDir()).Run()) //nolint:gosec // this is a test + + // expose the first validator for debugging and communication + if val.index == 0 { + runOpts.PortBindings = map[docker.Port][]docker.PortBinding{ + "1317/tcp": {{HostIP: "", HostPort: fmt.Sprintf("%d", 1317+portOffset)}}, + "6060/tcp": {{HostIP: "", HostPort: fmt.Sprintf("%d", 6060+portOffset)}}, + "6061/tcp": {{HostIP: "", HostPort: fmt.Sprintf("%d", 6061+portOffset)}}, + "6062/tcp": {{HostIP: "", HostPort: fmt.Sprintf("%d", 6062+portOffset)}}, + "6063/tcp": {{HostIP: "", HostPort: fmt.Sprintf("%d", 6063+portOffset)}}, + "6064/tcp": {{HostIP: "", HostPort: fmt.Sprintf("%d", 6064+portOffset)}}, + "6065/tcp": {{HostIP: "", HostPort: fmt.Sprintf("%d", 6065+portOffset)}}, + "9090/tcp": {{HostIP: "", HostPort: fmt.Sprintf("%d", 9090+portOffset)}}, + "26656/tcp": {{HostIP: "", HostPort: fmt.Sprintf("%d", 26656+portOffset)}}, + "26657/tcp": {{HostIP: "", HostPort: fmt.Sprintf("%d", 26657+portOffset)}}, + } + } + + resource, err := s.dkrPool.RunWithOptions(runOpts, noRestart) + + s.Require().NoError(err) + + s.valResources[c.id][i] = resource + s.T().Logf("started mantra %s validator container: %s", c.id, resource.Container.ID) + } + + rpcClient, err := rpchttp.New("tcp://localhost:26657", "/websocket") + s.Require().NoError(err) + + s.Require().Eventually( + func() bool { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + status, err := rpcClient.Status(ctx) + if err != nil { + return false + } + + // let the node produce a few blocks + if status.SyncInfo.CatchingUp || status.SyncInfo.LatestBlockHeight < 3 { + return false + } + return true + }, + 5*time.Minute, + time.Second, + "mantra node failed to produce blocks", + ) +} + +func noRestart(config *docker.HostConfig) { + // in this case we don't want the nodes to restart on failure + config.RestartPolicy = docker.RestartPolicy{ + Name: "no", + } +} + +// runIBCRelayer bootstraps an IBC Hermes relayer by creating an IBC connection and +// a transfer channel between chainA and chainB. +func (s *IntegrationTestSuite) runIBCRelayer() { + s.T().Log("starting Hermes relayer container") + + tmpDir, err := os.MkdirTemp("", "mantra-e2e-testnet-hermes-") + s.Require().NoError(err) + s.tmpDirs = append(s.tmpDirs, tmpDir) + + mantraAVal := s.chainA.validators[0] + mantraBVal := s.chainB.validators[0] + + mantraARly := s.chainA.genesisAccounts[relayerAccountIndexHermes] + mantraBRly := s.chainB.genesisAccounts[relayerAccountIndexHermes] + + hermesCfgPath := path.Join(tmpDir, "hermes") + + s.Require().NoError(os.MkdirAll(hermesCfgPath, 0o755)) + _, err = copyFile( + filepath.Join("./scripts/", "hermes_bootstrap.sh"), + filepath.Join(hermesCfgPath, "hermes_bootstrap.sh"), + ) + s.Require().NoError(err) + + s.hermesResource, err = s.dkrPool.RunWithOptions( + &dockertest.RunOptions{ + Name: fmt.Sprintf("%s-%s-relayer", s.chainA.id, s.chainB.id), + Repository: "ghcr.io/cosmos/hermes-e2e", + Tag: "1.0.0", + NetworkID: s.dkrNet.Network.ID, + Mounts: []string{ + fmt.Sprintf("%s/:/root/hermes", hermesCfgPath), + }, + PortBindings: map[docker.Port][]docker.PortBinding{ + "3031/tcp": {{HostIP: "", HostPort: "3031"}}, + }, + Env: []string{ + fmt.Sprintf("MANTRA_A_E2E_CHAIN_ID=%s", s.chainA.id), + fmt.Sprintf("MANTRA_B_E2E_CHAIN_ID=%s", s.chainB.id), + fmt.Sprintf("MANTRA_A_E2E_VAL_MNEMONIC=%s", mantraAVal.mnemonic), + fmt.Sprintf("MANTRA_B_E2E_VAL_MNEMONIC=%s", mantraBVal.mnemonic), + fmt.Sprintf("MANTRA_A_E2E_RLY_MNEMONIC=%s", mantraARly.mnemonic), + fmt.Sprintf("MANTRA_B_E2E_RLY_MNEMONIC=%s", mantraBRly.mnemonic), + fmt.Sprintf("MANTRA_A_E2E_VAL_HOST=%s", s.valResources[s.chainA.id][0].Container.Name[1:]), + fmt.Sprintf("MANTRA_B_E2E_VAL_HOST=%s", s.valResources[s.chainB.id][0].Container.Name[1:]), + }, + User: "root", + Entrypoint: []string{ + "sh", + "-c", + "chmod +x /root/hermes/hermes_bootstrap.sh && /root/hermes/hermes_bootstrap.sh && tail -f /dev/null", + }, + }, + noRestart, + ) + s.Require().NoError(err) + + s.T().Logf("started Hermes relayer container: %s", s.hermesResource.Container.ID) + + // XXX: Give time to both networks to start, otherwise we might see gRPC + // transport errors. + time.Sleep(10 * time.Second) + + // create the client, connection and channel between the two mantra chains + s.createConnection() + s.createChannel() +} + +func (s *IntegrationTestSuite) writeGovCommunitySpendProposal(c *chain, amount sdk.Coin, recipient string) { + template := ` + { + "messages":[ + { + "@type": "/cosmos.distribution.v1beta1.MsgCommunityPoolSpend", + "authority": "%s", + "recipient": "%s", + "amount": [{ + "denom": "%s", + "amount": "%s" + }] + } + ], + "deposit": "100uom", + "proposer": "Proposing validator address", + "metadata": "Community Pool Spend", + "title": "Fund Team!", + "summary": "summary", + "expedited": false + } + ` + propMsgBody := fmt.Sprintf(template, govModuleAddress, recipient, amount.Denom, amount.Amount.String()) + err := writeFile(filepath.Join(c.validators[0].configDir(), "config", proposalCommunitySpendFilename), []byte(propMsgBody)) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) writeSoftwareUpgradeProposal(c *chain, height int64, name string) { + body := `{ + "messages": [ + { + "@type": "/cosmos.upgrade.v1beta1.MsgSoftwareUpgrade", + "authority": "mantra10d07y265gmmuvt4z0w9aw880jnsr700j3fep4f", + "plan": { + "name": "%s", + "height": "%d", + "info": "test", + "upgraded_client_state": null + } + } + ], + "metadata": "ipfs://CID", + "deposit": "100uom", + "title": "title", + "summary": "test" + }` + + propMsgBody := fmt.Sprintf(body, name, height) + + err := writeFile(filepath.Join(c.validators[0].configDir(), "config", proposalSoftwareUpgrade), []byte(propMsgBody)) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) writeCancelSoftwareUpgradeProposal(c *chain) { + template := `{ + "messages": [ + { + "@type": "/cosmos.upgrade.v1beta1.MsgCancelUpgrade", + "authority": "mantra10d07y265gmmuvt4z0w9aw880jnsr700j3fep4f" + } + ], + "metadata": "ipfs://CID", + "deposit": "100uom", + "title": "title", + "summary": "test" + }` + + err := writeFile(filepath.Join(c.validators[0].configDir(), "config", proposalCancelSoftwareUpgrade), []byte(template)) + s.Require().NoError(err) +} + +func (s *IntegrationTestSuite) writeLiquidStakingParamsUpdateProposal(c *chain, oldParams stakingtypes.Params) { + template := ` + { + "messages": [ + { + "@type": "/cosmos.staking.v1beta1.MsgUpdateParams", + "authority": "%s", + "params": { + "unbonding_time": "%s", + "max_validators": %d, + "max_entries": %d, + "historical_entries": %d, + "bond_denom": "%s", + "min_commission_rate": "%s", + "validator_bond_factor": "%s", + "global_liquid_staking_cap": "%s", + "validator_liquid_staking_cap": "%s" + } + } + ], + "metadata": "ipfs://CID", + "deposit": "100uom", + "title": "Update LSM Params", + "summary": "e2e-test updating LSM staking params", + "expedited": false + }` + propMsgBody := fmt.Sprintf(template, + govAuthority, + oldParams.UnbondingTime, + oldParams.MaxValidators, + oldParams.MaxEntries, + oldParams.HistoricalEntries, + oldParams.BondDenom, + oldParams.MinCommissionRate, + math.LegacyNewDec(250), // validator bond factor + math.LegacyNewDecWithPrec(25, 2), // 25 global_liquid_staking_cap + math.LegacyNewDecWithPrec(50, 2), // 50 validator_liquid_staking_cap + ) + + err := writeFile(filepath.Join(c.validators[0].configDir(), "config", proposalLSMParamUpdateFilename), []byte(propMsgBody)) + s.Require().NoError(err) +} + +// writeGovParamChangeProposalBlocksPerEpoch writes a governance proposal JSON file to change the `BlocksPerEpoch` +// parameter to the provided `blocksPerEpoch` +func (s *IntegrationTestSuite) writeGovParamChangeProposalBlocksPerEpoch(c *chain, paramsJSON string) { + template := ` + { + "messages":[ + { + "@type": "/interchain_security.ccv.provider.v1.MsgUpdateParams", + "authority": "%s", + "params": %s + } + ], + "deposit": "100uom", + "proposer": "sample proposer", + "metadata": "sample metadata", + "title": "blocks per epoch title", + "summary": "blocks per epoch summary", + "expedited": false + }` + + propMsgBody := fmt.Sprintf(template, + govAuthority, + paramsJSON, + ) + + err := writeFile(filepath.Join(c.validators[0].configDir(), "config", proposalBlocksPerEpochFilename), []byte(propMsgBody)) + s.Require().NoError(err) +} + +// writeFailingExpeditedProposal writes a governance proposal JSON file. +// The proposal fails because only SoftwareUpgrade and CancelSoftwareUpgrade can be expedited. +func (s *IntegrationTestSuite) writeFailingExpeditedProposal(c *chain, blocksPerEpoch int64) { + template := ` + { + "messages":[ + { + "@type": "/cosmos.gov.v1.MsgExecLegacyContent", + "authority": "%s", + "content": { + "@type": "/cosmos.params.v1beta1.ParameterChangeProposal", + "title": "BlocksPerEpoch", + "description": "change blocks per epoch", + "changes": [{ + "subspace": "provider", + "key": "BlocksPerEpoch", + "value": "\"%d\"" + }] + } + } + ], + "deposit": "100uom", + "proposer": "sample proposer", + "metadata": "sample metadata", + "title": "blocks per epoch title", + "summary": "blocks per epoch summary", + "expedited": true + }` + + propMsgBody := fmt.Sprintf(template, + govAuthority, + blocksPerEpoch, + ) + + err := writeFile(filepath.Join(c.validators[0].configDir(), "config", proposalFailExpedited), []byte(propMsgBody)) + s.Require().NoError(err) +} + +// MsgSoftwareUpgrade can be expedited and it can only be submitted using "tx gov submit-proposal" command. +func (s *IntegrationTestSuite) writeExpeditedSoftwareUpgradeProp(c *chain) { + body := `{ + "messages": [ + { + "@type": "/cosmos.upgrade.v1beta1.MsgSoftwareUpgrade", + "authority": "mantra10d07y265gmmuvt4z0w9aw880jnsr700j3fep4f", + "plan": { + "name": "test-expedited-upgrade", + "height": "123456789", + "info": "test", + "upgraded_client_state": null + } + } + ], + "metadata": "ipfs://CID", + "deposit": "100uom", + "title": "title", + "summary": "test", + "expedited": true +}` + + err := writeFile(filepath.Join(c.validators[0].configDir(), "config", proposalExpeditedSoftwareUpgrade), []byte(body)) + s.Require().NoError(err) +} + +func configFile(filename string) string { + filepath := filepath.Join(mantraConfigPath, filename) + return filepath +} diff --git a/tests/e2e/e2e_slashing_test.go b/tests/e2e/e2e_slashing_test.go new file mode 100644 index 00000000..42b8883c --- /dev/null +++ b/tests/e2e/e2e_slashing_test.go @@ -0,0 +1,23 @@ +package e2e + +const jailedValidatorKey = "jailed" + +func (s *IntegrationTestSuite) testSlashing(chainEndpoint string) { + s.Run("test unjail validator", func() { + validators, err := queryValidators(chainEndpoint) + s.Require().NoError(err) + + for _, val := range validators.Validators { + if val.Jailed { + s.execUnjail( + s.chainA, + withKeyValue(flagFrom, jailedValidatorKey), + ) + + valQ, err := queryValidator(chainEndpoint, val.OperatorAddress) + s.Require().NoError(err) + s.Require().False(valQ.Jailed) + } + } + }) +} diff --git a/tests/e2e/e2e_staking_test.go b/tests/e2e/e2e_staking_test.go new file mode 100644 index 00000000..782a8f14 --- /dev/null +++ b/tests/e2e/e2e_staking_test.go @@ -0,0 +1,143 @@ +package e2e + +import ( + "fmt" + "strconv" + "time" + + "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func (s *IntegrationTestSuite) testStaking() { + chainEndpoint := fmt.Sprintf("http://%s", s.valResources[s.chainA.id][0].GetHostPort("1317/tcp")) + + validatorA := s.chainA.validators[0] + validatorB := s.chainA.validators[1] + validatorAAddr, _ := validatorA.keyInfo.GetAddress() + validatorBAddr, _ := validatorB.keyInfo.GetAddress() + + validatorAddressA := sdk.ValAddress(validatorAAddr).String() + validatorAddressB := sdk.ValAddress(validatorBAddr).String() + + delegatorAddress, _ := s.chainA.genesisAccounts[2].keyInfo.GetAddress() + + fees := sdk.NewCoin(uomDenom, math.NewInt(1)) + + existingDelegation := math.LegacyZeroDec() + res, err := queryDelegation(chainEndpoint, validatorAddressA, delegatorAddress.String()) + if err == nil { + existingDelegation = res.GetDelegationResponse().GetDelegation().GetShares() + } + + delegationAmount := math.NewInt(500000000) + delegation := sdk.NewCoin(uomDenom, delegationAmount) // 500 om + + // Alice delegate uom to Validator A + s.execDelegate(s.chainA, 0, delegation.String(), validatorAddressA, delegatorAddress.String(), mantraHomePath, fees.String()) + + // Validate delegation successful + s.Require().Eventually( + func() bool { + res, err := queryDelegation(chainEndpoint, validatorAddressA, delegatorAddress.String()) + amt := res.GetDelegationResponse().GetDelegation().GetShares() + s.Require().NoError(err) + + return amt.Equal(existingDelegation.Add(math.LegacyNewDecFromInt(delegationAmount))) + }, + 20*time.Second, + 5*time.Second, + ) + + redelegationAmount := delegationAmount.Quo(math.NewInt(2)) + redelegation := sdk.NewCoin(uomDenom, redelegationAmount) // 250 om + + // Alice re-delegate half of her uom delegation from Validator A to Validator B + s.execRedelegate(s.chainA, 0, redelegation.String(), validatorAddressA, validatorAddressB, delegatorAddress.String(), mantraHomePath, fees.String()) + + // Validate re-delegation successful + s.Require().Eventually( + func() bool { + res, err := queryDelegation(chainEndpoint, validatorAddressB, delegatorAddress.String()) + amt := res.GetDelegationResponse().GetDelegation().GetShares() + s.Require().NoError(err) + + return amt.Equal(math.LegacyNewDecFromInt(redelegationAmount)) + }, + 20*time.Second, + 5*time.Second, + ) + + var ( + currDelegation sdk.Coin + currDelegationAmount math.Int + ) + + // query alice's current delegation from validator A + s.Require().Eventually( + func() bool { + res, err := queryDelegation(chainEndpoint, validatorAddressA, delegatorAddress.String()) + amt := res.GetDelegationResponse().GetDelegation().GetShares() + s.Require().NoError(err) + + currDelegationAmount = amt.TruncateInt() + currDelegation = sdk.NewCoin(uomDenom, currDelegationAmount) + + return currDelegation.IsValid() + }, + 20*time.Second, + 5*time.Second, + ) + + // Alice unbonds all her uom delegation from Validator A + s.execUnbondDelegation(s.chainA, 0, currDelegation.String(), validatorAddressA, delegatorAddress.String(), mantraHomePath, fees.String()) + + var ubdDelegationEntry types.UnbondingDelegationEntry + + // validate unbonding delegations + s.Require().Eventually( + func() bool { + res, err := queryUnbondingDelegation(chainEndpoint, validatorAddressA, delegatorAddress.String()) + s.Require().NoError(err) + + s.Require().Len(res.GetUnbond().Entries, 1) + ubdDelegationEntry = res.GetUnbond().Entries[0] + + return ubdDelegationEntry.Balance.Equal(currDelegationAmount) + }, + 20*time.Second, + 5*time.Second, + ) + + // cancel the full amount of unbonding delegations from Validator A + s.execCancelUnbondingDelegation( + s.chainA, + 0, + currDelegation.String(), + validatorAddressA, + strconv.Itoa(int(ubdDelegationEntry.CreationHeight)), + delegatorAddress.String(), + mantraHomePath, + fees.String(), + ) + + // validate that unbonding delegation was successfully canceled + s.Require().Eventually( + func() bool { + resDel, err := queryDelegation(chainEndpoint, validatorAddressA, delegatorAddress.String()) + amt := resDel.GetDelegationResponse().GetDelegation().GetShares() + s.Require().NoError(err) + + // expect that no unbonding delegations are found for validator A + _, err = queryUnbondingDelegation(chainEndpoint, validatorAddressA, delegatorAddress.String()) + s.Require().Error(err) + + // expect to get the delegation back + return amt.Equal(math.LegacyNewDecFromInt(currDelegationAmount)) + }, + 20*time.Second, + 5*time.Second, + ) +} diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go new file mode 100644 index 00000000..7f13970a --- /dev/null +++ b/tests/e2e/e2e_test.go @@ -0,0 +1,108 @@ +package e2e + +import "fmt" + +var ( + runBankTest = true + runEncodeTest = true + runEvidenceTest = true + runGovTest = true + runIBCTest = false // TODO: enable after IBC test is fixed + runSlashingTest = true + runStakingAndDistributionTest = true + runVestingTest = true + runRestInterfacesTest = true + runRateLimitTest = false // TODO: enable after IBC is fixed +) + +func (s *IntegrationTestSuite) TestRestInterfaces() { + if !runRestInterfacesTest { + s.T().Skip() + } + s.testRestInterfaces() +} + +func (s *IntegrationTestSuite) TestBank() { + if !runBankTest { + s.T().Skip() + } + s.testBankTokenTransfer() +} + +func (s *IntegrationTestSuite) TestEncode() { + if !runEncodeTest { + s.T().Skip() + } + s.testEncode() + s.testDecode() +} + +func (s *IntegrationTestSuite) TestEvidence() { + if !runEvidenceTest { + s.T().Skip() + } + s.testEvidence() +} + +func (s *IntegrationTestSuite) TestGov() { + if !runGovTest { + s.T().Skip() + } + + s.GovCancelSoftwareUpgrade() + s.GovCommunityPoolSpend() + + // TODO: add back when CCV is enabled + // s.testSetBlocksPerEpoch() + // s.ExpeditedProposalRejected() + s.GovSoftwareUpgradeExpedited() +} + +func (s *IntegrationTestSuite) TestIBC() { + if !runIBCTest { + s.T().Skip() + } + + s.testIBCTokenTransfer() + s.testMultihopIBCTokenTransfer() + s.testFailedMultihopIBCTokenTransfer() +} + +func (s *IntegrationTestSuite) TestSlashing() { + if !runSlashingTest { + s.T().Skip() + } + chainAPI := fmt.Sprintf("http://%s", s.valResources[s.chainA.id][0].GetHostPort("1317/tcp")) + s.testSlashing(chainAPI) +} + +// todo add fee test with wrong denom order +func (s *IntegrationTestSuite) TestStakingAndDistribution() { + if !runStakingAndDistributionTest { + s.T().Skip() + } + s.testStaking() + s.testDistribution() +} + +func (s *IntegrationTestSuite) TestVesting() { + if !runVestingTest { + s.T().Skip() + } + chainAAPI := fmt.Sprintf("http://%s", s.valResources[s.chainA.id][0].GetHostPort("1317/tcp")) + s.testDelayedVestingAccount(chainAAPI) + s.testContinuousVestingAccount(chainAAPI) + s.testPeriodicVestingAccount(chainAAPI) +} + +// func (s *IntegrationTestSuite) TestRateLimit() { +// if !runRateLimitTest { +// s.T().Skip() +// } +// s.testAddRateLimits() +// s.testIBCTransfer(true) +// s.testUpdateRateLimit() +// s.testIBCTransfer(false) +// s.testResetRateLimit() +// s.testRemoveRateLimit() +// } diff --git a/tests/e2e/e2e_vesting_test.go b/tests/e2e/e2e_vesting_test.go new file mode 100644 index 00000000..11427cb3 --- /dev/null +++ b/tests/e2e/e2e_vesting_test.go @@ -0,0 +1,338 @@ +package e2e + +import ( + "encoding/json" + "math/rand" + "path/filepath" + "time" + + "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + delayedVestingKey = "delayed_vesting" + continuousVestingKey = "continuous_vesting" + lockedVestingKey = "locker_vesting" + periodicVestingKey = "periodic_vesting" + + vestingPeriodFile = "test_period.json" + vestingTxDelay = 5 +) + +type ( + vestingPeriod struct { + StartTime int64 `json:"start_time"` + Periods []period `json:"periods"` + } + period struct { + Coins string `json:"coins"` + Length int64 `json:"length_seconds"` + } +) + +var ( + genesisVestingKeys = []string{continuousVestingKey, delayedVestingKey, lockedVestingKey, periodicVestingKey} + vestingAmountVested = sdk.NewCoin(uomDenom, math.NewInt(99900000000)) + vestingAmount = sdk.NewCoin(uomDenom, math.NewInt(350000)) + vestingBalance = sdk.NewCoins(vestingAmountVested).Add(vestingAmount) + vestingDelegationAmount = sdk.NewCoin(uomDenom, math.NewInt(500000000)) + vestingDelegationFees = sdk.NewCoin(uomDenom, math.NewInt(1)) +) + +func (s *IntegrationTestSuite) testDelayedVestingAccount(api string) { + var ( + valIdx = 0 + chain = s.chainA + val = chain.validators[valIdx] + vestingDelayedAcc = chain.genesisVestingAccounts[delayedVestingKey] + ) + sender, _ := val.keyInfo.GetAddress() + valOpAddr := sdk.ValAddress(sender).String() + + s.Run("test delayed vesting genesis account", func() { + acc, err := queryDelayedVestingAccount(api, vestingDelayedAcc.String()) + s.Require().NoError(err) + + // Check address balance + balance, err := getSpecificBalance(api, vestingDelayedAcc.String(), uomDenom) + s.Require().NoError(err) + s.Require().Equal(vestingBalance.AmountOf(uomDenom), balance.Amount) + + // Delegate coins should succeed + s.execDelegate(chain, valIdx, vestingDelegationAmount.String(), valOpAddr, + vestingDelayedAcc.String(), mantraHomePath, vestingDelegationFees.String()) + + // Validate delegation successful + s.Require().Eventually( + func() bool { + res, err := queryDelegation(api, valOpAddr, vestingDelayedAcc.String()) + amt := res.GetDelegationResponse().GetDelegation().GetShares() + s.Require().NoError(err) + + return amt.Equal(math.LegacyNewDecFromInt(vestingDelegationAmount.Amount)) + }, + 20*time.Second, + 5*time.Second, + ) + + waitTime := acc.EndTime - time.Now().Unix() + if waitTime > vestingTxDelay { + // Transfer coins should fail + balance, err := getSpecificBalance(api, vestingDelayedAcc.String(), uomDenom) + s.Require().NoError(err) + s.execBankSend( + chain, + valIdx, + vestingDelayedAcc.String(), + Address(), + balance.Sub(standardFees).String(), + standardFees.String(), + true, + ) + waitTime = acc.EndTime - time.Now().Unix() + vestingTxDelay + time.Sleep(time.Duration(waitTime) * time.Second) + } + + // Transfer coins should succeed + balance, err = getSpecificBalance(api, vestingDelayedAcc.String(), uomDenom) + s.Require().NoError(err) + s.execBankSend( + chain, + valIdx, + vestingDelayedAcc.String(), + Address(), + balance.Sub(standardFees).String(), + standardFees.String(), + false, + ) + }) +} + +func (s *IntegrationTestSuite) testContinuousVestingAccount(api string) { + s.Run("test continuous vesting genesis account", func() { + var ( + valIdx = 0 + chain = s.chainA + val = chain.validators[valIdx] + continuousVestingAcc = chain.genesisVestingAccounts[continuousVestingKey] + ) + sender, _ := val.keyInfo.GetAddress() + valOpAddr := sdk.ValAddress(sender).String() + + acc, err := queryContinuousVestingAccount(api, continuousVestingAcc.String()) + s.Require().NoError(err) + + // Check address balance + balance, err := getSpecificBalance(api, continuousVestingAcc.String(), uomDenom) + s.Require().NoError(err) + s.Require().Equal(vestingBalance.AmountOf(uomDenom), balance.Amount) + + // Delegate coins should succeed + s.execDelegate(chain, valIdx, vestingDelegationAmount.String(), + valOpAddr, continuousVestingAcc.String(), mantraHomePath, vestingDelegationFees.String()) + + // Validate delegation successful + s.Require().Eventually( + func() bool { + res, err := queryDelegation(api, valOpAddr, continuousVestingAcc.String()) + amt := res.GetDelegationResponse().GetDelegation().GetShares() + s.Require().NoError(err) + + return amt.Equal(math.LegacyNewDecFromInt(vestingDelegationAmount.Amount)) + }, + 20*time.Second, + 5*time.Second, + ) + + waitStartTime := acc.StartTime - time.Now().Unix() + if waitStartTime > vestingTxDelay { + // Transfer coins should fail + balance, err := getSpecificBalance(api, continuousVestingAcc.String(), uomDenom) + s.Require().NoError(err) + s.execBankSend( + chain, + valIdx, + continuousVestingAcc.String(), + Address(), + balance.Sub(standardFees).String(), + standardFees.String(), + true, + ) + waitStartTime = acc.StartTime - time.Now().Unix() + vestingTxDelay + time.Sleep(time.Duration(waitStartTime) * time.Second) + } + + waitEndTime := acc.EndTime - time.Now().Unix() + if waitEndTime > vestingTxDelay { + // Transfer coins should fail + balance, err := getSpecificBalance(api, continuousVestingAcc.String(), uomDenom) + s.Require().NoError(err) + s.execBankSend( + chain, + valIdx, + continuousVestingAcc.String(), + Address(), + balance.Sub(standardFees).String(), + standardFees.String(), + true, + ) + waitEndTime = acc.EndTime - time.Now().Unix() + vestingTxDelay + time.Sleep(time.Duration(waitEndTime) * time.Second) + } + + // Transfer coins should succeed + balance, err = getSpecificBalance(api, continuousVestingAcc.String(), uomDenom) + s.Require().NoError(err) + s.execBankSend( + chain, + valIdx, + continuousVestingAcc.String(), + Address(), + balance.Sub(standardFees).String(), + standardFees.String(), + false, + ) + }) +} + +func (s *IntegrationTestSuite) testPeriodicVestingAccount(api string) { //nolint:unused + + s.Run("test periodic vesting genesis account", func() { + var ( + valIdx = 0 + chain = s.chainA + val = chain.validators[valIdx] + periodicVestingAddr = chain.genesisVestingAccounts[periodicVestingKey].String() + ) + sender, _ := val.keyInfo.GetAddress() + valOpAddr := sdk.ValAddress(sender).String() + + s.execCreatePeriodicVestingAccount( + chain, + periodicVestingAddr, + filepath.Join(mantraHomePath, vestingPeriodFile), + withKeyValue(flagFrom, sender.String()), + ) + + acc, err := queryPeriodicVestingAccount(api, periodicVestingAddr) + s.Require().NoError(err) + + // Check address balance + balance, err := getSpecificBalance(api, periodicVestingAddr, uomDenom) + s.Require().NoError(err) + + expectedBalance := sdk.NewCoin(uomDenom, math.NewInt(0)) + for _, period := range acc.VestingPeriods { + // _, coin := ante.Find(period.Amount, uomDenom) + _, coin := period.Amount.Find(uomDenom) + expectedBalance = expectedBalance.Add(coin) + } + s.Require().Equal(expectedBalance, balance) + + waitStartTime := acc.StartTime - time.Now().Unix() + if waitStartTime > vestingTxDelay { + // Transfer coins should fail + balance, err = getSpecificBalance(api, periodicVestingAddr, uomDenom) + s.Require().NoError(err) + s.execBankSend( + chain, + valIdx, + periodicVestingAddr, + Address(), + balance.Sub(standardFees).String(), + standardFees.String(), + true, + ) + waitStartTime = acc.StartTime - time.Now().Unix() + vestingTxDelay + time.Sleep(time.Duration(waitStartTime) * time.Second) + } + + firstPeriod := acc.StartTime + acc.VestingPeriods[0].Length + waitFirstPeriod := firstPeriod - time.Now().Unix() + if waitFirstPeriod > vestingTxDelay { + // Transfer coins should fail + balance, err = getSpecificBalance(api, periodicVestingAddr, uomDenom) + s.Require().NoError(err) + s.execBankSend( + chain, + valIdx, + periodicVestingAddr, + Address(), + balance.Sub(standardFees).String(), + standardFees.String(), + true, + ) + waitFirstPeriod = firstPeriod - time.Now().Unix() + vestingTxDelay + time.Sleep(time.Duration(waitFirstPeriod) * time.Second) + } + + // Delegate coins should succeed + s.execDelegate(chain, valIdx, vestingDelegationAmount.String(), valOpAddr, + periodicVestingAddr, mantraHomePath, vestingDelegationFees.String()) + + // Validate delegation successful + s.Require().Eventually( + func() bool { + res, err := queryDelegation(api, valOpAddr, periodicVestingAddr) + amt := res.GetDelegationResponse().GetDelegation().GetShares() + s.Require().NoError(err) + + return amt.Equal(math.LegacyNewDecFromInt(vestingDelegationAmount.Amount)) + }, + 20*time.Second, + 5*time.Second, + ) + + // Transfer coins should succeed + balance, err = getSpecificBalance(api, periodicVestingAddr, uomDenom) + s.Require().NoError(err) + s.execBankSend( + chain, + valIdx, + periodicVestingAddr, + Address(), + balance.Sub(standardFees).String(), + standardFees.String(), + false, + ) + + secondPeriod := firstPeriod + acc.VestingPeriods[1].Length + waitSecondPeriod := secondPeriod - time.Now().Unix() + if waitSecondPeriod > vestingTxDelay { + time.Sleep(time.Duration(waitSecondPeriod) * time.Second) + + // Transfer coins should succeed + balance, err = getSpecificBalance(api, periodicVestingAddr, uomDenom) + s.Require().NoError(err) + s.execBankSend( + chain, + valIdx, + periodicVestingAddr, + Address(), + balance.Sub(standardFees).String(), + standardFees.String(), + false, + ) + } + }) +} + +// generateVestingPeriod generate the vesting period file +func generateVestingPeriod() ([]byte, error) { + p := vestingPeriod{ + StartTime: time.Now().Add(time.Duration(rand.Intn(20)+95) * time.Second).Unix(), + Periods: []period{ + { + Coins: "850000000" + uomDenom, + Length: 35, + }, + { + Coins: "2000000000" + uomDenom, + Length: 35, + }, + }, + } + return json.Marshal(p) +} diff --git a/tests/e2e/genesis.go b/tests/e2e/genesis.go new file mode 100644 index 00000000..36a1fa01 --- /dev/null +++ b/tests/e2e/genesis.go @@ -0,0 +1,153 @@ +package e2e + +import ( + "encoding/json" + "fmt" + "time" + + feemarkettypes "github.com/skip-mev/feemarket/x/feemarket/types" + + "cosmossdk.io/math" + + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + govmigrv3 "github.com/cosmos/cosmos-sdk/x/gov/migrations/v3" + govmigrv4 "github.com/cosmos/cosmos-sdk/x/gov/migrations/v4" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + govlegacytypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func modifyGenesis(path, moniker, amountStr string, addrAll []sdk.AccAddress, basefee string, denom string) error { + serverCtx := server.NewDefaultContext() + config := serverCtx.Config + config.SetRoot(path) + config.Moniker = moniker + + coins, err := sdk.ParseCoinsNormalized(amountStr) + if err != nil { + return fmt.Errorf("failed to parse coins: %w", err) + } + + var balances []banktypes.Balance + var genAccounts []*authtypes.BaseAccount + for _, addr := range addrAll { + balance := banktypes.Balance{Address: addr.String(), Coins: coins.Sort()} + balances = append(balances, balance) + genAccount := authtypes.NewBaseAccount(addr, nil, 0, 0) + genAccounts = append(genAccounts, genAccount) + } + + genFile := config.GenesisFile() + appState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFile) + if err != nil { + return fmt.Errorf("failed to unmarshal genesis state: %w", err) + } + + authGenState := authtypes.GetGenesisStateFromAppState(cdc, appState) + accs, err := authtypes.UnpackAccounts(authGenState.Accounts) + if err != nil { + return fmt.Errorf("failed to get accounts from any: %w", err) + } + + for _, addr := range addrAll { + if accs.Contains(addr) { + return fmt.Errorf("failed to add account to genesis state; account already exists: %s", addr) + } + } + + // Add the new account to the set of genesis accounts and sanitize the + // accounts afterwards. + for _, genAcct := range genAccounts { + accs = append(accs, genAcct) + accs = authtypes.SanitizeGenesisAccounts(accs) + } + + genAccs, err := authtypes.PackAccounts(accs) + if err != nil { + return fmt.Errorf("failed to convert accounts into any's: %w", err) + } + + authGenState.Accounts = genAccs + + authGenStateBz, err := cdc.MarshalJSON(&authGenState) + if err != nil { + return fmt.Errorf("failed to marshal auth genesis state: %w", err) + } + appState[authtypes.ModuleName] = authGenStateBz + + bankGenState := banktypes.GetGenesisStateFromAppState(cdc, appState) + bankGenState.Balances = append(bankGenState.Balances, balances...) + bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances) + + bankGenStateBz, err := cdc.MarshalJSON(bankGenState) + if err != nil { + return fmt.Errorf("failed to marshal bank genesis state: %w", err) + } + appState[banktypes.ModuleName] = bankGenStateBz + + feemarketState := feemarkettypes.GetGenesisStateFromAppState(cdc, appState) + feemarketState.Params.MinBaseGasPrice = math.LegacyMustNewDecFromStr(basefee) + feemarketState.Params.FeeDenom = denom + feemarketState.Params.DistributeFees = true + feemarketState.State.BaseGasPrice = math.LegacyMustNewDecFromStr(basefee) + feemarketStateBz, err := cdc.MarshalJSON(&feemarketState) + if err != nil { + return fmt.Errorf("failed to marshal feemarket genesis state: %w", err) + } + appState[feemarkettypes.ModuleName] = feemarketStateBz + + stakingGenState := stakingtypes.GetGenesisStateFromAppState(cdc, appState) + stakingGenState.Params.BondDenom = denom + stakingGenStateBz, err := cdc.MarshalJSON(stakingGenState) + if err != nil { + return fmt.Errorf("failed to marshal staking genesis state: %s", err) + } + appState[stakingtypes.ModuleName] = stakingGenStateBz + + // Refactor to separate method + amnt := math.NewInt(10000) + quorum, _ := math.LegacyNewDecFromStr("0.000000000000000001") + threshold, _ := math.LegacyNewDecFromStr("0.000000000000000001") + + maxDepositPeriod := 10 * time.Minute + votingPeriod := 15 * time.Second + expeditedVoting := 13 * time.Second + + govStateLegacy := govlegacytypes.NewGenesisState(1, + govlegacytypes.NewDepositParams(sdk.NewCoins(sdk.NewCoin(denom, amnt)), maxDepositPeriod), + govlegacytypes.NewVotingParams(votingPeriod), + govlegacytypes.NewTallyParams(quorum, threshold, govlegacytypes.DefaultVetoThreshold), + ) + + govStateV3, err := govmigrv3.MigrateJSON(govStateLegacy) + if err != nil { + return fmt.Errorf("failed to migrate v1beta1 gov genesis state to v3: %w", err) + } + + govStateV4, err := govmigrv4.MigrateJSON(govStateV3) + if err != nil { + return fmt.Errorf("failed to migrate v1beta1 gov genesis state to v4: %w", err) + } + + govStateV4.Params.ExpeditedVotingPeriod = &expeditedVoting + govStateV4.Params.ExpeditedMinDeposit = sdk.NewCoins(sdk.NewCoin(denom, amnt)) // same as normal for testing + + govGenStateBz, err := cdc.MarshalJSON(govStateV4) + if err != nil { + return fmt.Errorf("failed to marshal gov genesis state: %w", err) + } + appState[govtypes.ModuleName] = govGenStateBz + + appStateJSON, err := json.Marshal(appState) + if err != nil { + return fmt.Errorf("failed to marshal application genesis state: %w", err) + } + genDoc.AppState = appStateJSON + + return genutil.ExportGenesisFile(genDoc, genFile) +} diff --git a/tests/e2e/http_util.go b/tests/e2e/http_util.go new file mode 100644 index 00000000..593759ac --- /dev/null +++ b/tests/e2e/http_util.go @@ -0,0 +1,40 @@ +package e2e + +import ( + "encoding/json" + "fmt" + "io" + "net/http" +) + +func httpGet(endpoint string) ([]byte, error) { + resp, err := http.Get(endpoint) //nolint:gosec // this is only used during tests + if err != nil { + return nil, fmt.Errorf("failed to execute HTTP request: %w", err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return body, nil +} + +func readJSON(resp *http.Response) (map[string]interface{}, error) { + defer resp.Body.Close() + + body, readErr := io.ReadAll(resp.Body) + if readErr != nil { + return nil, fmt.Errorf("failed to read Body") + } + + var data map[string]interface{} + err := json.Unmarshal(body, &data) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal response body") + } + + return data, nil +} diff --git a/tests/e2e/io.go b/tests/e2e/io.go new file mode 100644 index 00000000..80353bd5 --- /dev/null +++ b/tests/e2e/io.go @@ -0,0 +1,41 @@ +package e2e + +import ( + "fmt" + "io" + "os" +) + +// copyFile copy file from src to dst +func copyFile(src, dst string) (int64, error) { //nolint:unparam + sourceFileStat, err := os.Stat(src) + if err != nil { + return 0, err + } + + if !sourceFileStat.Mode().IsRegular() { + return 0, fmt.Errorf("%s is not a regular file", src) + } + + source, err := os.Open(src) + if err != nil { + return 0, err + } + defer source.Close() + + destination, err := os.Create(dst) + if err != nil { + return 0, err + } + defer destination.Close() + + nBytes, err := io.Copy(destination, source) + return nBytes, err +} + +// writeFile write a byte slice into a file path +// create the file if it doesn't exist +// NOTE: this file can be write and read by everyone +func writeFile(path string, body []byte) error { + return os.WriteFile(path, body, 0o666) //nolint:gosec +} diff --git a/tests/e2e/keys.go b/tests/e2e/keys.go new file mode 100644 index 00000000..d47a7ce5 --- /dev/null +++ b/tests/e2e/keys.go @@ -0,0 +1,20 @@ +package e2e + +import ( + "github.com/cosmos/go-bip39" +) + +// createMnemonic creates a random string mnemonic +func createMnemonic() (string, error) { + entropySeed, err := bip39.NewEntropy(256) + if err != nil { + return "", err + } + + mnemonic, err := bip39.NewMnemonic(entropySeed) + if err != nil { + return "", err + } + + return mnemonic, nil +} diff --git a/tests/e2e/query.go b/tests/e2e/query.go new file mode 100644 index 00000000..2addb0a3 --- /dev/null +++ b/tests/e2e/query.go @@ -0,0 +1,361 @@ +package e2e + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + + ratelimittypes "github.com/cosmos/ibc-apps/modules/rate-limiting/v8/types" + icacontrollertypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/controller/types" + + evidencetypes "cosmossdk.io/x/evidence/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govtypesv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func queryTx(endpoint, txHash string) error { + resp, err := http.Get(fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", endpoint, txHash)) + if err != nil { + return fmt.Errorf("failed to execute HTTP request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return fmt.Errorf("tx query returned non-200 status: %d", resp.StatusCode) + } + + var result map[string]interface{} + + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Errorf("failed to read response body: %w", err) + } + + txResp := result["tx_response"].(map[string]interface{}) + if v := txResp["code"]; v.(float64) != 0 { + return fmt.Errorf("tx %s failed with status code %v", txHash, v) + } + + return nil +} + +// if coin is zero, return empty coin. +func getSpecificBalance(endpoint, addr, denom string) (amt sdk.Coin, err error) { + balances, err := queryAllBalances(endpoint, addr) + if err != nil { + return amt, err + } + for _, c := range balances { + if strings.Contains(c.Denom, denom) { + amt = c + break + } + } + return amt, nil +} + +func queryAllBalances(endpoint, addr string) (sdk.Coins, error) { + body, err := httpGet(fmt.Sprintf("%s/cosmos/bank/v1beta1/balances/%s", endpoint, addr)) + if err != nil { + return nil, fmt.Errorf("failed to execute HTTP request: %w", err) + } + + var balancesResp banktypes.QueryAllBalancesResponse + if err := cdc.UnmarshalJSON(body, &balancesResp); err != nil { + return nil, err + } + + return balancesResp.Balances, nil +} + +func querySupplyOf(endpoint, denom string) (sdk.Coin, error) { + body, err := httpGet(fmt.Sprintf("%s/cosmos/bank/v1beta1/supply/by_denom?denom=%s", endpoint, denom)) + if err != nil { + return sdk.Coin{}, fmt.Errorf("failed to execute HTTP request: %w", err) + } + + var supplyOfResp banktypes.QuerySupplyOfResponse + if err := cdc.UnmarshalJSON(body, &supplyOfResp); err != nil { + return sdk.Coin{}, err + } + + return supplyOfResp.Amount, nil +} + +func queryStakingParams(endpoint string) (stakingtypes.QueryParamsResponse, error) { + body, err := httpGet(fmt.Sprintf("%s/cosmos/staking/v1beta1/params", endpoint)) + if err != nil { + return stakingtypes.QueryParamsResponse{}, fmt.Errorf("failed to execute HTTP request: %w", err) + } + + var params stakingtypes.QueryParamsResponse + if err := cdc.UnmarshalJSON(body, ¶ms); err != nil { + return stakingtypes.QueryParamsResponse{}, err + } + + return params, nil +} + +func queryDelegation(endpoint string, validatorAddr string, delegatorAddr string) (stakingtypes.QueryDelegationResponse, error) { + var res stakingtypes.QueryDelegationResponse + + body, err := httpGet(fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/delegations/%s", endpoint, validatorAddr, delegatorAddr)) + if err != nil { + return res, err + } + + if err = cdc.UnmarshalJSON(body, &res); err != nil { + return res, err + } + return res, nil +} + +func queryUnbondingDelegation(endpoint string, validatorAddr string, delegatorAddr string) (stakingtypes.QueryUnbondingDelegationResponse, error) { + var res stakingtypes.QueryUnbondingDelegationResponse + body, err := httpGet(fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s/delegations/%s/unbonding_delegation", endpoint, validatorAddr, delegatorAddr)) + if err != nil { + return res, err + } + + if err = cdc.UnmarshalJSON(body, &res); err != nil { + return res, err + } + return res, nil +} + +func queryDelegatorWithdrawalAddress(endpoint string, delegatorAddr string) (disttypes.QueryDelegatorWithdrawAddressResponse, error) { + var res disttypes.QueryDelegatorWithdrawAddressResponse + + body, err := httpGet(fmt.Sprintf("%s/cosmos/distribution/v1beta1/delegators/%s/withdraw_address", endpoint, delegatorAddr)) + if err != nil { + return res, err + } + + if err = cdc.UnmarshalJSON(body, &res); err != nil { + return res, err + } + return res, nil +} + +func queryGovProposal(endpoint string, proposalID int) (govtypesv1beta1.QueryProposalResponse, error) { + var govProposalResp govtypesv1beta1.QueryProposalResponse + + path := fmt.Sprintf("%s/cosmos/gov/v1beta1/proposals/%d", endpoint, proposalID) + + body, err := httpGet(path) + if err != nil { + return govProposalResp, fmt.Errorf("failed to execute HTTP request: %w", err) + } + if err := cdc.UnmarshalJSON(body, &govProposalResp); err != nil { + return govProposalResp, err + } + + return govProposalResp, nil +} + +func queryGovProposalV1(endpoint string, proposalID int) (govtypesv1.QueryProposalResponse, error) { + var govProposalResp govtypesv1.QueryProposalResponse + + path := fmt.Sprintf("%s/cosmos/gov/v1/proposals/%d", endpoint, proposalID) + + body, err := httpGet(path) + if err != nil { + return govProposalResp, fmt.Errorf("failed to execute HTTP request: %w", err) + } + if err := cdc.UnmarshalJSON(body, &govProposalResp); err != nil { + return govProposalResp, err + } + + return govProposalResp, nil +} + +func queryAccount(endpoint, address string) (acc sdk.AccountI, err error) { + var res authtypes.QueryAccountResponse + resp, err := http.Get(fmt.Sprintf("%s/cosmos/auth/v1beta1/accounts/%s", endpoint, address)) + if err != nil { + return nil, fmt.Errorf("failed to execute HTTP request: %w", err) + } + defer resp.Body.Close() + + bz, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if err := cdc.UnmarshalJSON(bz, &res); err != nil { + return nil, err + } + return acc, cdc.UnpackAny(res.Account, &acc) +} + +func queryDelayedVestingAccount(endpoint, address string) (authvesting.DelayedVestingAccount, error) { + baseAcc, err := queryAccount(endpoint, address) + if err != nil { + return authvesting.DelayedVestingAccount{}, err + } + acc, ok := baseAcc.(*authvesting.DelayedVestingAccount) + if !ok { + return authvesting.DelayedVestingAccount{}, + fmt.Errorf("cannot cast %v to DelayedVestingAccount", baseAcc) + } + return *acc, nil +} + +func queryContinuousVestingAccount(endpoint, address string) (authvesting.ContinuousVestingAccount, error) { + baseAcc, err := queryAccount(endpoint, address) + if err != nil { + return authvesting.ContinuousVestingAccount{}, err + } + acc, ok := baseAcc.(*authvesting.ContinuousVestingAccount) + if !ok { + return authvesting.ContinuousVestingAccount{}, + fmt.Errorf("cannot cast %v to ContinuousVestingAccount", baseAcc) + } + return *acc, nil +} + +func queryPeriodicVestingAccount(endpoint, address string) (authvesting.PeriodicVestingAccount, error) { //nolint:unused // this is called during e2e tests + baseAcc, err := queryAccount(endpoint, address) + if err != nil { + return authvesting.PeriodicVestingAccount{}, err + } + acc, ok := baseAcc.(*authvesting.PeriodicVestingAccount) + if !ok { + return authvesting.PeriodicVestingAccount{}, + fmt.Errorf("cannot cast %v to PeriodicVestingAccount", baseAcc) + } + return *acc, nil +} + +func queryValidator(endpoint, address string) (stakingtypes.Validator, error) { + var res stakingtypes.QueryValidatorResponse + + body, err := httpGet(fmt.Sprintf("%s/cosmos/staking/v1beta1/validators/%s", endpoint, address)) + if err != nil { + return stakingtypes.Validator{}, fmt.Errorf("failed to execute HTTP request: %w", err) + } + + if err := cdc.UnmarshalJSON(body, &res); err != nil { + return stakingtypes.Validator{}, err + } + return res.Validator, nil +} + +func queryValidators(endpoint string) (stakingtypes.Validators, error) { + var res stakingtypes.QueryValidatorsResponse + body, err := httpGet(fmt.Sprintf("%s/cosmos/staking/v1beta1/validators", endpoint)) + if err != nil { + return stakingtypes.Validators{}, fmt.Errorf("failed to execute HTTP request: %w", err) + } + + if err := cdc.UnmarshalJSON(body, &res); err != nil { + return stakingtypes.Validators{}, err + } + + return stakingtypes.Validators{Validators: res.Validators}, nil +} + +func queryEvidence(endpoint, hash string) (evidencetypes.QueryEvidenceResponse, error) { //nolint:unused // this is called during e2e tests + var res evidencetypes.QueryEvidenceResponse + body, err := httpGet(fmt.Sprintf("%s/cosmos/evidence/v1beta1/evidence/%s", endpoint, hash)) + if err != nil { + return res, err + } + + if err = cdc.UnmarshalJSON(body, &res); err != nil { + return res, err + } + return res, nil +} + +func queryAllEvidence(endpoint string) (evidencetypes.QueryAllEvidenceResponse, error) { + var res evidencetypes.QueryAllEvidenceResponse + body, err := httpGet(fmt.Sprintf("%s/cosmos/evidence/v1beta1/evidence", endpoint)) + if err != nil { + return res, err + } + + if err = cdc.UnmarshalJSON(body, &res); err != nil { + return res, err + } + return res, nil +} + +func queryAllRateLimits(endpoint string) ([]ratelimittypes.RateLimit, error) { + var res ratelimittypes.QueryAllRateLimitsResponse + + body, err := httpGet(fmt.Sprintf("%s/Stride-Labs/ibc-rate-limiting/ratelimit/ratelimits", endpoint)) + if err != nil { + return []ratelimittypes.RateLimit{}, fmt.Errorf("failed to execute HTTP request: %w", err) + } + + if err := cdc.UnmarshalJSON(body, &res); err != nil { + return []ratelimittypes.RateLimit{}, err + } + return res.RateLimits, nil +} + +//nolint:unparam +func queryRateLimit(endpoint, channelID, denom string) (ratelimittypes.QueryRateLimitResponse, error) { + var res ratelimittypes.QueryRateLimitResponse + + body, err := httpGet(fmt.Sprintf("%s/Stride-Labs/ibc-rate-limiting/ratelimit/ratelimit/%s/by_denom?denom=%s", endpoint, channelID, denom)) + if err != nil { + return ratelimittypes.QueryRateLimitResponse{}, fmt.Errorf("failed to execute HTTP request: %w", err) + } + + if err := cdc.UnmarshalJSON(body, &res); err != nil { + return ratelimittypes.QueryRateLimitResponse{}, err + } + return res, nil +} + +func queryRateLimitsByChainID(endpoint, channelID string) ([]ratelimittypes.RateLimit, error) { + var res ratelimittypes.QueryRateLimitsByChainIdResponse + + body, err := httpGet(fmt.Sprintf("%s/Stride-Labs/ibc-rate-limiting/ratelimit/ratelimits/%s", endpoint, channelID)) + if err != nil { + return []ratelimittypes.RateLimit{}, fmt.Errorf("failed to execute HTTP request: %w", err) + } + + if err := cdc.UnmarshalJSON(body, &res); err != nil { + return []ratelimittypes.RateLimit{}, err + } + return res.RateLimits, nil +} + +func queryICAAccountAddress(endpoint, owner, connectionID string) (string, error) { + body, err := httpGet(fmt.Sprintf("%s/ibc/apps/interchain_accounts/controller/v1/owners/%s/connections/%s", endpoint, owner, connectionID)) + if err != nil { + return "", fmt.Errorf("failed to execute HTTP request: %w", err) + } + + var icaAccountResp icacontrollertypes.QueryInterchainAccountResponse + if err := cdc.UnmarshalJSON(body, &icaAccountResp); err != nil { + return "", err + } + + return icaAccountResp.Address, nil +} + +// TODO: Uncomment this function when CCV module is added +// func queryBlocksPerEpoch(endpoint string) (int64, error) { +// body, err := httpGet(fmt.Sprintf("%s/interchain_security/ccv/provider/params", endpoint)) +// if err != nil { +// return 0, fmt.Errorf("failed to execute HTTP request: %w", err) +// } + +// var response providertypes.QueryParamsResponse +// if err = cdc.UnmarshalJSON(body, &response); err != nil { +// return 0, err +// } + +// return response.Params.BlocksPerEpoch, nil +// } diff --git a/tests/e2e/scripts/hermes_bootstrap.sh b/tests/e2e/scripts/hermes_bootstrap.sh new file mode 100755 index 00000000..13f9fee9 --- /dev/null +++ b/tests/e2e/scripts/hermes_bootstrap.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +set -ex + +# initialize Hermes relayer configuration +mkdir -p /root/.hermes/ +touch /root/.hermes/config.toml + +echo $MANTRA_B_E2E_RLY_MNEMONIC >/root/.hermes/MANTRA_B_E2E_RLY_MNEMONIC.txt +echo $MANTRA_A_E2E_RLY_MNEMONIC >/root/.hermes/MANTRA_A_E2E_RLY_MNEMONIC.txt + +# setup Hermes relayer configuration with non-zero gas_price +tee /root/.hermes/config.toml < Date: Fri, 27 Dec 2024 12:08:43 +0800 Subject: [PATCH 02/14] edit makefile tests --- Makefile | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index f53417d6..daa5da48 100644 --- a/Makefile +++ b/Makefile @@ -134,15 +134,17 @@ $(BUILDDIR)/: ### Tests ### ############################################################################### -PACKAGES_UNIT=$(shell go list ./... | grep -v -e '/tests/e2e') +PACKAGES_UNIT=$(shell go list ./... | grep -v -e '/tests/e2e' | grep -v '/simulation') PACKAGES_E2E=$(shell cd tests/e2e && go list ./... | grep '/e2e') TEST_PACKAGES=./... -TEST_TARGETS := test-unit test-e2e +TEST_TARGETS := test-unit test-e2e test-cover test-unit: ARGS=-timeout=5m -tags='norace' test-unit: TEST_PACKAGES=$(PACKAGES_UNIT) test-e2e: ARGS=-timeout=35m -v test-e2e: TEST_PACKAGES=$(PACKAGES_E2E) +test-cover: ARGS=-timeout=30m -coverprofile=coverage.txt -covermode=atomic -tags='norace' +test-cover: TEST_PACKAGES=$(PACKAGES_UNIT) $(TEST_TARGETS): run-tests run-tests: @@ -154,12 +156,6 @@ else @go test -mod=readonly $(ARGS) $(TEST_PACKAGES) endif -test-unit: - @VERSION=$(VERSION) go test ./x/... -mod=readonly -vet=all -tags='norace' $(PACKAGES_NOSIMULATION) - -test-cover: - @VERSION=$(VERSION) go test ./x/... -mod=readonly -timeout 30m -coverprofile=coverage.txt -covermode=atomic -tags='norace' $(PACKAGES_NOSIMULATION) - test-connect: build-image @VERSION=$(VERSION) cd tests/connect && go test -v -race . From 401c8a5e2d3c9ab82c669d5d04f9ddbc10e81254 Mon Sep 17 00:00:00 2001 From: freeelancer Date: Fri, 27 Dec 2024 15:28:53 +0800 Subject: [PATCH 03/14] change fees for multisend --- tests/e2e/e2e_bank_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/e2e_bank_test.go b/tests/e2e/e2e_bank_test.go index 80b425c2..0d6e1b3b 100644 --- a/tests/e2e/e2e_bank_test.go +++ b/tests/e2e/e2e_bank_test.go @@ -85,7 +85,7 @@ func (s *IntegrationTestSuite) testBankTokenTransfer() { afterCharlieUomBalance, err = getSpecificBalance(chainEndpoint, charlie.String(), uomDenom) s.Require().NoError(err) - decremented := beforeAliceUomBalance.Sub(tokenAmount).Sub(tokenAmount).Sub(sdk.NewCoin(uomDenom, math.NewInt(963))).IsEqual(afterAliceUomBalance) + decremented := beforeAliceUomBalance.Sub(tokenAmount).Sub(tokenAmount).Sub(sdk.NewCoin(uomDenom, math.NewInt(1006))).IsEqual(afterAliceUomBalance) incremented := beforeBobUomBalance.Add(tokenAmount).IsEqual(afterBobUomBalance) && beforeCharlieUomBalance.Add(tokenAmount).IsEqual(afterCharlieUomBalance) From 205333e77531e762beaed33fd8513f457cf323d6 Mon Sep 17 00:00:00 2001 From: freeelancer Date: Fri, 27 Dec 2024 18:57:01 +0800 Subject: [PATCH 04/14] update cosmos-sdk for connect tests --- tests/connect/go.mod | 17 ++++++++++------- tests/connect/go.sum | 32 ++++++++++++++++---------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/tests/connect/go.mod b/tests/connect/go.mod index 0f8b05b9..74a56649 100644 --- a/tests/connect/go.mod +++ b/tests/connect/go.mod @@ -5,7 +5,7 @@ go 1.23 toolchain go1.23.1 require ( - github.com/cosmos/cosmos-sdk v0.50.10 + github.com/cosmos/cosmos-sdk v0.50.11 github.com/icza/dyno v0.0.0-20230330125955-09f820a8d9c0 github.com/skip-mev/connect/tests/integration/v2 v2.0.0-20240918152634-04c8ba59dddc github.com/skip-mev/connect/v2 v2.1.2 @@ -16,7 +16,10 @@ require ( replace ( github.com/ChainSafe/go-schnorrkel => github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d github.com/ChainSafe/go-schnorrkel/1 => github.com/ChainSafe/go-schnorrkel v1.0.0 - github.com/cosmos/cosmos-sdk => github.com/MANTRA-Chain/cosmos-sdk v0.50.8-0.20240930142812-c3c9e0c9e75c + // Direct cosmos-sdk branch link: https://github.com/MANTRA-Chain/cosmos-sdk/tree/mantra/v0.50.11, current branch: mantra/v0.50.11 + // Direct commit link: https://github.com/MANTRA-Chain/cosmos-sdk/commit/0b3817cefa94e68b24a7b4f3c949cf469339047d + // Direct tag link: https://github.com/MANTRA-Chain/cosmos-sdk/tree/v0.50.11-v2-mantra-1 + github.com/cosmos/cosmos-sdk => github.com/MANTRA-Chain/cosmos-sdk v0.50.11-v2-mantra-1 github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 github.com/vedhavyas/go-subkey => github.com/strangelove-ventures/go-subkey v1.0.7 ) @@ -32,14 +35,14 @@ require ( cosmossdk.io/client/v2 v2.0.0-beta.4 // indirect cosmossdk.io/collections v0.4.0 // indirect cosmossdk.io/core v0.11.1 // indirect - cosmossdk.io/depinject v1.0.0 // indirect + cosmossdk.io/depinject v1.1.0 // indirect cosmossdk.io/errors v1.0.1 // indirect cosmossdk.io/log v1.4.1 // indirect cosmossdk.io/math v1.4.0 // indirect cosmossdk.io/store v1.1.1 // indirect cosmossdk.io/x/evidence v0.1.1 // indirect cosmossdk.io/x/feegrant v0.1.1 // indirect - cosmossdk.io/x/tx v0.13.5 // indirect + cosmossdk.io/x/tx v0.13.7 // indirect cosmossdk.io/x/upgrade v0.1.4 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect @@ -77,12 +80,12 @@ require ( github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/cosmos/btcutil v1.0.5 // indirect - github.com/cosmos/cosmos-db v1.0.2 // indirect + github.com/cosmos/cosmos-db v1.1.0 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/gogoproto v1.7.0 // indirect - github.com/cosmos/iavl v1.2.0 // indirect + github.com/cosmos/iavl v1.2.2 // indirect github.com/cosmos/ibc-go/modules/capability v1.0.1 // indirect github.com/cosmos/ibc-go/v8 v8.5.1 // indirect github.com/cosmos/ics23/go v0.11.0 // indirect @@ -269,7 +272,7 @@ require ( google.golang.org/api v0.189.0 // indirect google.golang.org/genproto v0.0.0-20240722135656-d784300faade // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f // indirect google.golang.org/grpc v1.67.1 // indirect google.golang.org/protobuf v1.35.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/tests/connect/go.sum b/tests/connect/go.sum index 0c58fcca..1bb5c9ed 100644 --- a/tests/connect/go.sum +++ b/tests/connect/go.sum @@ -194,8 +194,8 @@ cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s= cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0= cosmossdk.io/core v0.11.1 h1:h9WfBey7NAiFfIcUhDVNS503I2P2HdZLebJlUIs8LPA= cosmossdk.io/core v0.11.1/go.mod h1:OJzxcdC+RPrgGF8NJZR2uoQr56tc7gfBKhiKeDO7hH0= -cosmossdk.io/depinject v1.0.0 h1:dQaTu6+O6askNXO06+jyeUAnF2/ssKwrrszP9t5q050= -cosmossdk.io/depinject v1.0.0/go.mod h1:zxK/h3HgHoA/eJVtiSsoaRaRA2D5U4cJ5thIG4ssbB8= +cosmossdk.io/depinject v1.1.0 h1:wLan7LG35VM7Yo6ov0jId3RHWCGRhe8E8bsuARorl5E= +cosmossdk.io/depinject v1.1.0/go.mod h1:kkI5H9jCGHeKeYWXTqYdruogYrEeWvBQCw1Pj4/eCFI= cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= cosmossdk.io/log v1.4.1 h1:wKdjfDRbDyZRuWa8M+9nuvpVYxrEOwbD/CA8hvhU8QM= @@ -210,8 +210,8 @@ cosmossdk.io/x/evidence v0.1.1 h1:Ks+BLTa3uftFpElLTDp9L76t2b58htjVbSZ86aoK/E4= cosmossdk.io/x/evidence v0.1.1/go.mod h1:OoDsWlbtuyqS70LY51aX8FBTvguQqvFrt78qL7UzeNc= cosmossdk.io/x/feegrant v0.1.1 h1:EKFWOeo/pup0yF0svDisWWKAA9Zags6Zd0P3nRvVvw8= cosmossdk.io/x/feegrant v0.1.1/go.mod h1:2GjVVxX6G2fta8LWj7pC/ytHjryA6MHAJroBWHFNiEQ= -cosmossdk.io/x/tx v0.13.5 h1:FdnU+MdmFWn1pTsbfU0OCf2u6mJ8cqc1H4OMG418MLw= -cosmossdk.io/x/tx v0.13.5/go.mod h1:V6DImnwJMTq5qFjeGWpXNiT/fjgE4HtmclRmTqRVM3w= +cosmossdk.io/x/tx v0.13.7 h1:8WSk6B/OHJLYjiZeMKhq7DK7lHDMyK0UfDbBMxVmeOI= +cosmossdk.io/x/tx v0.13.7/go.mod h1:V6DImnwJMTq5qFjeGWpXNiT/fjgE4HtmclRmTqRVM3w= cosmossdk.io/x/upgrade v0.1.4 h1:/BWJim24QHoXde8Bc64/2BSEB6W4eTydq0X/2f8+g38= cosmossdk.io/x/upgrade v0.1.4/go.mod h1:9v0Aj+fs97O+Ztw+tG3/tp5JSlrmT7IcFhAebQHmOPo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -243,8 +243,8 @@ github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUq github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc= github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/MANTRA-Chain/cosmos-sdk v0.50.8-0.20240930142812-c3c9e0c9e75c h1:c8gqCCJ0kcbtARnbc6GBq34FdniFkvycQuRMQSIb1rU= -github.com/MANTRA-Chain/cosmos-sdk v0.50.8-0.20240930142812-c3c9e0c9e75c/go.mod h1:3eaelj+PSlqbz2aDNGKKFWBEUExH2LvMvTgO5S3U/gg= +github.com/MANTRA-Chain/cosmos-sdk v0.50.11-v2-mantra-1 h1:8A13Xh6s+Eyl0kuFYGsWaDhji1OxVY+SnFHfml8t/jE= +github.com/MANTRA-Chain/cosmos-sdk v0.50.11-v2-mantra-1/go.mod h1:gt14Meok2IDCjbDtjwkbUcgVNEpUBDN/4hg9cCUtLgw= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= @@ -303,8 +303,8 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= -github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= -github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= +github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -377,8 +377,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= -github.com/cosmos/cosmos-db v1.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAKs= -github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA= +github.com/cosmos/cosmos-db v1.1.0 h1:KLHNVQ73h7vawXTpj9UJ7ZR2IXv51tsEHkQJJ9EBDzI= +github.com/cosmos/cosmos-db v1.1.0/go.mod h1:t7c4A6cfGdpUwwVxrQ0gQLeRQqGUBJu0yvE4F/26REg= github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA= github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRApmI9hjHH05nefC0Ojec= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= @@ -389,8 +389,8 @@ github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fro= github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0= -github.com/cosmos/iavl v1.2.0 h1:kVxTmjTh4k0Dh1VNL046v6BXqKziqMDzxo93oh3kOfM= -github.com/cosmos/iavl v1.2.0/go.mod h1:HidWWLVAtODJqFD6Hbne2Y0q3SdxByJepHUOeoH4LiI= +github.com/cosmos/iavl v1.2.2 h1:qHhKW3I70w+04g5KdsdVSHRbFLgt3yY3qTMd4Xa4rC8= +github.com/cosmos/iavl v1.2.2/go.mod h1:GiM43q0pB+uG53mLxLDzimxM9l/5N9UuSY3/D0huuVw= github.com/cosmos/ibc-go/modules/capability v1.0.1 h1:ibwhrpJ3SftEEZRxCRkH0fQZ9svjthrX2+oXdZvzgGI= github.com/cosmos/ibc-go/modules/capability v1.0.1/go.mod h1:rquyOV262nGJplkumH+/LeYs04P3eV8oB7ZM4Ygqk4E= github.com/cosmos/ibc-go/v8 v8.5.1 h1:3JleEMKBjRKa3FeTKt4fjg22za/qygLBo7mDkoYTNBs= @@ -784,8 +784,8 @@ github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= -github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= +github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= +github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -1761,8 +1761,8 @@ google.golang.org/genproto v0.0.0-20240722135656-d784300faade h1:lKFsS7wpngDgSCe google.golang.org/genproto v0.0.0-20240722135656-d784300faade/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY= google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f h1:cUMEy+8oS78BWIH9OWazBkzbr090Od9tWBNtZHkOhf0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= From 265bed86ae51b3d3ab8278a7530e9fec170f48c1 Mon Sep 17 00:00:00 2001 From: allthatjazzleo Date: Fri, 27 Dec 2024 19:31:48 +0800 Subject: [PATCH 05/14] - add sdk and e2e test workflow - fix tests-unit.yml to use make - update Makefile - update docker ci - add .dockerignore - update .gitignore --- .dockerignore | 40 ++++++++++++++++++++++++++++++++ .github/workflows/docker.yml | 5 ++-- .github/workflows/tests-e2e.yml | 15 ++++++++++++ .github/workflows/tests-sdk.yml | 33 ++++++++++++++++++++++++++ .github/workflows/tests-unit.yml | 10 ++++---- .gitignore | 3 +++ Makefile | 14 ++++++----- 7 files changed, 106 insertions(+), 14 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/tests-e2e.yml create mode 100644 .github/workflows/tests-sdk.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..ecad5deb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,40 @@ +# Ignore the git related directory +.git +.github +.devcontainer + +# Ignore local environment files +.env +*.local + +# Ignore build artifacts +bin/ +build/ +obj/ +dist/ + +# Ignore test files +/tests/ + +# Ignore any other unnecessary directories +/proto +/networks +/adr + +# Ignore any other unnecessary files +*.log +*.tmp +*.bak +.golangci.yml +.goreleaser.yml +.lycheeignore +.markdownlint.yml +.markdownlintignore +buf.work.yaml +chains.yaml +coderabbit.yaml +config.yml +LICENSE +README.md +SECURITY.md +sonar-project.properties diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c0324437..7dd87e8c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -50,9 +50,8 @@ jobs: with: chain: mantrachain chains-spec-file: chains.yaml - clone-key: ${{ secrets.GIT_CLONE_KEY }} - heighliner-owner: ${{ github.repository_owner }} - heighliner-tag: v1.6.4 + heighliner-owner: strangelove-ventures + heighliner-tag: v1.7.1 github-organization: ${{ github.repository_owner }} github-repo: ${{ github.event.repository.name }} git-ref: ${{ github.event.inputs.release_tag || github.ref_name }} diff --git a/.github/workflows/tests-e2e.yml b/.github/workflows/tests-e2e.yml new file mode 100644 index 00000000..b1b0d47a --- /dev/null +++ b/.github/workflows/tests-e2e.yml @@ -0,0 +1,15 @@ +on: + push: + branches: main + pull_request: + branches: main +name: E2E Tests +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v5 + with: + go-version: 1.23 + - uses: actions/checkout@v4 + - run: make test-e2e diff --git a/.github/workflows/tests-sdk.yml b/.github/workflows/tests-sdk.yml new file mode 100644 index 00000000..d097c257 --- /dev/null +++ b/.github/workflows/tests-sdk.yml @@ -0,0 +1,33 @@ +on: + push: + branches: main + pull_request: + branches: main +name: SDK Tests +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v5 + with: + go-version: 1.23 + - uses: actions/checkout@v4 + - name: Extract Tag or Commit + run: | + FULL_VERSION=$(go list -m github.com/cosmos/cosmos-sdk 2> /dev/null | sed 's:.* ::') + if [[ $FULL_VERSION == *"-0."* ]]; then + # This is a pseudo-version (commit-based) + SDK_VERSION=${FULL_VERSION##*-} + else + # This is a regular version tag + SDK_VERSION=$FULL_VERSION + fi + echo "SDK_VERSION=${VERSION}" >> $GITHUB_ENV + - name: Checkout MANTRA-Chain/cosmos-sdk + uses: actions/checkout@v4 + with: + repository: MANTRA-Chain/cosmos-sdk + path: cosmos-sdk + ref: ${{ env.SDK_VERSION }} + - run: make test-all + working-directory: cosmos-sdk diff --git a/.github/workflows/tests-unit.yml b/.github/workflows/tests-unit.yml index 2f3236f0..57d393b4 100644 --- a/.github/workflows/tests-unit.yml +++ b/.github/workflows/tests-unit.yml @@ -1,8 +1,8 @@ on: - push: - branches: main - pull_request: - branches: main + push: + branches: main + pull_request: + branches: main name: Unit Tests jobs: test: @@ -12,4 +12,4 @@ jobs: with: go-version: 1.23 - uses: actions/checkout@v4 - - run: go test ./... + - run: make test-unit diff --git a/.gitignore b/.gitignore index bfd2f476..cce4ce67 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ release/ swagger-proto tmp-swagger-gen client/docs/node_modules +build +exclusive.lock +coverage.txt diff --git a/Makefile b/Makefile index daa5da48..77393398 100644 --- a/Makefile +++ b/Makefile @@ -137,28 +137,30 @@ $(BUILDDIR)/: PACKAGES_UNIT=$(shell go list ./... | grep -v -e '/tests/e2e' | grep -v '/simulation') PACKAGES_E2E=$(shell cd tests/e2e && go list ./... | grep '/e2e') TEST_PACKAGES=./... -TEST_TARGETS := test-unit test-e2e test-cover +TEST_TARGETS := test-unit test-e2e test-cover test-connect +DIR=$(CURDIR) test-unit: ARGS=-timeout=5m -tags='norace' test-unit: TEST_PACKAGES=$(PACKAGES_UNIT) test-e2e: ARGS=-timeout=35m -v test-e2e: TEST_PACKAGES=$(PACKAGES_E2E) +test-e2e: build-image test-cover: ARGS=-timeout=30m -coverprofile=coverage.txt -covermode=atomic -tags='norace' test-cover: TEST_PACKAGES=$(PACKAGES_UNIT) +test-connect: ARGS=-v -race +test-connect: DIR=$(CURDIR)/tests/connect +test-connect: build-image $(TEST_TARGETS): run-tests run-tests: ifneq (,$(shell which tparse 2>/dev/null)) @echo "--> Running tests" - @go test -mod=readonly -json $(ARGS) $(TEST_PACKAGES) | tparse + @cd $(DIR) && go test -mod=readonly -json $(ARGS) $(TEST_PACKAGES) | tparse else @echo "--> Running tests" - @go test -mod=readonly $(ARGS) $(TEST_PACKAGES) + cd $(DIR) && go test -mod=readonly $(ARGS) $(TEST_PACKAGES) endif -test-connect: build-image - @VERSION=$(VERSION) cd tests/connect && go test -v -race . - ############################################################################### ### Release ### ############################################################################### From 086f45fccef712a781698366eb347a4b54fc120b Mon Sep 17 00:00:00 2001 From: freeelancer Date: Fri, 27 Dec 2024 23:04:59 +0800 Subject: [PATCH 06/14] update test-cover --- .github/workflows/codecov.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index a3f9424f..260da38b 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -13,7 +13,7 @@ jobs: - name: Gather dependencies run: go mod download - name: Run coverage - run: go test -race -coverprofile=coverage.txt -covermode=atomic ./... + run: make test-cover - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 with: From 75a00b29483e11632f75f5c84a5d8e27af7e07b2 Mon Sep 17 00:00:00 2001 From: allthatjazzleo Date: Thu, 2 Jan 2025 15:38:47 +0800 Subject: [PATCH 07/14] install tparse before test --- .github/workflows/connect-test.yml | 3 +++ .github/workflows/tests-e2e.yml | 3 +++ .github/workflows/tests-sdk.yml | 3 +++ .github/workflows/tests-unit.yml | 3 +++ 4 files changed, 12 insertions(+) diff --git a/.github/workflows/connect-test.yml b/.github/workflows/connect-test.yml index 3b8edde3..7897140e 100644 --- a/.github/workflows/connect-test.yml +++ b/.github/workflows/connect-test.yml @@ -17,4 +17,7 @@ jobs: with: go-version: ${{ matrix.go-version }} - uses: actions/checkout@v4 + - name: Install tparse + run: | + go install github.com/mfridman/tparse@latest - run: make test-connect diff --git a/.github/workflows/tests-e2e.yml b/.github/workflows/tests-e2e.yml index b1b0d47a..2ca55fb7 100644 --- a/.github/workflows/tests-e2e.yml +++ b/.github/workflows/tests-e2e.yml @@ -12,4 +12,7 @@ jobs: with: go-version: 1.23 - uses: actions/checkout@v4 + - name: Install tparse + run: | + go install github.com/mfridman/tparse@latest - run: make test-e2e diff --git a/.github/workflows/tests-sdk.yml b/.github/workflows/tests-sdk.yml index d097c257..e8a09452 100644 --- a/.github/workflows/tests-sdk.yml +++ b/.github/workflows/tests-sdk.yml @@ -29,5 +29,8 @@ jobs: repository: MANTRA-Chain/cosmos-sdk path: cosmos-sdk ref: ${{ env.SDK_VERSION }} + - name: Install tparse + run: | + go install github.com/mfridman/tparse@latest - run: make test-all working-directory: cosmos-sdk diff --git a/.github/workflows/tests-unit.yml b/.github/workflows/tests-unit.yml index 57d393b4..b50e0617 100644 --- a/.github/workflows/tests-unit.yml +++ b/.github/workflows/tests-unit.yml @@ -12,4 +12,7 @@ jobs: with: go-version: 1.23 - uses: actions/checkout@v4 + - name: Install tparse + run: | + go install github.com/mfridman/tparse@latest - run: make test-unit From bbf0b9e4839648b84aa8e0f512c426d8c0c7fcd5 Mon Sep 17 00:00:00 2001 From: freeelancer Date: Fri, 3 Jan 2025 16:34:37 +0800 Subject: [PATCH 08/14] fix: lint --- .golangci.yml | 1 - tests/e2e/chain.go | 14 +-- tests/e2e/e2e_distribution_test.go | 1 - tests/e2e/e2e_exec_test.go | 150 +++++++++++++++-------------- tests/e2e/e2e_gov_test.go | 4 +- tests/e2e/e2e_ibc_test.go | 7 +- tests/e2e/e2e_setup_test.go | 25 ++--- tests/e2e/e2e_staking_test.go | 1 - tests/e2e/e2e_test.go | 2 +- tests/e2e/e2e_vesting_test.go | 4 +- tests/e2e/genesis.go | 4 +- tests/e2e/query.go | 142 +++++++++++++-------------- tests/e2e/validator.go | 4 +- 13 files changed, 175 insertions(+), 184 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 3e093302..88032957 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -17,7 +17,6 @@ linters: - errname - copyloopvar - fatcontext - - forcetypeassert - gci - goconst - gochecksumtype diff --git a/tests/e2e/chain.go b/tests/e2e/chain.go index 1fc1ed45..065800bf 100644 --- a/tests/e2e/chain.go +++ b/tests/e2e/chain.go @@ -4,16 +4,14 @@ import ( "fmt" "os" - tmrand "github.com/cometbft/cometbft/libs/rand" - - dbm "github.com/cosmos/cosmos-db" - ratelimittypes "github.com/cosmos/ibc-apps/modules/rate-limiting/v8/types" - "cosmossdk.io/log" evidencetypes "cosmossdk.io/x/evidence/types" upgradetypes "cosmossdk.io/x/upgrade/types" - wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" + "github.com/MANTRA-Chain/mantrachain/app" + "github.com/MANTRA-Chain/mantrachain/app/params" + tmrand "github.com/cometbft/cometbft/libs/rand" + dbm "github.com/cosmos/cosmos-db" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" @@ -26,9 +24,7 @@ import ( govv1beta1types "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" paramsproptypes "github.com/cosmos/cosmos-sdk/x/params/types/proposal" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - "github.com/MANTRA-Chain/mantrachain/app" - "github.com/MANTRA-Chain/mantrachain/app/params" + ratelimittypes "github.com/cosmos/ibc-apps/modules/rate-limiting/v8/types" ) const ( diff --git a/tests/e2e/e2e_distribution_test.go b/tests/e2e/e2e_distribution_test.go index 6322e5f6..45f713c6 100644 --- a/tests/e2e/e2e_distribution_test.go +++ b/tests/e2e/e2e_distribution_test.go @@ -5,7 +5,6 @@ import ( "time" "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" ) diff --git a/tests/e2e/e2e_exec_test.go b/tests/e2e/e2e_exec_test.go index 59e82afc..882f0d45 100644 --- a/tests/e2e/e2e_exec_test.go +++ b/tests/e2e/e2e_exec_test.go @@ -11,10 +11,7 @@ import ( "strings" "time" - "github.com/ory/dockertest/v3/docker" - "cosmossdk.io/x/feegrant" - "github.com/cosmos/cosmos-sdk/client/flags" sdk "github.com/cosmos/cosmos-sdk/types" vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" @@ -23,6 +20,7 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/ory/dockertest/v3/docker" ) const ( @@ -132,8 +130,7 @@ func (s *IntegrationTestSuite) execDecode( return decoded } -func (s *IntegrationTestSuite) execVestingTx( //nolint:unused - +func (s *IntegrationTestSuite) execVestingTx( c *chain, method string, args []string, @@ -161,8 +158,7 @@ func (s *IntegrationTestSuite) execVestingTx( //nolint:unused s.T().Logf("successfully %s with %v", method, args) } -func (s *IntegrationTestSuite) execCreatePeriodicVestingAccount( //nolint:unused - +func (s *IntegrationTestSuite) execCreatePeriodicVestingAccount( c *chain, address, jsonPath string, @@ -198,6 +194,7 @@ func (s *IntegrationTestSuite) execUnjail( s.T().Logf("successfully unjail with options %v", opt) } +//nolint:unused func (s *IntegrationTestSuite) execFeeGrant(c *chain, valIdx int, granter, grantee, spendLimit string, opt ...flagOption) { opt = append(opt, withKeyValue(flagFrom, granter)) opt = append(opt, withKeyValue(flagSpendLimit, spendLimit)) @@ -229,31 +226,32 @@ func (s *IntegrationTestSuite) execFeeGrant(c *chain, valIdx int, granter, grant s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) } -func (s *IntegrationTestSuite) execFeeGrantRevoke(c *chain, valIdx int, granter, grantee string, opt ...flagOption) { - opt = append(opt, withKeyValue(flagFrom, granter)) - opts := applyOptions(c.id, opt) +// func (s *IntegrationTestSuite) execFeeGrantRevoke(c *chain, valIdx int, granter, grantee string, opt ...flagOption) { +// opt = append(opt, withKeyValue(flagFrom, granter)) +// opts := applyOptions(c.id, opt) - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - defer cancel() +// ctx, cancel := context.WithTimeout(context.Background(), time.Minute) +// defer cancel() - s.T().Logf("revoking %s fee grant from %s on chain %s", grantee, granter, c.id) +// s.T().Logf("revoking %s fee grant from %s on chain %s", grantee, granter, c.id) - mantraCommand := []string{ - mantrachaindBinary, - txCommand, - feegrant.ModuleName, - "revoke", - granter, - grantee, - "-y", - } - for flag, value := range opts { - mantraCommand = append(mantraCommand, fmt.Sprintf("--%s=%v", flag, value)) - } +// mantraCommand := []string{ +// mantrachaindBinary, +// txCommand, +// feegrant.ModuleName, +// "revoke", +// granter, +// grantee, +// "-y", +// } +// for flag, value := range opts { +// mantraCommand = append(mantraCommand, fmt.Sprintf("--%s=%v", flag, value)) +// } - s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) -} +// s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) +// } +//nolint:unparam func (s *IntegrationTestSuite) execBankSend( c *chain, valIdx int, @@ -430,7 +428,6 @@ func (s *IntegrationTestSuite) runGovExec(c *chain, valIdx int, submitterAddr, g // } func (s *IntegrationTestSuite) execDelegate(c *chain, valIdx int, amount, valOperAddress, delegatorAddr, home, delegateFees string) { //nolint:unparam - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -767,6 +764,7 @@ func (s *IntegrationTestSuite) defaultExecValidation(chain *chain, valIdx int) f } } +//nolint:unused func (s *IntegrationTestSuite) expectTxSubmitError(expectErrString string) func([]byte, []byte) bool { return func(stdOut []byte, stdErr []byte) bool { var txResp sdk.TxResponse @@ -780,59 +778,60 @@ func (s *IntegrationTestSuite) expectTxSubmitError(expectErrString string) func( } } -func (s *IntegrationTestSuite) executeValidatorBond(c *chain, valIdx int, valOperAddress, delegatorAddr, home, delegateFees string) { - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - defer cancel() +// func (s *IntegrationTestSuite) executeValidatorBond(c *chain, valIdx int, valOperAddress, delegatorAddr, home, delegateFees string) { +// ctx, cancel := context.WithTimeout(context.Background(), time.Minute) +// defer cancel() - s.T().Logf("Executing mantrachaind tx staking validator-bond %s", c.id) +// s.T().Logf("Executing mantrachaind tx staking validator-bond %s", c.id) - mantraCommand := []string{ - mantrachaindBinary, - txCommand, - stakingtypes.ModuleName, - "validator-bond", - valOperAddress, - fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddr), - fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), - fmt.Sprintf("--%s=%s", flags.FlagGasPrices, delegateFees), - "--keyring-backend=test", - fmt.Sprintf("--%s=%s", flags.FlagHome, home), - "--output=json", - "-y", - } +// mantraCommand := []string{ +// mantrachaindBinary, +// txCommand, +// stakingtypes.ModuleName, +// "validator-bond", +// valOperAddress, +// fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddr), +// fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), +// fmt.Sprintf("--%s=%s", flags.FlagGasPrices, delegateFees), +// "--keyring-backend=test", +// fmt.Sprintf("--%s=%s", flags.FlagHome, home), +// "--output=json", +// "-y", +// } - s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) - s.T().Logf("%s successfully executed validator bond tx to %s", delegatorAddr, valOperAddress) -} +// s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) +// s.T().Logf("%s successfully executed validator bond tx to %s", delegatorAddr, valOperAddress) +// } -func (s *IntegrationTestSuite) executeTokenizeShares(c *chain, valIdx int, amount, valOperAddress, delegatorAddr, home, delegateFees string) { - ctx, cancel := context.WithTimeout(context.Background(), time.Minute) - defer cancel() +// func (s *IntegrationTestSuite) executeTokenizeShares(c *chain, valIdx int, amount, valOperAddress, delegatorAddr, home, delegateFees string) { +// ctx, cancel := context.WithTimeout(context.Background(), time.Minute) +// defer cancel() - s.T().Logf("Executing mantrachaind tx staking tokenize-share %s", c.id) +// s.T().Logf("Executing mantrachaind tx staking tokenize-share %s", c.id) - mantraCommand := []string{ - mantrachaindBinary, - txCommand, - stakingtypes.ModuleName, - "tokenize-share", - valOperAddress, - amount, - delegatorAddr, - fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddr), - fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), - fmt.Sprintf("--%s=%s", flags.FlagGasPrices, delegateFees), - fmt.Sprintf("--%s=%d", flags.FlagGas, 1000000), - "--keyring-backend=test", - fmt.Sprintf("--%s=%s", flags.FlagHome, home), - "--output=json", - "-y", - } +// mantraCommand := []string{ +// mantrachaindBinary, +// txCommand, +// stakingtypes.ModuleName, +// "tokenize-share", +// valOperAddress, +// amount, +// delegatorAddr, +// fmt.Sprintf("--%s=%s", flags.FlagFrom, delegatorAddr), +// fmt.Sprintf("--%s=%s", flags.FlagChainID, c.id), +// fmt.Sprintf("--%s=%s", flags.FlagGasPrices, delegateFees), +// fmt.Sprintf("--%s=%d", flags.FlagGas, 1000000), +// "--keyring-backend=test", +// fmt.Sprintf("--%s=%s", flags.FlagHome, home), +// "--output=json", +// "-y", +// } - s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) - s.T().Logf("%s successfully executed tokenize share tx from %s", delegatorAddr, valOperAddress) -} +// s.executeTxCommand(ctx, c, mantraCommand, valIdx, s.defaultExecValidation(c, valIdx)) +// s.T().Logf("%s successfully executed tokenize share tx from %s", delegatorAddr, valOperAddress) +// } +//nolint:unused func (s *IntegrationTestSuite) executeRedeemShares(c *chain, valIdx int, amount, delegatorAddr, home, delegateFees string) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -859,6 +858,7 @@ func (s *IntegrationTestSuite) executeRedeemShares(c *chain, valIdx int, amount, s.T().Logf("%s successfully executed redeem share tx for %s", delegatorAddr, amount) } +//nolint:unused func (s *IntegrationTestSuite) executeTransferTokenizeShareRecord(c *chain, valIdx int, recordID, owner, newOwner, home, txFees string) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -888,6 +888,8 @@ func (s *IntegrationTestSuite) executeTransferTokenizeShareRecord(c *chain, valI // signTxFileOnline signs a transaction file using the mantracli tx sign command // the from flag is used to specify the keyring account to sign the transaction // the from account must be registered in the keyring and exist on chain (have a balance or be a genesis account) +// +//nolint:unused func (s *IntegrationTestSuite) signTxFileOnline(chain *chain, valIdx int, from string, txFilePath string) ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -923,6 +925,8 @@ func (s *IntegrationTestSuite) signTxFileOnline(chain *chain, valIdx int, from s // broadcastTxFile broadcasts a signed transaction file using the mantracli tx broadcast command // the from flag is used to specify the keyring account to sign the transaction // the from account must be registered in the keyring and exist on chain (have a balance or be a genesis account) +// +//nolint:unused func (s *IntegrationTestSuite) broadcastTxFile(chain *chain, valIdx int, from string, txFilePath string) ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() diff --git a/tests/e2e/e2e_gov_test.go b/tests/e2e/e2e_gov_test.go index 816cd19b..ec88a02e 100644 --- a/tests/e2e/e2e_gov_test.go +++ b/tests/e2e/e2e_gov_test.go @@ -7,7 +7,6 @@ import ( "cosmossdk.io/math" upgradetypes "cosmossdk.io/x/upgrade/types" - sdk "github.com/cosmos/cosmos-sdk/types" govtypesv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" govtypesv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" @@ -98,7 +97,7 @@ func (s *IntegrationTestSuite) GovCancelSoftwareUpgrade() { GovCommunityPoolSpend tests passing a community spend proposal. Test Benchmarks: 1. Fund Community Pool -2. Submission, deposit and vote of proposal to spend from the community pool to send oms to a recipient +2. Submission, deposit and vote of proposal to spend from the community pool to send atoms to a recipient 3. Validation that the recipient balance has increased by proposal amount */ func (s *IntegrationTestSuite) GovCommunityPoolSpend() { @@ -204,6 +203,7 @@ func (s *IntegrationTestSuite) submitGovCommand(chainAAPIEndpoint, sender string }) } +//nolint:unused func (s *IntegrationTestSuite) submitGovCommandExpectingFailure(sender string, govCommand string, proposalFlags []string) { s.Run(fmt.Sprintf("Running failing expedited tx gov %s -- expecting error", govCommand), func() { // should return an error -- the Tx fails at the ante handler diff --git a/tests/e2e/e2e_ibc_test.go b/tests/e2e/e2e_ibc_test.go index f9e22212..245644f2 100644 --- a/tests/e2e/e2e_ibc_test.go +++ b/tests/e2e/e2e_ibc_test.go @@ -26,7 +26,6 @@ type PacketMetadata struct { Forward *ForwardMetadata `json:"forward"` } -//nolint:unparam func (s *IntegrationTestSuite) sendIBC(c *chain, valIdx int, sender, recipient, token, fees, note string, expErr bool) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -60,7 +59,7 @@ func (s *IntegrationTestSuite) sendIBC(c *chain, valIdx int, sender, recipient, } } -func (s *IntegrationTestSuite) hermesClearPacket(configPath, chainID, portID, channelID string) (success bool) { //nolint:unparam +func (s *IntegrationTestSuite) hermesClearPacket(configPath, chainID, portID, channelID string) (success bool) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -95,6 +94,7 @@ type RelayerPacketsOutput struct { Status string `json:"status"` } +//nolint:unused func (s *IntegrationTestSuite) createConnection() { s.T().Logf("connecting %s and %s chains via IBC", s.chainA.id, s.chainB.id) @@ -118,6 +118,7 @@ func (s *IntegrationTestSuite) createConnection() { s.T().Logf("connected %s and %s chains via IBC", s.chainA.id, s.chainB.id) } +//nolint:unused func (s *IntegrationTestSuite) createChannel() { s.T().Logf("creating IBC transfer channel created between chains %s and %s", s.chainA.id, s.chainB.id) @@ -320,7 +321,7 @@ func (s *IntegrationTestSuite) testFailedMultihopIBCTokenTransfer() { var ( beforeSenderUOmBalance sdk.Coin - err error + err error ) s.Require().Eventually( diff --git a/tests/e2e/e2e_setup_test.go b/tests/e2e/e2e_setup_test.go index 04eab991..40f19df3 100644 --- a/tests/e2e/e2e_setup_test.go +++ b/tests/e2e/e2e_setup_test.go @@ -14,21 +14,12 @@ import ( "testing" "time" - "github.com/ory/dockertest/v3" - // "github.com/cosmos/cosmos-sdk/crypto/hd" - // "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/ory/dockertest/v3/docker" - "github.com/spf13/viper" - "github.com/stretchr/testify/suite" - + "cosmossdk.io/math" + evidencetypes "cosmossdk.io/x/evidence/types" tmconfig "github.com/cometbft/cometbft/config" "github.com/cometbft/cometbft/crypto/ed25519" tmjson "github.com/cometbft/cometbft/libs/json" rpchttp "github.com/cometbft/cometbft/rpc/client/http" - - "cosmossdk.io/math" - evidencetypes "cosmossdk.io/x/evidence/types" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" @@ -42,6 +33,10 @@ import ( genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/ory/dockertest/v3" + "github.com/ory/dockertest/v3/docker" + "github.com/spf13/viper" + "github.com/stretchr/testify/suite" ) const ( @@ -80,6 +75,7 @@ const ( transferPort = "transfer" transferChannel = "channel-0" + //nolint:unused govAuthority = "mantra10d07y265gmmuvt4z0w9aw880jnsr700j3fep4f" ) @@ -609,6 +605,8 @@ func noRestart(config *docker.HostConfig) { // runIBCRelayer bootstraps an IBC Hermes relayer by creating an IBC connection and // a transfer channel between chainA and chainB. +// +//nolint:unused func (s *IntegrationTestSuite) runIBCRelayer() { s.T().Log("starting Hermes relayer container") @@ -746,6 +744,7 @@ func (s *IntegrationTestSuite) writeCancelSoftwareUpgradeProposal(c *chain) { s.Require().NoError(err) } +//nolint:unused func (s *IntegrationTestSuite) writeLiquidStakingParamsUpdateProposal(c *chain, oldParams stakingtypes.Params) { template := ` { @@ -791,6 +790,8 @@ func (s *IntegrationTestSuite) writeLiquidStakingParamsUpdateProposal(c *chain, // writeGovParamChangeProposalBlocksPerEpoch writes a governance proposal JSON file to change the `BlocksPerEpoch` // parameter to the provided `blocksPerEpoch` +// +//nolint:unused func (s *IntegrationTestSuite) writeGovParamChangeProposalBlocksPerEpoch(c *chain, paramsJSON string) { template := ` { @@ -820,6 +821,8 @@ func (s *IntegrationTestSuite) writeGovParamChangeProposalBlocksPerEpoch(c *chai // writeFailingExpeditedProposal writes a governance proposal JSON file. // The proposal fails because only SoftwareUpgrade and CancelSoftwareUpgrade can be expedited. +// +//nolint:unused func (s *IntegrationTestSuite) writeFailingExpeditedProposal(c *chain, blocksPerEpoch int64) { template := ` { diff --git a/tests/e2e/e2e_staking_test.go b/tests/e2e/e2e_staking_test.go index 782a8f14..f64ae6aa 100644 --- a/tests/e2e/e2e_staking_test.go +++ b/tests/e2e/e2e_staking_test.go @@ -6,7 +6,6 @@ import ( "time" "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking/types" ) diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index 7f13970a..1d59201b 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -12,7 +12,7 @@ var ( runStakingAndDistributionTest = true runVestingTest = true runRestInterfacesTest = true - runRateLimitTest = false // TODO: enable after IBC is fixed + // runRateLimitTest = false // TODO: enable after IBC is fixed ) func (s *IntegrationTestSuite) TestRestInterfaces() { diff --git a/tests/e2e/e2e_vesting_test.go b/tests/e2e/e2e_vesting_test.go index 11427cb3..827e1f41 100644 --- a/tests/e2e/e2e_vesting_test.go +++ b/tests/e2e/e2e_vesting_test.go @@ -7,7 +7,6 @@ import ( "time" "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -197,8 +196,7 @@ func (s *IntegrationTestSuite) testContinuousVestingAccount(api string) { }) } -func (s *IntegrationTestSuite) testPeriodicVestingAccount(api string) { //nolint:unused - +func (s *IntegrationTestSuite) testPeriodicVestingAccount(api string) { s.Run("test periodic vesting genesis account", func() { var ( valIdx = 0 diff --git a/tests/e2e/genesis.go b/tests/e2e/genesis.go index 36a1fa01..3b52d0ce 100644 --- a/tests/e2e/genesis.go +++ b/tests/e2e/genesis.go @@ -5,10 +5,7 @@ import ( "fmt" "time" - feemarkettypes "github.com/skip-mev/feemarket/x/feemarket/types" - "cosmossdk.io/math" - "github.com/cosmos/cosmos-sdk/server" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" @@ -20,6 +17,7 @@ import ( govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govlegacytypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + feemarkettypes "github.com/skip-mev/feemarket/x/feemarket/types" ) func modifyGenesis(path, moniker, amountStr string, addrAll []sdk.AccAddress, basefee string, denom string) error { diff --git a/tests/e2e/query.go b/tests/e2e/query.go index 2addb0a3..9973ce44 100644 --- a/tests/e2e/query.go +++ b/tests/e2e/query.go @@ -7,11 +7,7 @@ import ( "net/http" "strings" - ratelimittypes "github.com/cosmos/ibc-apps/modules/rate-limiting/v8/types" - icacontrollertypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/controller/types" - evidencetypes "cosmossdk.io/x/evidence/types" - sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" @@ -29,7 +25,7 @@ func queryTx(endpoint, txHash string) error { } defer resp.Body.Close() - if resp.StatusCode != 200 { + if resp.StatusCode != http.StatusOK { return fmt.Errorf("tx query returned non-200 status: %d", resp.StatusCode) } @@ -76,33 +72,33 @@ func queryAllBalances(endpoint, addr string) (sdk.Coins, error) { return balancesResp.Balances, nil } -func querySupplyOf(endpoint, denom string) (sdk.Coin, error) { - body, err := httpGet(fmt.Sprintf("%s/cosmos/bank/v1beta1/supply/by_denom?denom=%s", endpoint, denom)) - if err != nil { - return sdk.Coin{}, fmt.Errorf("failed to execute HTTP request: %w", err) - } +// func querySupplyOf(endpoint, denom string) (sdk.Coin, error) { +// body, err := httpGet(fmt.Sprintf("%s/cosmos/bank/v1beta1/supply/by_denom?denom=%s", endpoint, denom)) +// if err != nil { +// return sdk.Coin{}, fmt.Errorf("failed to execute HTTP request: %w", err) +// } - var supplyOfResp banktypes.QuerySupplyOfResponse - if err := cdc.UnmarshalJSON(body, &supplyOfResp); err != nil { - return sdk.Coin{}, err - } +// var supplyOfResp banktypes.QuerySupplyOfResponse +// if err := cdc.UnmarshalJSON(body, &supplyOfResp); err != nil { +// return sdk.Coin{}, err +// } - return supplyOfResp.Amount, nil -} +// return supplyOfResp.Amount, nil +// } -func queryStakingParams(endpoint string) (stakingtypes.QueryParamsResponse, error) { - body, err := httpGet(fmt.Sprintf("%s/cosmos/staking/v1beta1/params", endpoint)) - if err != nil { - return stakingtypes.QueryParamsResponse{}, fmt.Errorf("failed to execute HTTP request: %w", err) - } +// func queryStakingParams(endpoint string) (stakingtypes.QueryParamsResponse, error) { +// body, err := httpGet(fmt.Sprintf("%s/cosmos/staking/v1beta1/params", endpoint)) +// if err != nil { +// return stakingtypes.QueryParamsResponse{}, fmt.Errorf("failed to execute HTTP request: %w", err) +// } - var params stakingtypes.QueryParamsResponse - if err := cdc.UnmarshalJSON(body, ¶ms); err != nil { - return stakingtypes.QueryParamsResponse{}, err - } +// var params stakingtypes.QueryParamsResponse +// if err := cdc.UnmarshalJSON(body, ¶ms); err != nil { +// return stakingtypes.QueryParamsResponse{}, err +// } - return params, nil -} +// return params, nil +// } func queryDelegation(endpoint string, validatorAddr string, delegatorAddr string) (stakingtypes.QueryDelegationResponse, error) { var res stakingtypes.QueryDelegationResponse @@ -221,7 +217,7 @@ func queryContinuousVestingAccount(endpoint, address string) (authvesting.Contin return *acc, nil } -func queryPeriodicVestingAccount(endpoint, address string) (authvesting.PeriodicVestingAccount, error) { //nolint:unused // this is called during e2e tests +func queryPeriodicVestingAccount(endpoint, address string) (authvesting.PeriodicVestingAccount, error) { baseAcc, err := queryAccount(endpoint, address) if err != nil { return authvesting.PeriodicVestingAccount{}, err @@ -288,62 +284,62 @@ func queryAllEvidence(endpoint string) (evidencetypes.QueryAllEvidenceResponse, return res, nil } -func queryAllRateLimits(endpoint string) ([]ratelimittypes.RateLimit, error) { - var res ratelimittypes.QueryAllRateLimitsResponse +// func queryAllRateLimits(endpoint string) ([]ratelimittypes.RateLimit, error) { +// var res ratelimittypes.QueryAllRateLimitsResponse - body, err := httpGet(fmt.Sprintf("%s/Stride-Labs/ibc-rate-limiting/ratelimit/ratelimits", endpoint)) - if err != nil { - return []ratelimittypes.RateLimit{}, fmt.Errorf("failed to execute HTTP request: %w", err) - } +// body, err := httpGet(fmt.Sprintf("%s/Stride-Labs/ibc-rate-limiting/ratelimit/ratelimits", endpoint)) +// if err != nil { +// return []ratelimittypes.RateLimit{}, fmt.Errorf("failed to execute HTTP request: %w", err) +// } - if err := cdc.UnmarshalJSON(body, &res); err != nil { - return []ratelimittypes.RateLimit{}, err - } - return res.RateLimits, nil -} +// if err := cdc.UnmarshalJSON(body, &res); err != nil { +// return []ratelimittypes.RateLimit{}, err +// } +// return res.RateLimits, nil +// } -//nolint:unparam -func queryRateLimit(endpoint, channelID, denom string) (ratelimittypes.QueryRateLimitResponse, error) { - var res ratelimittypes.QueryRateLimitResponse +// //nolint:unparam +// func queryRateLimit(endpoint, channelID, denom string) (ratelimittypes.QueryRateLimitResponse, error) { +// var res ratelimittypes.QueryRateLimitResponse - body, err := httpGet(fmt.Sprintf("%s/Stride-Labs/ibc-rate-limiting/ratelimit/ratelimit/%s/by_denom?denom=%s", endpoint, channelID, denom)) - if err != nil { - return ratelimittypes.QueryRateLimitResponse{}, fmt.Errorf("failed to execute HTTP request: %w", err) - } +// body, err := httpGet(fmt.Sprintf("%s/Stride-Labs/ibc-rate-limiting/ratelimit/ratelimit/%s/by_denom?denom=%s", endpoint, channelID, denom)) +// if err != nil { +// return ratelimittypes.QueryRateLimitResponse{}, fmt.Errorf("failed to execute HTTP request: %w", err) +// } - if err := cdc.UnmarshalJSON(body, &res); err != nil { - return ratelimittypes.QueryRateLimitResponse{}, err - } - return res, nil -} +// if err := cdc.UnmarshalJSON(body, &res); err != nil { +// return ratelimittypes.QueryRateLimitResponse{}, err +// } +// return res, nil +// } -func queryRateLimitsByChainID(endpoint, channelID string) ([]ratelimittypes.RateLimit, error) { - var res ratelimittypes.QueryRateLimitsByChainIdResponse +// func queryRateLimitsByChainID(endpoint, channelID string) ([]ratelimittypes.RateLimit, error) { +// var res ratelimittypes.QueryRateLimitsByChainIdResponse - body, err := httpGet(fmt.Sprintf("%s/Stride-Labs/ibc-rate-limiting/ratelimit/ratelimits/%s", endpoint, channelID)) - if err != nil { - return []ratelimittypes.RateLimit{}, fmt.Errorf("failed to execute HTTP request: %w", err) - } +// body, err := httpGet(fmt.Sprintf("%s/Stride-Labs/ibc-rate-limiting/ratelimit/ratelimits/%s", endpoint, channelID)) +// if err != nil { +// return []ratelimittypes.RateLimit{}, fmt.Errorf("failed to execute HTTP request: %w", err) +// } - if err := cdc.UnmarshalJSON(body, &res); err != nil { - return []ratelimittypes.RateLimit{}, err - } - return res.RateLimits, nil -} +// if err := cdc.UnmarshalJSON(body, &res); err != nil { +// return []ratelimittypes.RateLimit{}, err +// } +// return res.RateLimits, nil +// } -func queryICAAccountAddress(endpoint, owner, connectionID string) (string, error) { - body, err := httpGet(fmt.Sprintf("%s/ibc/apps/interchain_accounts/controller/v1/owners/%s/connections/%s", endpoint, owner, connectionID)) - if err != nil { - return "", fmt.Errorf("failed to execute HTTP request: %w", err) - } +// func queryICAAccountAddress(endpoint, owner, connectionID string) (string, error) { +// body, err := httpGet(fmt.Sprintf("%s/ibc/apps/interchain_accounts/controller/v1/owners/%s/connections/%s", endpoint, owner, connectionID)) +// if err != nil { +// return "", fmt.Errorf("failed to execute HTTP request: %w", err) +// } - var icaAccountResp icacontrollertypes.QueryInterchainAccountResponse - if err := cdc.UnmarshalJSON(body, &icaAccountResp); err != nil { - return "", err - } +// var icaAccountResp icacontrollertypes.QueryInterchainAccountResponse +// if err := cdc.UnmarshalJSON(body, &icaAccountResp); err != nil { +// return "", err +// } - return icaAccountResp.Address, nil -} +// return icaAccountResp.Address, nil +// } // TODO: Uncomment this function when CCV module is added // func queryBlocksPerEpoch(endpoint string) (int64, error) { diff --git a/tests/e2e/validator.go b/tests/e2e/validator.go index 97628e93..5ac8dadf 100644 --- a/tests/e2e/validator.go +++ b/tests/e2e/validator.go @@ -8,13 +8,11 @@ import ( "path" "path/filepath" + "cosmossdk.io/math" tmcfg "github.com/cometbft/cometbft/config" tmos "github.com/cometbft/cometbft/libs/os" "github.com/cometbft/cometbft/p2p" "github.com/cometbft/cometbft/privval" - - "cosmossdk.io/math" - "github.com/cosmos/cosmos-sdk/client/tx" sdkcrypto "github.com/cosmos/cosmos-sdk/crypto" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" From 5eec32d39fee8b9e53463cf6b0c002e12db13a93 Mon Sep 17 00:00:00 2001 From: freeelancer Date: Fri, 3 Jan 2025 16:45:16 +0800 Subject: [PATCH 09/14] update docker file --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 74e0f4e1..a5a7cdd6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,8 +19,8 @@ RUN apk add --no-cache \ # Download go dependencies COPY go.mod go.sum ./ -RUN --mount=type=cache,target=/root/.cache/go-build \ - --mount=type=cache,target=/root/go/pkg/mod \ +RUN --mount=type=cache,target=/nonroot/.cache/go-build \ + --mount=type=cache,target=/nonroot/go/pkg/mod \ go mod download # Cosmwasm - Download correct libwasmvm version From 4f996c22ded5f34779be5d0aadb8057ee8aa6a38 Mon Sep 17 00:00:00 2001 From: freeelancer Date: Mon, 6 Jan 2025 15:37:22 +0800 Subject: [PATCH 10/14] update build-and-run-single-node --- Makefile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 77393398..9155c499 100644 --- a/Makefile +++ b/Makefile @@ -235,13 +235,14 @@ mocks: build-and-run-single-node: build @echo "Building and running a single node for testing..." @mkdir -p .mantrasinglenodetest - @if [ ! -f .mantrasinglenodetest/config.toml ]; then \ - ./build/mantrachaind init single-node-test --chain-id test-chain --home .mantrasinglenodetest; \ + @if [ ! -f .mantrasinglenodetest/config/config.toml ]; then \ + ./build/mantrachaind init single-node-test --chain-id test-chain --home .mantrasinglenodetest --default-denom uom; \ ./build/mantrachaind keys add validator --keyring-backend test --home .mantrasinglenodetest; \ - ./build/mantrachaind genesis add-genesis-account $$(./build/mantrachaind keys show validator -a --keyring-backend test --home .mantrasinglenodetest) 100000000stake --home .mantrasinglenodetest; \ - ./build/mantrachaind genesis gentx validator 100000000stake --chain-id test-chain --keyring-backend test --home .mantrasinglenodetest; \ + ./build/mantrachaind genesis add-genesis-account $$(./build/mantrachaind keys show validator -a --keyring-backend test --home .mantrasinglenodetest) 100000000000000uom --home .mantrasinglenodetest; \ + ./build/mantrachaind genesis gentx validator 100000000uom --chain-id test-chain --keyring-backend test --home .mantrasinglenodetest; \ ./build/mantrachaind genesis collect-gentxs --home .mantrasinglenodetest; \ + sed -i'' -e 's/"fee_denom": "stake"/"fee_denom": "uom"/' .mantrasinglenodetest/config/genesis.json; \ fi - ./build/mantrachaind start --home .mantrasinglenodetest --minimum-gas-prices 0stake + ./build/mantrachaind start --home .mantrasinglenodetest --minimum-gas-prices 0uom .PHONY: build-and-run-single-node From 0865c6a0fd5cd5e9157cebade2d0eb4273c1ee26 Mon Sep 17 00:00:00 2001 From: allthatjazzleo Date: Mon, 6 Jan 2025 16:30:38 +0800 Subject: [PATCH 11/14] print SDK_VERSION --- .github/workflows/tests-sdk.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests-sdk.yml b/.github/workflows/tests-sdk.yml index e8a09452..fb0d2690 100644 --- a/.github/workflows/tests-sdk.yml +++ b/.github/workflows/tests-sdk.yml @@ -23,6 +23,7 @@ jobs: SDK_VERSION=$FULL_VERSION fi echo "SDK_VERSION=${VERSION}" >> $GITHUB_ENV + echo "SDK_VERSION=${SDK_VERSION}" - name: Checkout MANTRA-Chain/cosmos-sdk uses: actions/checkout@v4 with: From 88aa810338a7c39ca6e8d7339d7d98a2a98b8182 Mon Sep 17 00:00:00 2001 From: allthatjazzleo Date: Mon, 6 Jan 2025 16:35:06 +0800 Subject: [PATCH 12/14] fix SDK_VERSION --- .github/workflows/tests-sdk.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/tests-sdk.yml b/.github/workflows/tests-sdk.yml index fb0d2690..5a70811c 100644 --- a/.github/workflows/tests-sdk.yml +++ b/.github/workflows/tests-sdk.yml @@ -22,8 +22,7 @@ jobs: # This is a regular version tag SDK_VERSION=$FULL_VERSION fi - echo "SDK_VERSION=${VERSION}" >> $GITHUB_ENV - echo "SDK_VERSION=${SDK_VERSION}" + echo "SDK_VERSION=${SDK_VERSION}" | tee -a $GITHUB_ENV - name: Checkout MANTRA-Chain/cosmos-sdk uses: actions/checkout@v4 with: From 9a6cc1c3c47a0b290636b252838a459b91300ec9 Mon Sep 17 00:00:00 2001 From: allthatjazzleo Date: Mon, 6 Jan 2025 16:55:22 +0800 Subject: [PATCH 13/14] skip test on tools fo sdk --- .github/workflows/tests-sdk.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests-sdk.yml b/.github/workflows/tests-sdk.yml index 5a70811c..bcc7d2a5 100644 --- a/.github/workflows/tests-sdk.yml +++ b/.github/workflows/tests-sdk.yml @@ -32,5 +32,8 @@ jobs: - name: Install tparse run: | go install github.com/mfridman/tparse@latest - - run: make test-all + - run: | + # skip test on tools + rm -rf tools + make test-all working-directory: cosmos-sdk From 5a0f81bbfd8b82b34cc37db64cfc8d839b84995b Mon Sep 17 00:00:00 2001 From: freeelancer Date: Mon, 6 Jan 2025 17:03:33 +0800 Subject: [PATCH 14/14] add name --- .github/workflows/tests-sdk.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests-sdk.yml b/.github/workflows/tests-sdk.yml index bcc7d2a5..34fb0a9e 100644 --- a/.github/workflows/tests-sdk.yml +++ b/.github/workflows/tests-sdk.yml @@ -32,7 +32,8 @@ jobs: - name: Install tparse run: | go install github.com/mfridman/tparse@latest - - run: | + - name: Run all tests + run: | # skip test on tools rm -rf tools make test-all