diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index fd385df..c7854b5 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -65,6 +65,7 @@ jobs: - "ictest-poa" - "ictest-tokenfactory" - "ictest-manifest" + - "ictest-group-poa" fail-fast: false steps: diff --git a/Makefile b/Makefile index bf1f704..e7be52d 100644 --- a/Makefile +++ b/Makefile @@ -131,6 +131,8 @@ ictest-manifest: ictest-poa: cd interchaintest && go test -race -v -run TestPOA . -count=1 +ictest-group-poa: + cd interchaintest && go test -race -v -run TestGroupPOA . -count=1 .PHONY: ictest-ibc ictest-tokenfactory @@ -174,7 +176,7 @@ coverage: ## Run coverage report @echo "--> Cleaning up coverage files, if any" @rm -rf /tmp/manifest-ledger-coverage/* @echo "--> Running coverage" - @go test -race -covermode=atomic -v -cpu=$$(nproc) -cover $$(go list ./...) ./interchaintest/... -coverpkg=github.com/liftedinit/manifest-ledger/... -args -test.gocoverdir="/tmp/manifest-ledger-coverage" > /dev/null 2>&1 + @go test -timeout 30m -race -covermode=atomic -v -cpu=$$(nproc) -cover $$(go list ./...) ./interchaintest/... -coverpkg=github.com/liftedinit/manifest-ledger/... -args -test.gocoverdir="/tmp/manifest-ledger-coverage" > /dev/null 2>&1 @echo "--> Converting binary coverage report to text format" @go tool covdata textfmt -i=/tmp/manifest-ledger-coverage -o coverage.out @echo "--> Filtering coverage report" diff --git a/interchaintest/go.mod b/interchaintest/go.mod index 7c4326a..3201529 100644 --- a/interchaintest/go.mod +++ b/interchaintest/go.mod @@ -13,11 +13,14 @@ replace ( require ( cosmossdk.io/math v1.3.0 + cosmossdk.io/x/upgrade v0.1.2 + github.com/cockroachdb/errors v1.11.1 github.com/cosmos/cosmos-sdk v0.50.6 + github.com/cosmos/gogoproto v1.4.12 github.com/cosmos/ibc-go/v8 v8.2.1 github.com/docker/docker v24.0.9+incompatible github.com/liftedinit/manifest-ledger v0.0.0-00000000000000-000000000000 - github.com/strangelove-ventures/interchaintest/v8 v8.2.0 + github.com/strangelove-ventures/interchaintest/v8 v8.3.0 github.com/strangelove-ventures/poa v0.50.1 github.com/strangelove-ventures/tokenfactory v0.50.1 github.com/stretchr/testify v1.9.0 @@ -38,9 +41,9 @@ require ( cosmossdk.io/errors v1.0.1 // indirect cosmossdk.io/log v1.3.1 // indirect cosmossdk.io/store v1.1.0 // indirect + cosmossdk.io/x/evidence v0.1.1 // indirect cosmossdk.io/x/feegrant v0.1.1 // indirect cosmossdk.io/x/tx v0.13.3 // indirect - cosmossdk.io/x/upgrade v0.1.2 // 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 @@ -64,7 +67,7 @@ require ( github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chzyer/readline v1.5.1 // indirect - github.com/cockroachdb/errors v1.11.1 // indirect + github.com/cockroachdb/apd/v2 v2.0.2 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/pebble v1.1.0 // indirect github.com/cockroachdb/redact v1.1.5 // indirect @@ -76,10 +79,10 @@ require ( 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.4.12 // indirect github.com/cosmos/iavl v1.1.2 // indirect github.com/cosmos/ibc-go/modules/capability v1.0.0 // indirect github.com/cosmos/ics23/go v0.10.0 // indirect + github.com/cosmos/interchain-security/v5 v5.0.0-alpha1.0.20240424193412-7cd900ad2a74 // indirect github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect github.com/danieljoos/wincred v1.2.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -97,7 +100,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/dvsekhvalnov/jose2go v1.7.0 // indirect github.com/emicklei/dot v1.6.2 // indirect - github.com/ethereum/go-ethereum v1.13.14 // indirect + github.com/ethereum/go-ethereum v1.13.15 // indirect github.com/fatih/color v1.16.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -140,6 +143,7 @@ require ( github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/hdevalence/ed25519consensus v0.2.0 // indirect @@ -152,7 +156,6 @@ require ( github.com/ipfs/go-cid v0.4.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect - github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/klauspost/compress v1.17.8 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/kr/pretty v0.3.1 // indirect @@ -181,9 +184,9 @@ require ( github.com/multiformats/go-multicodec v0.9.0 // indirect github.com/multiformats/go-multihash v0.2.3 // indirect github.com/multiformats/go-varint v0.0.7 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect github.com/oklog/run v1.1.0 // indirect - github.com/onsi/gomega v1.27.10 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc5 // indirect github.com/pelletier/go-toml v1.9.5 // indirect @@ -247,18 +250,16 @@ require ( google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.5.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect - lukechampine.com/uint128 v1.2.0 // indirect - modernc.org/cc/v3 v3.40.0 // indirect - modernc.org/ccgo/v3 v3.16.13 // indirect - modernc.org/libc v1.29.0 // indirect + modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect + modernc.org/libc v1.41.0 // indirect modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.7.2 // indirect - modernc.org/opt v0.1.3 // indirect - modernc.org/sqlite v1.28.0 // indirect - modernc.org/strutil v1.1.3 // indirect + modernc.org/sqlite v1.29.5 // indirect + modernc.org/strutil v1.2.0 // indirect modernc.org/token v1.1.0 // indirect nhooyr.io/websocket v1.8.11 // indirect pgregory.net/rapid v1.1.0 // indirect diff --git a/interchaintest/go.sum b/interchaintest/go.sum index 540b3cf..9000897 100644 --- a/interchaintest/go.sum +++ b/interchaintest/go.sum @@ -340,8 +340,8 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= -github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/datadriven v1.0.3-0.20230801171734-e384cf455877 h1:1MLK4YpFtIEo3ZtMA5C795Wtv5VuUnrXX7mQG+aHg6o= +github.com/cockroachdb/datadriven v1.0.3-0.20230801171734-e384cf455877/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= @@ -387,6 +387,8 @@ github.com/cosmos/ibc-go/v8 v8.2.1 h1:MTsnZZjxvGD4Fv5pYyx5UkELafSX0rlPt6IfsE2BpT github.com/cosmos/ibc-go/v8 v8.2.1/go.mod h1:wj3qx75iC/XNnsMqbPDCIGs0G6Y3E/lo3bdqCyoCy+8= github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= github.com/cosmos/ics23/go v0.10.0/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0= +github.com/cosmos/interchain-security/v5 v5.0.0-alpha1.0.20240424193412-7cd900ad2a74 h1:6atU/xizTL10q6EprP7oRuvfgUP2F6puvutnVoE+FRc= +github.com/cosmos/interchain-security/v5 v5.0.0-alpha1.0.20240424193412-7cd900ad2a74/go.mod h1:h/RkwOppo5AJj+1pkQyfjqU1MPdpohD/S6oEeAXpGZY= github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM= github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -455,8 +457,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.13.14 h1:EwiY3FZP94derMCIam1iW4HFVrSgIcpsu0HwTQtm6CQ= -github.com/ethereum/go-ethereum v1.13.14/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU= +github.com/ethereum/go-ethereum v1.13.15 h1:U7sSGYGo4SPjP6iNIifNoyIAiNjrmQkz6EwQG+/EZWo= +github.com/ethereum/go-ethereum v1.13.15/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= @@ -708,6 +710,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -761,8 +765,6 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= @@ -813,8 +815,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= -github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= @@ -876,6 +878,8 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= @@ -918,6 +922,8 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= 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/oxyno-zeta/gomock-extra-matcher v1.2.0 h1:WPEclU0y0PMwUzdDcaKZvld4aXpa3fkzjiUMQdcBEHg= +github.com/oxyno-zeta/gomock-extra-matcher v1.2.0/go.mod h1:S0r7HmKeCGsHmvIVFMjKWwswb4+30nCNWbXRMBVPkaU= 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= @@ -1039,8 +1045,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= -github.com/strangelove-ventures/interchaintest/v8 v8.2.0 h1:EZXPvZXL1y/kvh9XI04A2stL+2UMvykhNUv28euRnL8= -github.com/strangelove-ventures/interchaintest/v8 v8.2.0/go.mod h1:pupV0YN3A56/u9kHj9U1F8MdDUEolBIn05F0W1q/0oI= +github.com/strangelove-ventures/interchaintest/v8 v8.3.0 h1:1XyATc0JkvzDhBS71CdpM5z1t6bmO9PgH/5/nffoM9Q= +github.com/strangelove-ventures/interchaintest/v8 v8.3.0/go.mod h1:5goHQtgWO9khoUO/SfR//w3uaw/uYVriDR1OY6PyydM= github.com/strangelove-ventures/poa v0.50.1 h1:84OjmMrJEvQxfNKsgUW4aCeeqTqA0dswCAF7J+017Dg= github.com/strangelove-ventures/poa v0.50.1/go.mod h1:Jmdk38G8DN3rrvPfQwVF60uUzVmn4Q7k2N8Q5eSRb6Y= github.com/strangelove-ventures/tokenfactory v0.50.1 h1:4P+wux9HEk+apmT3nrKsuCvPbWFNOHApaYYsqX5/zY8= @@ -1139,6 +1145,8 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.2.0 h1:TaP3xedm7JaAgScZO7tlvlKrqT0p7I6OsdGB5YNSMDU= +go.uber.org/mock v0.2.0/go.mod h1:J0y0rp9L3xiff1+ZBfKxlC1fz2+aO16tw0tsDOixfuM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -1782,34 +1790,22 @@ launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbc launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= -lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= -modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= -modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= -modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= -modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v1.29.0 h1:tTFRFq69YKCF2QyGNuRUQxKBm1uZZLubf6Cjh/pVHXs= -modernc.org/libc v1.29.0/go.mod h1:DaG/4Q3LRRdqpiLyP0C2m1B8ZMGkQ+cCgOIjEtQlYhQ= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk= +modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= -modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= -modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= -modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= -modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c= +modernc.org/sqlite v1.29.5 h1:8l/SQKAjDtZFo9lkJLdk8g9JEOeYRG4/ghStDCCTiTE= +modernc.org/sqlite v1.29.5/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= -modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0= nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= diff --git a/interchaintest/helpers/group.go b/interchaintest/helpers/group.go new file mode 100644 index 0000000..f4a7f88 --- /dev/null +++ b/interchaintest/helpers/group.go @@ -0,0 +1,140 @@ +package helpers + +import ( + "context" + "encoding/json" + "path" + "strconv" + "testing" + + "github.com/cockroachdb/errors" + "github.com/cosmos/cosmos-sdk/x/group" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/dockerutil" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/strangelove-ventures/interchaintest/v8/testutil" + "github.com/stretchr/testify/require" +) + +// SubmitGroupProposal submits a group proposal to the chain. +// TODO: This function should be part of `interchaintest` +// See https://github.com/strangelove-ventures/interchaintest/issues/1138 +func SubmitGroupProposal(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain, config *ibc.ChainConfig, keyName string, prop *group.MsgSubmitProposal) (string, error) { + file := "proposal.json" + propJson, err := json.MarshalIndent(prop, "", " ") + require.NoError(t, err) + + tn := chain.GetNode() + + fw := dockerutil.NewFileWriter(nil, tn.DockerClient, tn.TestName) + err = fw.WriteFile(ctx, tn.VolumeName, file, propJson) + require.NoError(t, err) + + submitCommand := []string{ + "group", "submit-proposal", + path.Join(tn.HomeDir(), file), "--gas", "auto", + } + + return exec(ctx, chain, config, tn.TxCommand(keyName, submitCommand...)) +} + +//// QueryGroupProposal queries a group proposal on the chain. +//// TODO: This function should be part of `interchaintest` +//// See https://github.com/strangelove-ventures/interchaintest/issues/1138 +//func QueryGroupProposal(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain, config *ibc.ChainConfig, proposalId string) (string, error) { +// query := []string{ +// "group", "proposal", proposalId, +// } +// +// tn := chain.GetNode() +// +// o, _, err := tn.Exec(ctx, tn.QueryCommand(query...), config.Env) +// if err != nil { +// return "", errors.WithMessage(err, "failed to query group proposal") +// } +// +// var data interface{} +// if err := json.Unmarshal([]byte(o), &data); err != nil { +// return "", errors.WithMessage(err, "failed to unmarshal group proposal") +// +// } +// +// prettyJSON, err := json.MarshalIndent(data, "", " ") +// if err != nil { +// return "", errors.WithMessage(err, "failed to marshal group proposal") +// } +// +// return string(prettyJSON), nil +//} + +// VoteGroupProposal votes on a group proposal on the chain. +// TODO: This function should be part of `interchaintest` +// See https://github.com/strangelove-ventures/interchaintest/issues/1138 +func VoteGroupProposal(ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, proposalId, accAddr, vote, metadata string) (string, error) { + voteCommand := []string{ + "group", "vote", proposalId, accAddr, vote, metadata, + } + return exec(ctx, chain, config, chain.GetNode().TxCommand(accAddr, voteCommand...)) +} + +func ExecGroupProposal(ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, accAddr, proposalId string) (string, error) { + execCommand := []string{ + "group", "exec", proposalId, + } + return exec(ctx, chain, config, chain.GetNode().TxCommand(accAddr, execCommand...)) +} + +func exec(ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, command []string) (string, error) { + tn := chain.GetNode() + + o, _, err := tn.Exec(ctx, command, config.Env) + if err != nil { + return "", errors.WithMessage(err, "failed to execute group proposal") + } + + output := cosmos.CosmosTx{} + if err := json.Unmarshal([]byte(o), &output); err != nil { + return "", errors.WithMessage(err, "failed to unmarshal group proposal") + } + + if err := testutil.WaitForBlocks(ctx, 3, tn); err != nil { + return "", errors.WithMessage(err, "failed to wait for blocks") + } + + txResp, err := chain.GetTransaction(output.TxHash) + if err != nil { + return "", errors.WithMessage(err, "failed to get transaction") + } + + if txResp.Code != 0 { + return "", errors.Errorf("failed to execute group proposal: %s", txResp.RawLog) + } + + // The transaction itself can be successful but the proposal can fail + // Check for proposal execution failure + var logs string + success := true + expectedProposalResult := strconv.Quote(group.PROPOSAL_EXECUTOR_RESULT_SUCCESS.String()) + for _, event := range txResp.Events { + if event.GetType() != "cosmos.group.v1.EventExec" { + continue + } + for _, attr := range event.GetAttributes() { + switch attr.Key { + case "logs": + logs = attr.Value + case "result": + if attr.Value != expectedProposalResult { + success = false + } + } + } + } + + // The proposal failed, return the logs + if !success { + return "", errors.Newf("failed to execute group proposal: %s", logs) + } + + return txResp.TxHash, nil +} diff --git a/interchaintest/helpers/poa.go b/interchaintest/helpers/poa.go index ff00d63..6a7f9c0 100644 --- a/interchaintest/helpers/poa.go +++ b/interchaintest/helpers/poa.go @@ -3,14 +3,18 @@ package helpers import ( "context" "fmt" - "testing" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v8/ibc" ) -func POASetPower(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, valoper string, power int64, flags ...string) (sdk.TxResponse, error) { +func POASetPower(ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, valoper string, power int64, flags ...string) (sdk.TxResponse, error) { cmd := TxCommandBuilder(ctx, chain, []string{"tx", "poa", "set-power", valoper, fmt.Sprintf("%d", power)}, user.KeyName(), flags...) return ExecuteTransaction(ctx, chain, cmd) } + +func POAUpdateParams(ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, admins string, allowValidatorSelfExit bool, flags ...string) (sdk.TxResponse, error) { + cmd := TxCommandBuilder(ctx, chain, []string{"tx", "poa", "update-params", admins, fmt.Sprintf("%t", allowValidatorSelfExit)}, user.KeyName(), flags...) + return ExecuteTransaction(ctx, chain, cmd) +} diff --git a/interchaintest/ibc_test.go b/interchaintest/ibc_test.go index a285930..6920df5 100644 --- a/interchaintest/ibc_test.go +++ b/interchaintest/ibc_test.go @@ -19,7 +19,6 @@ import ( ) func TestIBC(t *testing.T) { - t.Parallel() ctx := context.Background() // Same as ChainNode.HomeDir() but we need it before the chain is created diff --git a/interchaintest/mainfest_test.go b/interchaintest/mainfest_test.go index 4c2cf3f..5eaae91 100644 --- a/interchaintest/mainfest_test.go +++ b/interchaintest/mainfest_test.go @@ -21,8 +21,6 @@ import ( ) func TestManifestModule(t *testing.T) { - t.Parallel() - ctx := context.Background() // Same as ChainNode.HomeDir() but we need it before the chain is created diff --git a/interchaintest/poa_group_test.go b/interchaintest/poa_group_test.go new file mode 100644 index 0000000..056184e --- /dev/null +++ b/interchaintest/poa_group_test.go @@ -0,0 +1,735 @@ +package interchaintest + +import ( + "context" + "fmt" + "path" + "strconv" + "testing" + "time" + + sdkmath "cosmossdk.io/math" + upgradetypes "cosmossdk.io/x/upgrade/types" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + grouptypes "github.com/cosmos/cosmos-sdk/x/group" + "github.com/cosmos/gogoproto/proto" + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + poatypes "github.com/strangelove-ventures/poa" + tokenfactorytypes "github.com/strangelove-ventures/tokenfactory/x/tokenfactory/types" + "github.com/stretchr/testify/require" + + "github.com/liftedinit/manifest-ledger/interchaintest/helpers" + manifesttypes "github.com/liftedinit/manifest-ledger/x/manifest/types" +) + +const ( + groupAddr = "manifest1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsfmy9qj" + planName = "foobar" + planHeight int64 = 200 + metadata = "AQ==" + tfDenom = "foo" + tfTicker = "FOO" + user1 = "user1" + user2 = "user2" +) + +var ( + groupInfo = grouptypes.GroupInfo{ + Id: 1, + Admin: groupAddr, // The Group Policy is the admin of the Group (--group-policy-as-admin) + Metadata: metadata, + Version: 2, + TotalWeight: "2", + CreatedAt: time.Now(), + } + + member1 = createMember(accAddr, "1", user1) + member2 = createMember(acc2Addr, "1", user2) + + groupMember1 = createGroupMember(1, &member1) + groupMember2 = createGroupMember(1, &member2) + + groupPolicy = createGroupPolicyInfo(groupAddr, 1, "policy metadata") + + tfFullDenom = fmt.Sprintf("factory/%s/%s", groupAddr, tfDenom) + + upgradeProposal = createUpgradeProposal(groupAddr, planName, planHeight) + cancelUpgradeProposal = createCancelUpgradeProposal(groupAddr) + manifestUpdateProposal = createManifestUpdateProposal(groupAddr, + createManifestParams( + createInflation(Denom, 200_000_000, false), + []*manifesttypes.StakeHolders{ + createStakeHolders(acc3Addr, 50_000_000), + createStakeHolders(acc4Addr, 50_000_000), + }, + ), + ) + manifestDefaultProposal = createManifestUpdateProposal(groupAddr, + createManifestParams( + createInflation(Denom, 0, false), + []*manifesttypes.StakeHolders{ + createStakeHolders(acc2Addr, 100_000_000), + }, + ), + ) + manifestPayoutProposal = createManifestPayoutProposal(groupAddr, sdk.NewInt64Coin(Denom, 50)) + manifestBurnProposal = createManifestBurnProposal(groupAddr, sdk.NewCoins(sdk.NewInt64Coin(Denom, 50))) + poaDefaultParams = createPOAParams([]string{groupAddr}, true) + bankSendProposal = createBankSendProposal(groupAddr, accAddr, sdk.NewInt64Coin(Denom, 1)) + tfCreateProposal = createTfCreateDenomProposal(groupAddr, tfDenom) + tfMintProposal = createTfMintProposal(groupAddr, sdk.NewInt64Coin(tfFullDenom, 1234), "") + tfMintToProposal = createTfMintProposal(groupAddr, sdk.NewInt64Coin(tfFullDenom, 4321), accAddr) + tfBurnProposal = createTfBurnProposal(groupAddr, sdk.NewInt64Coin(tfFullDenom, 1234), "") + tfBurnFromProposal = createTfBurnProposal(groupAddr, sdk.NewInt64Coin(tfFullDenom, 4321), accAddr) + tfForceTransferProposal = createTfForceTransferProposal(groupAddr, sdk.NewInt64Coin(tfFullDenom, 1), accAddr, acc2Addr) + tfChangeAdminProposal = createTfChangeAdminProposal(groupAddr, tfFullDenom, accAddr) + tfModifyProposal = createTfModifyMetadataProposal(groupAddr, tfFullDenom, tfFullDenom, tfTicker, tfFullDenom, tfTicker, "The foo token description") + proposalId = 1 +) + +func TestGroupPOA(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + + // Same as ChainNode.HomeDir() but we need it before the chain is created + // The node volume is always mounted at /var/cosmos-chain/[chain-name] + // This is a hackish way to get the coverage files from the ephemeral containers + name := "group-poa" + internalGoCoverDir := path.Join("/var/cosmos-chain", name) + + err := groupPolicy.SetDecisionPolicy(createThresholdDecisionPolicy("1", 10*time.Second, 0*time.Second)) + require.NoError(t, err) + + // TODO: The following block is needed in order for the GroupPolicy to get properly serialized in the ModifyGenesis function + // https://github.com/strangelove-ventures/interchaintest/issues/1138 + enc := AppEncoding() + grouptypes.RegisterInterfaces(enc.InterfaceRegistry) + cdc := codec.NewProtoCodec(enc.InterfaceRegistry) + _, err = cdc.MarshalJSON(groupPolicy) + require.NoError(t, err) + + groupGenesis := createGroupGenesis() + + cfgA := LocalChainConfig + cfgA.ModifyGenesis = cosmos.ModifyGenesis(groupGenesis) + cfgA.Env = []string{ + fmt.Sprintf("GOCOVERDIR=%s", internalGoCoverDir), + fmt.Sprintf("POA_ADMIN_ADDRESS=%s", groupAddr), // This is required in order for GetPoAAdmin to return the Group address + } + + // setup base chain + chains := interchaintest.CreateChainWithConfig(t, numVals, numNodes, name, "", cfgA) + chain := chains[0].(*cosmos.CosmosChain) + + ctx, _, client, _ := interchaintest.BuildInitialChain(t, chains, false) + + user1Wallet, err := interchaintest.GetAndFundTestUserWithMnemonic(ctx, user1, accMnemonic, DefaultGenesisAmt, chain) + require.NoError(t, err) + _, err = interchaintest.GetAndFundTestUserWithMnemonic(ctx, user2, acc1Mnemonic, DefaultGenesisAmt, chain) + require.NoError(t, err) + + // Make sure the chain's HomeDir and the GOCOVERDIR are the same + require.Equal(t, internalGoCoverDir, chain.GetNode().HomeDir()) + + // Software Upgrade + testSoftwareUpgrade(t, ctx, chain, &cfgA, accAddr) + // Manifest module + testManifestParamsUpdate(t, ctx, chain, &cfgA, accAddr) + testManifestParamsUpdateWithInflation(t, ctx, chain, &cfgA, accAddr) + testManifestParamsUpdateEmpty(t, ctx, chain, &cfgA, accAddr) + testManifestStakeholdersPayout(t, ctx, chain, &cfgA, accAddr) + // POA Update + testPOAParamsUpdateEmpty(t, ctx, chain, &cfgA, accAddr) + testPOAParamsUpdate(t, ctx, chain, &cfgA, accAddr, user1Wallet) + // TokenFactory + testTokenCreate(t, ctx, chain, &cfgA, accAddr) + // Bank + testBankSend(t, ctx, chain, &cfgA, accAddr) + testBankSendIllegal(t, ctx, chain, &cfgA, accAddr) + + t.Cleanup(func() { + // Copy coverage files from the container + CopyCoverageFromContainer(ctx, t, client, chain.GetNode().ContainerID(), chain.HomeDir()) + }) +} + +// testSoftwareUpgrade tests the submission, voting, and execution of a software upgrade proposal +// The software upgrade plan is set and then cancelled +func testSoftwareUpgrade(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, accAddr string) { + t.Log("\n===== TEST GROUP SOFTWARE UPGRADE =====") + verifyUpgradePlanIsNil(t, ctx, chain) + verifyUpgradeAuthority(t, ctx, chain, groupAddr) + + // Set the upgrade plan + createAndRunProposalSuccess(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &upgradeProposal)}) + verifyUpgradePlan(t, ctx, chain, &upgradetypes.Plan{Name: planName, Height: planHeight}) + + // Cancel the upgrade + createAndRunProposalSuccess(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &cancelUpgradeProposal)}) + verifyUpgradePlanIsNil(t, ctx, chain) +} + +// testManifestParamsUpdate tests the submission, voting, and execution of a manifest params update proposal +// This proposal tests for https://github.com/liftedinit/manifest-ledger/issues/61 +// `nil` inflation parameter is allowed and should be handled correctly +func testManifestParamsUpdate(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, accAddr string) { + t.Log("\n===== TEST GROUP MANIFEST PARAMS UPDATE (NIL INFLATION PARAMETER - FAIL) =====") + t.Log("\n===== TEST FIX FOR https://github.com/liftedinit/manifest-ledger/issues/61 =====") + newProposal := manifestDefaultProposal + newProposal.Params.Inflation = nil + + checkManifestParams(ctx, t, chain, &manifestDefaultProposal.Params) + createAndRunProposalFailure(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &newProposal)}, manifesttypes.ErrInflationParamsNotSet.Error()) + checkManifestParams(ctx, t, chain, &manifestDefaultProposal.Params) +} + +// testManifestParamsUpdateWithInflation tests the submission, voting, and execution of a manifest params update proposal +func testManifestParamsUpdateWithInflation(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, accAddr string) { + t.Log("\n===== TEST GROUP MANIFEST PARAMS UPDATE =====") + checkManifestParams(ctx, t, chain, &manifestDefaultProposal.Params) + createAndRunProposalSuccess(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &manifestUpdateProposal)}) + checkManifestParams(ctx, t, chain, &manifestUpdateProposal.Params) + resetManifestParams(t, ctx, chain, config, accAddr) +} + +// testManifestParamsUpdateEmpty tests the submission, voting, and execution of an empty manifest params update proposal +func testManifestParamsUpdateEmpty(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, accAddr string) { + t.Log("\n===== TEST GROUP MANIFEST PARAMS UPDATE (EMPTY PARAM - FAIL) =====") + checkManifestParams(ctx, t, chain, &manifestDefaultProposal.Params) + manifestUpdateEmptyProposal := createManifestUpdateProposal(groupAddr, manifesttypes.Params{}) + createAndRunProposalFailure(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &manifestUpdateEmptyProposal)}, manifesttypes.ErrInflationParamsNotSet.Error()) + checkManifestParams(ctx, t, chain, &manifestDefaultProposal.Params) +} + +// testManifestStakeholdersPayout tests the submission, voting, and execution of a manifest stakeholders payout proposal. +// The stakeholders are paid out and the newly minted tokens are burned +func testManifestStakeholdersPayout(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, accAddr string) { + t.Log("\n===== TEST GROUP MANIFEST STAKEHOLDERS PAYOUT (MINT) & BURN =====") + // Verify the initial balances + verifyBalance(t, ctx, chain, accAddr, Denom, DefaultGenesisAmt) + verifyBalance(t, ctx, chain, groupAddr, Denom, sdkmath.ZeroInt()) + + createAndRunProposalSuccess(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &manifestUpdateProposal)}) + verifyManifestStakeholders(t, ctx, chain, []*manifesttypes.StakeHolders{createStakeHolders(acc3Addr, 50_000_000), createStakeHolders(acc4Addr, 50_000_000)}) + + // Stakeholders payout + createAndRunProposalSuccess(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &manifestPayoutProposal)}) + verifyBalance(t, ctx, chain, acc3Addr, Denom, sdkmath.NewInt(25)) + verifyBalance(t, ctx, chain, acc4Addr, Denom, sdkmath.NewInt(25)) + + buildWallet(t, ctx, chain, acc3Addr, acc3Mnemonic) + buildWallet(t, ctx, chain, acc4Addr, acc4Mnemonic) + + // Send back the funds to the Group address + sendFunds(t, ctx, chain, acc3Addr, groupAddr, Denom, sdkmath.NewInt(25)) + verifyBalance(t, ctx, chain, acc3Addr, Denom, sdkmath.ZeroInt()) + + sendFunds(t, ctx, chain, acc4Addr, groupAddr, Denom, sdkmath.NewInt(25)) + verifyBalance(t, ctx, chain, acc4Addr, Denom, sdkmath.ZeroInt()) + + // Burn the newly minted tokens using a Group Proposal + createAndRunProposalSuccess(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &manifestBurnProposal)}) + verifyBalance(t, ctx, chain, accAddr, Denom, DefaultGenesisAmt) + verifyBalance(t, ctx, chain, acc3Addr, Denom, sdkmath.ZeroInt()) + verifyBalance(t, ctx, chain, acc4Addr, Denom, sdkmath.ZeroInt()) + verifyBalance(t, ctx, chain, groupAddr, Denom, sdkmath.ZeroInt()) +} + +// testPOAParamsUpdateEmpty tests the submission, voting, and execution of an empty POA params update proposal +// This proposal tests that the Admins field cannot be empty +func testPOAParamsUpdateEmpty(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, accAddr string) { + t.Log("\n===== TEST GROUP POA PARAMS UPDATE (EMPTY ADMINS - FAIL) =====") + poaUpdateProposal := createPOAUpdateParams(groupAddr, createPOAParams(nil, false)) + createAndRunProposalFailure(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, poaUpdateProposal)}, poatypes.ErrMustProvideAtLeastOneAddress.Error()) + checkPOAParams(ctx, t, chain, &poaDefaultParams) +} + +// testPOAParamsUpdate tests the submission, voting, and execution of a POA params update proposal +func testPOAParamsUpdate(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, accAddr string, user ibc.Wallet) { + t.Log("\n===== TEST GROUP POA PARAMS UPDATE =====") + poaUpdateProposal := createPOAUpdateParams(groupAddr, createPOAParams([]string{accAddr}, false)) + createAndRunProposalSuccess(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, poaUpdateProposal)}) + checkPOAParams(ctx, t, chain, &poaUpdateProposal.Params) + + // NOTE: + // At this point, the POA_ADMIN_ADDRESS is still the Group address, but the POA module admin field is now `accAddr` + // What this means is that the POA_ADMIN_ADDRESS is the authority for all CosmosSDK modules, including the Group module, + // but the POA module admin field is the authority for the POA module itself. + + // Reset the POA Admin back to the Group address using the POA module admin field, i.e., `accAddr` + // Resetting the POA Admin back to the Group address using a group proposal will NOT work + updatePOAParams(t, ctx, chain, user, groupAddr, true) + checkPOAParams(ctx, t, chain, &poaDefaultParams) +} + +// testBankSend tests the sending of funds from one account to another using a group proposal +func testBankSend(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, accAddr string) { + t.Log("\n===== TEST GROUP BANK SEND =====") + + // Verify the initial balances + verifyBalance(t, ctx, chain, accAddr, Denom, DefaultGenesisAmt) + verifyBalance(t, ctx, chain, groupAddr, Denom, sdkmath.ZeroInt()) + + // Send funds from accAddr to groupAddr + sendFunds(t, ctx, chain, accAddr, groupAddr, Denom, sdkmath.NewInt(1)) + verifyBalance(t, ctx, chain, accAddr, Denom, sdkmath.NewInt(DefaultGenesisAmt.Int64()-1)) + verifyBalance(t, ctx, chain, groupAddr, Denom, sdkmath.OneInt()) + + // Send funds from groupAddr back to accAddr using a Group Proposal + createAndRunProposalSuccess(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &bankSendProposal)}) + verifyBalance(t, ctx, chain, accAddr, Denom, DefaultGenesisAmt) + verifyBalance(t, ctx, chain, groupAddr, Denom, sdkmath.ZeroInt()) +} + +func testBankSendIllegal(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, accAddr string) { + t.Log("\n===== TEST GROUP BANK SEND (INVALID SENDER - FAIL) =====") + newProp := bankSendProposal + newProp.FromAddress = accAddr + newProp.ToAddress = acc2Addr + + // Verify initial balances + verifyBalance(t, ctx, chain, accAddr, Denom, DefaultGenesisAmt) + verifyBalance(t, ctx, chain, acc2Addr, Denom, DefaultGenesisAmt) + + // Send funds from groupAddr back to accAddr using a Group Proposal + createAndRunProposalFailure(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &newProp)}, "msg does not have group policy authorization") + + // Verify the funds were not sent + verifyBalance(t, ctx, chain, accAddr, Denom, DefaultGenesisAmt) + verifyBalance(t, ctx, chain, acc2Addr, Denom, DefaultGenesisAmt) +} + +// testTokenCreate tests the creation, modification, and admin transfer of a token using a group proposal +func testTokenCreate(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, accAddr string) { + t.Log("\n===== TEST GROUP TOKEN CREATION, MODIFICATION, MINT (-TO), BURN (-FROM), FORCE TRANSFER AND ADMIN CHANGE =====") + createAndRunProposalSuccess(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &tfCreateProposal)}) + verifyTfAdmin(t, ctx, chain, tfFullDenom, groupAddr) + + // Modify token metadata + createAndRunProposalSuccess(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &tfModifyProposal)}) + verifyBankDenomMetadata(t, ctx, chain, tfModifyProposal.Metadata) + + // Mint some token to groupAddr + createAndRunProposalSuccess(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &tfMintProposal)}) + verifyBalance(t, ctx, chain, groupAddr, tfFullDenom, tfMintProposal.Amount.Amount) + + // Burn the token using a Group Proposal + createAndRunProposalSuccess(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &tfBurnProposal)}) + verifyBalance(t, ctx, chain, groupAddr, tfFullDenom, sdkmath.ZeroInt()) + + // Mint some token to accAddr + createAndRunProposalSuccess(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &tfMintToProposal)}) + verifyBalance(t, ctx, chain, accAddr, tfFullDenom, tfMintToProposal.Amount.Amount) + + // Force transfer the token from accAddr to acc2Addr using a Group Proposal + createAndRunProposalSuccess(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &tfForceTransferProposal)}) + verifyBalance(t, ctx, chain, accAddr, tfFullDenom, sdkmath.NewInt(4320)) + verifyBalance(t, ctx, chain, acc2Addr, tfFullDenom, tfForceTransferProposal.Amount.Amount) + + // Send the token from acc2Addr to accAddr + sendFunds(t, ctx, chain, acc2Addr, accAddr, tfFullDenom, sdkmath.OneInt()) + + // Verify the token was sent + verifyBalance(t, ctx, chain, accAddr, tfFullDenom, sdkmath.NewInt(4321)) + verifyBalance(t, ctx, chain, acc2Addr, tfFullDenom, sdkmath.ZeroInt()) + + // Burn the token from accAddr using a Group Proposal + createAndRunProposalSuccess(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &tfBurnFromProposal)}) + verifyBalance(t, ctx, chain, accAddr, tfFullDenom, sdkmath.ZeroInt()) + + // Transfer the token to accAddr + createAndRunProposalSuccess(t, ctx, chain, config, accAddr, []*types.Any{createAny(t, &tfChangeAdminProposal)}) + verifyTfAdmin(t, ctx, chain, tfFullDenom, accAddr) +} + +func _createAndRunProposal(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, proposer string, proposalAny []*types.Any) error { + prop := createProposal(groupAddr, []string{proposer}, proposalAny, "Proposal", "Proposal") + return submitVoteAndExecProposal(ctx, t, chain, config, accAddr, prop) +} + +func createAndRunProposalSuccess(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, proposer string, proposalAny []*types.Any) { + err := _createAndRunProposal(t, ctx, chain, config, proposer, proposalAny) + require.NoError(t, err) +} + +func createAndRunProposalFailure(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, proposer string, proposalAny []*types.Any, expectedErr string) { + err := _createAndRunProposal(t, ctx, chain, config, proposer, proposalAny) + require.Error(t, err) + require.ErrorContains(t, err, expectedErr) +} + +// resetManifestParams resets the manifest params back to the default +func resetManifestParams(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, config *ibc.ChainConfig, accAddr string) { + manifestDefaultProposalAny := createAny(t, &manifestDefaultProposal) + prop := createProposal(groupAddr, []string{accAddr}, []*types.Any{manifestDefaultProposalAny}, "Manifest Params Update Proposal (reset)", "Reset the manifest params to the default") + err := submitVoteAndExecProposal(ctx, t, chain, config, accAddr, prop) + require.NoError(t, err) + + checkManifestParams(ctx, t, chain, &manifestDefaultProposal.Params) +} + +// checkManifestParams checks the manifest params against the expected params +func checkManifestParams(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain, expectedParams *manifesttypes.Params) { + resp, err := manifesttypes.NewQueryClient(chain.GetNode().GrpcConn).Params(ctx, &manifesttypes.QueryParamsRequest{}) + require.NoError(t, err) + require.NotNil(t, resp) + require.NotNil(t, resp.Params) + if expectedParams.Inflation != nil { + require.Equal(t, expectedParams.Inflation.MintDenom, resp.Params.Inflation.MintDenom) + require.Equal(t, expectedParams.Inflation.YearlyAmount, resp.Params.Inflation.YearlyAmount) + require.Equal(t, expectedParams.Inflation.AutomaticEnabled, resp.Params.Inflation.AutomaticEnabled) + } + require.Len(t, expectedParams.StakeHolders, len(resp.Params.StakeHolders)) + for i, sh := range expectedParams.StakeHolders { + require.Equal(t, sh.Address, resp.Params.StakeHolders[i].Address) + require.Equal(t, sh.Percentage, resp.Params.StakeHolders[i].Percentage) + } +} + +// checkPOAParams checks the POA params against the expected params +func checkPOAParams(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain, expectedParams *poatypes.Params) { + resp, err := poatypes.NewQueryClient(chain.GetNode().GrpcConn).Params(ctx, &poatypes.QueryParamsRequest{}) + require.NoError(t, err) + require.NotNil(t, resp) + require.NotNil(t, resp.Params) + require.Len(t, resp.Params.Admins, len(expectedParams.Admins)) + for i, admin := range expectedParams.Admins { + require.Equal(t, admin, resp.Params.Admins[i]) + } + require.Equal(t, expectedParams.AllowValidatorSelfExit, resp.Params.AllowValidatorSelfExit) +} + +// submitVoteAndExecProposal submits, votes, and executes a group proposal +func submitVoteAndExecProposal(ctx context.Context, t *testing.T, chain *cosmos.CosmosChain, config *ibc.ChainConfig, keyName string, prop *grouptypes.MsgSubmitProposal) error { + // Increment the proposal ID regardless of the outcome + defer func() { proposalId++ }() + + pid := strconv.Itoa(proposalId) + + marshalProposal(t, prop) + + _, err := helpers.SubmitGroupProposal(ctx, t, chain, config, keyName, prop) + if err != nil { + return err + } + _, err = helpers.VoteGroupProposal(ctx, chain, config, pid, keyName, grouptypes.VOTE_OPTION_YES.String(), metadata) + if err != nil { + return err + } + _, err = helpers.ExecGroupProposal(ctx, chain, config, keyName, pid) + if err != nil { + return err + } + + return nil +} + +// createProposal creates a group proposal +func createProposal(groupPolicyAddress string, proposers []string, messages []*types.Any, title string, summary string) *grouptypes.MsgSubmitProposal { + return &grouptypes.MsgSubmitProposal{ + GroupPolicyAddress: groupPolicyAddress, + Proposers: proposers, + Metadata: metadata, + Messages: messages, + Exec: 0, + Title: title, + Summary: summary, + } +} + +// createAny creates a types.Any from a proto.Message +func createAny(t *testing.T, msg proto.Message) *types.Any { + anyV, err := types.NewAnyWithValue(msg) + require.NoError(t, err) + return anyV +} + +// marshalProposal is a hackish way to ensure the prop is properly serialized +// TODO: The following block is needed in order for the prop to get properly serialized +// https://github.com/strangelove-ventures/interchaintest/issues/1138 +func marshalProposal(t *testing.T, prop *grouptypes.MsgSubmitProposal) { + enc := AppEncoding() + grouptypes.RegisterInterfaces(enc.InterfaceRegistry) + cdc := codec.NewProtoCodec(enc.InterfaceRegistry) + _, err := cdc.MarshalJSON(prop) + require.NoError(t, err) +} + +func createGroupGenesis() []cosmos.GenesisKV { + return append(DefaultGenesis, + cosmos.NewGenesisKV("app_state.group.group_seq", "1"), + cosmos.NewGenesisKV("app_state.group.groups", []grouptypes.GroupInfo{groupInfo}), + cosmos.NewGenesisKV("app_state.group.group_members", []grouptypes.GroupMember{groupMember1, groupMember2}), + cosmos.NewGenesisKV("app_state.group.group_policy_seq", "1"), + cosmos.NewGenesisKV("app_state.group.group_policies", []*grouptypes.GroupPolicyInfo{groupPolicy}), + cosmos.NewGenesisKV("app_state.poa.params.admins", []string{groupAddr}), + cosmos.NewGenesisKV("app_state.manifest.params.inflation.automatic_enabled", false), + cosmos.NewGenesisKV("app_state.manifest.params.inflation.yearly_amount", "0"), + ) +} + +func createMember(address, weight, metadata string) grouptypes.Member { + return grouptypes.Member{ + Address: address, + Weight: weight, + Metadata: metadata, + AddedAt: time.Now(), + } +} + +func createGroupMember(groupID uint64, member *grouptypes.Member) grouptypes.GroupMember { + return grouptypes.GroupMember{ + GroupId: groupID, + Member: member, + } +} + +func createGroupPolicyInfo(address string, groupID uint64, metadata string) *grouptypes.GroupPolicyInfo { + return &grouptypes.GroupPolicyInfo{ + Address: address, + GroupId: groupID, + Admin: address, + Version: 1, + Metadata: metadata, + } +} + +func createThresholdDecisionPolicy(threshold string, votingPeriod, minExecutionPeriod time.Duration) *grouptypes.ThresholdDecisionPolicy { + return &grouptypes.ThresholdDecisionPolicy{ + Threshold: threshold, + Windows: &grouptypes.DecisionPolicyWindows{ + VotingPeriod: votingPeriod, + MinExecutionPeriod: minExecutionPeriod, + }, + } +} + +func createUpgradeProposal(authority, planName string, planHeight int64) upgradetypes.MsgSoftwareUpgrade { + return upgradetypes.MsgSoftwareUpgrade{ + Authority: authority, + Plan: upgradetypes.Plan{ + Name: planName, + Height: planHeight, + Info: "{}", + }, + } +} + +func createCancelUpgradeProposal(authority string) upgradetypes.MsgCancelUpgrade { + return upgradetypes.MsgCancelUpgrade{ + Authority: authority, + } +} + +func createManifestParams(inflation *manifesttypes.Inflation, stakeholders []*manifesttypes.StakeHolders) manifesttypes.Params { + return manifesttypes.Params{ + Inflation: inflation, + StakeHolders: stakeholders, + } +} + +func createInflation(mintDenom string, yearlyAmount uint64, automaticEnabled bool) *manifesttypes.Inflation { + return &manifesttypes.Inflation{ + MintDenom: mintDenom, + YearlyAmount: yearlyAmount, + AutomaticEnabled: automaticEnabled, + } +} + +func createStakeHolders(address string, percentage int32) *manifesttypes.StakeHolders { + return &manifesttypes.StakeHolders{ + Address: address, + Percentage: percentage, + } +} + +func createManifestUpdateProposal(authority string, params manifesttypes.Params) manifesttypes.MsgUpdateParams { + return manifesttypes.MsgUpdateParams{ + Authority: authority, + Params: params, + } +} + +func createManifestPayoutProposal(authority string, payout sdk.Coin) manifesttypes.MsgPayoutStakeholders { + return manifesttypes.MsgPayoutStakeholders{ + Authority: authority, + Payout: payout, + } +} + +func createManifestBurnProposal(sender string, amounts sdk.Coins) manifesttypes.MsgBurnHeldBalance { + return manifesttypes.MsgBurnHeldBalance{ + Sender: sender, + BurnCoins: amounts, + } +} + +func createPOAParams(admins []string, allowValidatorSelfExit bool) poatypes.Params { + return poatypes.Params{ + Admins: admins, + AllowValidatorSelfExit: allowValidatorSelfExit, + } +} + +func createPOAUpdateParams(sender string, params poatypes.Params) *poatypes.MsgUpdateParams { + return &poatypes.MsgUpdateParams{ + Sender: sender, + Params: params, + } +} + +func createBankSendProposal(from, to string, amount sdk.Coin) banktypes.MsgSend { + return banktypes.MsgSend{ + FromAddress: from, + ToAddress: to, + Amount: sdk.Coins{amount}, + } +} + +func createTfCreateDenomProposal(sender, subdenom string) tokenfactorytypes.MsgCreateDenom { + return tokenfactorytypes.MsgCreateDenom{ + Sender: sender, + Subdenom: subdenom, + } +} + +func createTfMintProposal(sender string, amount sdk.Coin, mintTo string) tokenfactorytypes.MsgMint { + return tokenfactorytypes.MsgMint{ + Sender: sender, + Amount: amount, + MintToAddress: mintTo, + } +} + +func createTfBurnProposal(sender string, amount sdk.Coin, burnFrom string) tokenfactorytypes.MsgBurn { + return tokenfactorytypes.MsgBurn{ + Sender: sender, + Amount: amount, + BurnFromAddress: burnFrom, + } +} + +func createTfForceTransferProposal(sender string, amount sdk.Coin, from, to string) tokenfactorytypes.MsgForceTransfer { + return tokenfactorytypes.MsgForceTransfer{ + Sender: sender, + Amount: amount, + TransferFromAddress: from, + TransferToAddress: to, + } +} + +func createTfChangeAdminProposal(sender, denom, newAdmin string) tokenfactorytypes.MsgChangeAdmin { + return tokenfactorytypes.MsgChangeAdmin{ + Sender: sender, + Denom: denom, + NewAdmin: newAdmin, + } +} + +func createTfMetadata(base, denom, display, name, symbol, description string) banktypes.Metadata { + return banktypes.Metadata{ + Base: base, + Display: display, + Name: name, + Symbol: symbol, + Description: description, + DenomUnits: []*banktypes.DenomUnit{ + { + Denom: denom, + Exponent: 0, + Aliases: []string{symbol}, + }, + { + Denom: symbol, + Exponent: 6, + Aliases: []string{denom}, + }, + }, + } +} + +func createTfModifyMetadataProposal(sender, denom, name, symbol, base, display, description string) tokenfactorytypes.MsgSetDenomMetadata { + return tokenfactorytypes.MsgSetDenomMetadata{ + Sender: sender, + Metadata: createTfMetadata(base, denom, display, name, symbol, description), + } +} + +func verifyBalance(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, address, denom string, expected sdkmath.Int) { + bal, err := chain.BankQueryBalance(ctx, address, denom) + require.NoError(t, err) + require.Equal(t, expected, bal) +} + +func buildWallet(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, address, mnemonic string) { + _, err := chain.BuildWallet(ctx, address, mnemonic) + require.NoError(t, err) +} + +func _verifyUpgradePlan(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain) *upgradetypes.Plan { + plan, err := chain.UpgradeQueryPlan(ctx) + require.NoError(t, err) + return plan +} + +func verifyUpgradePlanIsNil(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain) { + plan := _verifyUpgradePlan(t, ctx, chain) + require.Nil(t, plan) +} + +func verifyUpgradePlan(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, expectedPlan *upgradetypes.Plan) { + plan := _verifyUpgradePlan(t, ctx, chain) + require.NotNil(t, plan) + require.Equal(t, expectedPlan.Name, plan.Name) + require.Equal(t, expectedPlan.Height, plan.Height) +} + +func verifyUpgradeAuthority(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, expectedAuthority string) { + authority, err := chain.UpgradeQueryAuthority(ctx) + require.NoError(t, err) + require.Equal(t, expectedAuthority, authority) +} + +func verifyManifestStakeholders(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, expectedStakeholders []*manifesttypes.StakeHolders) { + resp, err := manifesttypes.NewQueryClient(chain.GetNode().GrpcConn).Params(ctx, &manifesttypes.QueryParamsRequest{}) + require.NoError(t, err) + require.Len(t, resp.Params.StakeHolders, len(expectedStakeholders)) + for i, sh := range expectedStakeholders { + require.Equal(t, sh.Address, resp.Params.StakeHolders[i].Address) + require.Equal(t, sh.Percentage, resp.Params.StakeHolders[i].Percentage) + } +} + +func sendFunds(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, from, to, denom string, amount sdkmath.Int) { + err := chain.SendFunds(ctx, from, ibc.WalletAmount{ + Address: to, + Denom: denom, + Amount: amount, + }) + require.NoError(t, err) +} + +func updatePOAParams(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, groupAddr string, allowValidatorSelfExit bool) { + r, err := helpers.POAUpdateParams(ctx, chain, user, groupAddr, allowValidatorSelfExit) + require.NoError(t, err) + require.NotNil(t, r) + require.Equal(t, uint32(0x0), r.Code) +} + +func verifyBankDenomMetadata(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, expectedMetadata banktypes.Metadata) { + meta, err := chain.BankQueryDenomMetadata(ctx, tfFullDenom) + require.NoError(t, err) + require.Equal(t, expectedMetadata, *meta) +} + +func verifyTfAdmin(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, denom, expectedAdmin string) { + resp, err := chain.TokenFactoryQueryAdmin(ctx, denom) + require.NoError(t, err) + require.Equal(t, expectedAdmin, resp.AuthorityMetadata.Admin) +} diff --git a/interchaintest/poa_test.go b/interchaintest/poa_test.go index a4dfe6f..17c57b1 100644 --- a/interchaintest/poa_test.go +++ b/interchaintest/poa_test.go @@ -164,14 +164,14 @@ func testPowerErrors(t *testing.T, ctx context.Context, chain *cosmos.CosmosChai var err error t.Run("fail: set-power message from a non authorized user", func(t *testing.T) { - res, _ = helpers.POASetPower(t, ctx, chain, incorrectUser, validators[1], 1_000_000) + res, _ = helpers.POASetPower(ctx, chain, incorrectUser, validators[1], 1_000_000) res, err := chain.GetTransaction(res.TxHash) require.NoError(t, err) require.Contains(t, res.RawLog, poa.ErrNotAnAuthority.Error()) }) t.Run("fail: set-power message below minimum power requirement (self bond)", func(t *testing.T) { - res, err = helpers.POASetPower(t, ctx, chain, admin, validators[0], 1) + res, err = helpers.POASetPower(ctx, chain, admin, validators[0], 1) require.Error(t, err) // cli validate error require.ErrorContains(t, err, poa.ErrPowerBelowMinimum.Error()) }) diff --git a/interchaintest/setup.go b/interchaintest/setup.go index 7b40bce..fa3d92a 100644 --- a/interchaintest/setup.go +++ b/interchaintest/setup.go @@ -16,7 +16,11 @@ import ( tokenfactorytypes "github.com/strangelove-ventures/tokenfactory/x/tokenfactory/types" "github.com/stretchr/testify/require" - types "github.com/liftedinit/manifest-ledger/x/manifest/types" + grouptypes "github.com/cosmos/cosmos-sdk/x/group" + + manifesttypes "github.com/liftedinit/manifest-ledger/x/manifest/types" + + "github.com/liftedinit/manifest-ledger/x/manifest/types" sdkmath "cosmossdk.io/math" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" @@ -38,7 +42,11 @@ var ( accAddr = "manifest1hj5fveer5cjtn4wd6wstzugjfdxzl0xp8ws9ct" accMnemonic = "decorate bright ozone fork gallery riot bus exhaust worth way bone indoor calm squirrel merry zero scheme cotton until shop any excess stage laundry" - acc2Addr = "manifest1efd63aw40lxf3n4mhf7dzhjkr453axurm6rp3z" + acc2Addr = "manifest1efd63aw40lxf3n4mhf7dzhjkr453axurm6rp3z" + acc3Addr = "manifest1sc0aw0e6mcrm7ex5v3ll5gh4dq5whn3acmkupn" + acc3Mnemonic = "pelican gasp plunge better swallow school infant magic mercy portion candy beauty intact soldier scan must plate logic trial horror theory scrub sorry stand" + acc4Addr = "manifest1g292xgmcclhq4au5p7580m2s3f8tpwjra644jm" + acc4Mnemonic = "myself bamboo retire day exile cancel peanut agree come method odor innocent welcome royal engage key surprise practice capable sauce orient young divert mirror" CosmosGovModuleAcc = "manifest10d07y265gmmuvt4z0w9aw880jnsr700jmq3jzm" @@ -94,6 +102,8 @@ var ( func AppEncoding() *sdktestutil.TestEncodingConfig { enc := cosmos.DefaultEncoding() + manifesttypes.RegisterInterfaces(enc.InterfaceRegistry) + grouptypes.RegisterInterfaces(enc.InterfaceRegistry) tokenfactorytypes.RegisterInterfaces(enc.InterfaceRegistry) poatypes.RegisterInterfaces(enc.InterfaceRegistry) diff --git a/interchaintest/tokenfactory_test.go b/interchaintest/tokenfactory_test.go index 15cac6d..340dd0c 100644 --- a/interchaintest/tokenfactory_test.go +++ b/interchaintest/tokenfactory_test.go @@ -15,8 +15,6 @@ import ( ) func TestTokenFactory(t *testing.T) { - t.Parallel() - ctx := context.Background() // Same as ChainNode.HomeDir() but we need it before the chain is created @@ -131,7 +129,6 @@ func TestTokenFactory(t *testing.T) { // ensure the admin is the contract admin, err := appChain.TokenFactoryQueryAdmin(ctx, tfDenom) - t.Log("admin", admin) require.NoError(t, err) if admin.AuthorityMetadata.Admin != uaddr2 { t.Fatal("admin not uaddr2. Did not properly transfer.") diff --git a/scripts/test_node.sh b/scripts/test_node.sh index a81f6ca..329dec6 100644 --- a/scripts/test_node.sh +++ b/scripts/test_node.sh @@ -27,6 +27,11 @@ export GRPC_WEB=${GRPC_WEB:-"9091"} export ROSETTA=${ROSETTA:-"8080"} export TIMEOUT_COMMIT=${TIMEOUT_COMMIT:-"5s"} +export DAEMON_NAME=manifestd +export DAEMON_HOME=$HOME_DIR +export DAEMON_ALLOW_DOWNLOAD_BINARIES=false +export DAEMON_RESTART_AFTER_UPGRADE=true + alias BINARY="$BINARY --home=$HOME_DIR" command -v $BINARY > /dev/null 2>&1 || { echo >&2 "$BINARY command not found. Ensure this is setup / properly installed in your GOPATH (make install)."; exit 1; } @@ -61,31 +66,55 @@ from_scratch () { update_test_genesis '.app_state["gov"]["params"]["voting_period"]="15s"' update_test_genesis '.app_state["gov"]["params"]["expedited_voting_period"]="10s"' # staking - update_test_genesis '.app_state["staking"]["params"]["bond_denom"]="ustake"' # PoA Token + update_test_genesis '.app_state["staking"]["params"]["bond_denom"]="upoa"' # PoA Token update_test_genesis '.app_state["staking"]["params"]["min_commission_rate"]="0.000000000000000000"' # mint update_test_genesis '.app_state["mint"]["params"]["mint_denom"]="umfx"' # not used update_test_genesis '.app_state["mint"]["params"]["blocks_per_year"]="6311520"' + # group + update_test_genesis '.app_state["group"]["group_seq"]="1"' + update_test_genesis '.app_state["group"]["groups"]=[{"id": "1", "admin": "manifest1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsfmy9qj", "metadata": "AQ==", "version": "2", "total_weight": "2", "created_at": "2024-05-16T15:10:54.372190727Z"}]' + update_test_genesis '.app_state["group"]["group_members"]=[{"group_id": "1", "member": {"address": "manifest1hj5fveer5cjtn4wd6wstzugjfdxzl0xp8ws9ct", "weight": "1", "metadata": "user1", "added_at": "2024-05-16T15:10:54.372190727Z"}}, {"group_id": "1", "member": {"address": "manifest1efd63aw40lxf3n4mhf7dzhjkr453axurm6rp3z", "weight": "1", "metadata": "user2", "added_at": "2024-05-16T15:10:54.372190727Z"}}]' + update_test_genesis '.app_state["group"]["group_policy_seq"]="1"' + update_test_genesis '.app_state["group"]["group_policies"]=[{"address": "manifest1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsfmy9qj", "group_id": "1", "admin": "manifest1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsfmy9qj", "metadata": "AQ==", "version": "2", "decision_policy": { "@type": "/cosmos.group.v1.ThresholdDecisionPolicy", "threshold": "1", "windows": {"voting_period": "30s", "min_execution_period": "0s"}}, "created_at": "2024-05-16T15:10:54.372190727Z"}]' + # Custom Modules + # POA + update_test_genesis '.app_state["poa"]["params"]["admins"]=["'$POA_ADMIN_ADDRESS'"]' + # TokenFactory update_test_genesis '.app_state["tokenfactory"]["params"]["denom_creation_fee"]=[]' update_test_genesis '.app_state["tokenfactory"]["params"]["denom_creation_gas_consume"]=2000000' # manifest update_test_genesis '.app_state["manifest"]["params"]["inflation"]["mint_denom"]="umfx"' - update_test_genesis '.app_state["manifest"]["params"]["inflation"]["yearly_amount"]="500000000000"' # in micro format (1MFX = 10**6) - update_test_genesis '.app_state["manifest"]["params"]["inflation"]["automatic_enabled"]=true' + update_test_genesis '.app_state["manifest"]["params"]["inflation"]["yearly_amount"]="0"' # in micro format (1MFX = 10**6) + update_test_genesis '.app_state["manifest"]["params"]["inflation"]["automatic_enabled"]=false' update_test_genesis '.app_state["manifest"]["params"]["stake_holders"]=[{"address":"manifest1hj5fveer5cjtn4wd6wstzugjfdxzl0xp8ws9ct","percentage":100000000}]' + # tokenfactory + # SPDT +# update_test_genesis '.app_state["tokenfactory"]["factory_denoms"]=[{"denom": "factory/manifest1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsfmy9qj/uspdt", "authority_metadata": {"admin": "manifest1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsfmy9qj"}}]' +# update_test_genesis '.app_state["bank"]["denom_metadata"]=[{"description": "SpaceData", "denom_units": [{"denom": "factory/manifest1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsfmy9qj/uspdt", "exponent": 0, "aliases": ["SPDT"]}, {"denom": "SPDT", "exponent": 6, "aliases": ["factory/manifest1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsfmy9qj/uspdt"]}], "base": "factory/manifest1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsfmy9qj/uspdt", "display": "SPDT", "name": "SpaceData", "symbol": "SPDT", "uri": "", "uri_hash": ""}]' + + #ABUS +# update_test_genesis '.app_state["tokenfactory"]["factory_denoms"] |= . + [{"denom": "factory/manifest1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsfmy9qj/uabus", "authority_metadata": {"admin": "manifest1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsfmy9qj"}}]' +# update_test_genesis '.app_state["bank"]["denom_metadata"] |= . + [{"description": "Arebus Gas Token", "denom_units": [{"denom": "factory/manifest1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsfmy9qj/uabus", "exponent": 0, "aliases": ["ABUS"]}, {"denom": "ABUS", "exponent": 6, "aliases": ["factory/manifest1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsfmy9qj/uabus"]}], "base": "factory/manifest1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsfmy9qj/uabus", "display": "ABUS", "name": "Arebus Gas Token", "symbol": "ABUS", "uri": "", "uri_hash": ""}]' + + # Add all other MANY tokens + # ... + # ... + # Allocate genesis accounts - BINARY genesis add-genesis-account $KEY 1000000ustake,10000000umfx,1000utest --keyring-backend $KEYRING +# BINARY genesis add-genesis-account $KEY 1000000upoa,10000000umfx,1000utest,1000000000000000000000factory/manifest1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsfmy9qj/uspdt,100000000000000000000000factory/manifest1afk9zr2hn2jsac63h4hm60vl9z3e5u69gndzf7c99cqge3vzwjzsfmy9qj/uabus --keyring-backend $KEYRING + BINARY genesis add-genesis-account $KEY 1000000upoa,10000000umfx,1000utest --keyring-backend $KEYRING BINARY genesis add-genesis-account $KEY2 100000umfx,1000utest --keyring-backend $KEYRING # Set 1 POAToken -> user GenTxFlags="--commission-rate=0.0 --commission-max-rate=1.0 --commission-max-change-rate=0.1" - BINARY genesis gentx $KEY 1000000ustake --keyring-backend $KEYRING --chain-id $CHAIN_ID $GenTxFlags + BINARY genesis gentx $KEY 1000000upoa --keyring-backend $KEYRING --chain-id $CHAIN_ID $GenTxFlags # Collect genesis tx BINARY genesis collect-gentxs --home=$HOME_DIR @@ -127,4 +156,6 @@ sed -i 's/address = ":8080"/address = "0.0.0.0:'$ROSETTA'"/g' $HOME_DIR/config/a sed -i 's/timeout_commit = "5s"/timeout_commit = "'$TIMEOUT_COMMIT'"/g' $HOME_DIR/config/config.toml # Start the node -BINARY start --pruning=nothing --minimum-gas-prices=0umfx --rpc.laddr="tcp://0.0.0.0:$RPC" \ No newline at end of file +BINARY start --pruning=nothing --minimum-gas-prices=0umfx --rpc.laddr="tcp://0.0.0.0:$RPC" +#cosmovisor init $(which $BINARY) +#cosmovisor run start --pruning=nothing --minimum-gas-prices=0umfx --rpc.laddr="tcp://0.0.0.0:$RPC" --home $HOME_DIR diff --git a/x/manifest/types/errors.go b/x/manifest/types/errors.go index 783084a..8a1b0c8 100644 --- a/x/manifest/types/errors.go +++ b/x/manifest/types/errors.go @@ -4,4 +4,7 @@ import ( "cosmossdk.io/errors" ) -var ErrManualMintingDisabled = errors.Register(ModuleName, 1, "manual minting is disabled due to automatic inflation being on") +var ( + ErrManualMintingDisabled = errors.Register(ModuleName, 1, "manual minting is disabled due to automatic inflation being on") + ErrInflationParamsNotSet = errors.Register(ModuleName, 2, "inflation params not set") +) diff --git a/x/manifest/types/params.go b/x/manifest/types/params.go index 5b9673e..4df2c66 100644 --- a/x/manifest/types/params.go +++ b/x/manifest/types/params.go @@ -48,6 +48,10 @@ func (p Params) String() string { // Validate does the sanity check on the params. func (p Params) Validate() error { + // Fix for https://github.com/liftedinit/manifest-ledger/issues/61 + if p.Inflation == nil { + return ErrInflationParamsNotSet + } if len(p.StakeHolders) == 0 { return nil }