test(integration): port x/bank tests to server/v2 app (backport #21912) (#22457)

Co-authored-by: Matt Kocubinski <mkocubinski@gmail.com>
Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
mergify[bot] 2024-11-08 15:30:23 +01:00 committed by GitHub
parent a0458127fc
commit 1250d2a6da
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 1904 additions and 29 deletions

View File

@ -16,6 +16,13 @@ import (
"github.com/cosmos/cosmos-sdk/codec/types"
)
var DefaultProviders = depinject.Provide(
ProvideInterfaceRegistry,
ProvideLegacyAmino,
ProvideProtoCodec,
ProvideAddressCodec,
)
func ProvideInterfaceRegistry(
addressCodec address.Codec,
validatorAddressCodec address.ValidatorAddressCodec,

View File

@ -8,8 +8,10 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
var _ Mempool[sdk.Tx] = (*NoOpMempool[sdk.Tx])(nil) // verify interface at compile time
var _ Mempool[transaction.Tx] = (*NoOpMempool[transaction.Tx])(nil)
var (
_ Mempool[sdk.Tx] = (*NoOpMempool[sdk.Tx])(nil) // verify interface at compile time
_ Mempool[transaction.Tx] = (*NoOpMempool[transaction.Tx])(nil)
)
// NoOpMempool defines a no-op mempool. Transactions are completely discarded and
// ignored when BaseApp interacts with the mempool.

View File

@ -48,11 +48,8 @@ func AppConfig() depinject.Config {
return depinject.Configs(
ModuleConfig, // Alternatively use appconfig.LoadYAML(AppConfigYAML)
runtime.DefaultServiceBindings(),
codec.DefaultProviders,
depinject.Provide(
codec.ProvideInterfaceRegistry,
codec.ProvideAddressCodec,
codec.ProvideProtoCodec,
codec.ProvideLegacyAmino,
ProvideRootStoreConfig,
// inject desired account types:
multisigdepinject.ProvideAccount,

View File

@ -6,12 +6,15 @@ require (
cosmossdk.io/api v0.8.0 // main
cosmossdk.io/collections v0.4.1-0.20241107084833-00f3065e70ee // main
cosmossdk.io/core v1.0.0-alpha.5 // main
cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 // main
cosmossdk.io/core/testing v0.0.0 // main
cosmossdk.io/depinject v1.1.0
cosmossdk.io/log v1.4.1
cosmossdk.io/math v1.3.0
cosmossdk.io/runtime/v2 v2.0.0-20241107153845-4e240908dd60
cosmossdk.io/server/v2/stf v0.0.0-20241107153845-4e240908dd60
cosmossdk.io/simapp v0.0.0-20230309163709-87da587416ba
cosmossdk.io/store v1.1.1-0.20240909133312-50288938d1b6 // main
cosmossdk.io/store v1.1.1
cosmossdk.io/store/v2 v2.0.0-00010101000000-000000000000
cosmossdk.io/x/accounts v0.0.0-20240913065641-0064ccbce64e
cosmossdk.io/x/accounts/defaults/base v0.0.0-00010101000000-000000000000
cosmossdk.io/x/accounts/defaults/lockup v0.0.0-20240417181816-5e7aae0db1f5
@ -29,7 +32,7 @@ require (
cosmossdk.io/x/protocolpool v0.0.0-20230925135524-a1bc045b3190
cosmossdk.io/x/slashing v0.0.0-00010101000000-000000000000
cosmossdk.io/x/staking v0.0.0-20240226161501-23359a0b6d91
cosmossdk.io/x/tx v1.0.0-alpha.2 // main
cosmossdk.io/x/tx v1.0.0-alpha.2
cosmossdk.io/x/upgrade v0.0.0-20230613133644-0a778132a60f
github.com/cometbft/cometbft v1.0.0-rc1.0.20240908111210-ab0be101882f
github.com/cometbft/cometbft/api v1.0.0-rc.1
@ -46,6 +49,7 @@ require (
github.com/spf13/cobra v1.8.1 // indirect
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b
google.golang.org/grpc v1.67.1
google.golang.org/protobuf v1.35.1
gotest.tools/v3 v3.5.1
@ -63,8 +67,10 @@ require (
cloud.google.com/go/storage v1.42.0 // indirect
cosmossdk.io/client/v2 v2.0.0-20230630094428-02b760776860 // indirect
cosmossdk.io/errors v1.0.1 // indirect
cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5 // indirect
cosmossdk.io/indexer/postgres v0.0.0-20241107084833-00f3065e70ee // indirect
cosmossdk.io/schema v0.3.1-0.20241010135032-192601639cac // indirect
cosmossdk.io/server/v2/appmanager v0.0.0-20241107153845-4e240908dd60 // indirect
cosmossdk.io/x/circuit v0.0.0-20230613133644-0a778132a60f // indirect
cosmossdk.io/x/epochs v0.0.0-20240522060652-a1ae4c3e0337 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
@ -107,7 +113,7 @@ require (
github.com/emicklei/dot v1.6.2 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/getsentry/sentry-go v0.29.0 // indirect
github.com/go-kit/kit v0.13.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
@ -164,6 +170,7 @@ require (
github.com/manifoldco/promptui v0.9.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/minio/highwayhash v1.0.3 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
@ -176,9 +183,9 @@ require (
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.20.4 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.59.1 // indirect
github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
@ -198,7 +205,6 @@ require (
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/zondax/hid v0.9.2 // indirect
github.com/zondax/ledger-go v0.14.3 // indirect
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect
go.etcd.io/bbolt v1.4.0-alpha.1 // indirect
go.opencensus.io v0.24.0 // indirect
@ -212,7 +218,7 @@ require (
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/oauth2 v0.22.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/term v0.25.0 // indirect
@ -239,8 +245,10 @@ replace (
// pseudo version lower than the latest tag
cosmossdk.io/api => cosmossdk.io/api v0.7.3-0.20240924065902-eb7653cfecdf // main
cosmossdk.io/client/v2 => ../client/v2
// pseudo version lower than the latest tag
cosmossdk.io/core/testing => cosmossdk.io/core/testing v0.0.0-20241107153845-4e240908dd60
cosmossdk.io/store => cosmossdk.io/store v1.0.0-rc.0.0.20241106093505-9611c5a0e6e3 // main
// pseudo version lower than the latest tag
cosmossdk.io/store/v2 => cosmossdk.io/store/v2 v2.0.0-20241107153845-4e240908dd60
cosmossdk.io/x/accounts => ../x/accounts
cosmossdk.io/x/accounts/defaults/base => ../x/accounts/defaults/base
cosmossdk.io/x/accounts/defaults/lockup => ../x/accounts/defaults/lockup

View File

@ -198,23 +198,33 @@ cosmossdk.io/collections v0.4.1-0.20241107084833-00f3065e70ee h1:OLqvi9ekfShobmd
cosmossdk.io/collections v0.4.1-0.20241107084833-00f3065e70ee/go.mod h1:DcD++Yfcq0OFtM3CJNYLIBjfZ+4DEyeJ/AUk6gkwlOE=
cosmossdk.io/core v1.0.0-alpha.5 h1:McjYXAQ6XcT20v2uHyH7PhoWH8V+mebzfVFqT3GinsI=
cosmossdk.io/core v1.0.0-alpha.5/go.mod h1:3u9cWq1FAVtiiCrDPpo4LhR+9V6k/ycSG4/Y/tREWCY=
cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 h1:NxxUo0GMJUbIuVg0R70e3cbn9eFTEuMr7ev1AFvypdY=
cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29/go.mod h1:8s2tPeJtSiQuoyPmr2Ag7meikonISO4Fv4MoO8+ORrs=
cosmossdk.io/core/testing v0.0.0-20241107153845-4e240908dd60 h1:owHSnQ2LZ4Kqgb1qhl8HeOTgebR04FwKHrZk4Q2d50Y=
cosmossdk.io/core/testing v0.0.0-20241107153845-4e240908dd60/go.mod h1:3YvVv9aJayjPhdX0DY1IMrGse4sR63hNBWx2VtDWjGQ=
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/errors/v2 v2.0.0-20240731132947-df72853b3ca5 h1:IQNdY2kB+k+1OM2DvqFG1+UgeU1JzZrWtwuWzI3ZfwA=
cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5/go.mod h1:0CuYKkFHxc1vw2JC+t21THBCALJVROrWVR/3PQ1urpc=
cosmossdk.io/indexer/postgres v0.0.0-20241107084833-00f3065e70ee h1:BfzybkpW7xSpRSQPSvv4T6MbEYtcezJMFZ16hZcftDE=
cosmossdk.io/indexer/postgres v0.0.0-20241107084833-00f3065e70ee/go.mod h1:vb5uiIC3rDjz+V7UaSLNA3g5TpbRygWi652qtMugWHA=
cosmossdk.io/log v1.4.1 h1:wKdjfDRbDyZRuWa8M+9nuvpVYxrEOwbD/CA8hvhU8QM=
cosmossdk.io/log v1.4.1/go.mod h1:k08v0Pyq+gCP6phvdI6RCGhLf/r425UT6Rk/m+o74rU=
cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE=
cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k=
cosmossdk.io/runtime/v2 v2.0.0-20241107153845-4e240908dd60 h1:305IbyP2jaJeKNRglxBtEAY/JKz8rD3l+081zrAThGY=
cosmossdk.io/runtime/v2 v2.0.0-20241107153845-4e240908dd60/go.mod h1:zXxA8bHeShGxzTLR9m3OdF+aJ/IEmWSnrz343Ri6Me8=
cosmossdk.io/schema v0.3.0/go.mod h1:RDAhxIeNB4bYqAlF4NBJwRrgtnciMcyyg0DOKnhNZQQ=
cosmossdk.io/schema v0.3.1-0.20241010135032-192601639cac h1:3joNZZWZ3k7fMsrBDL1ktuQ2xQwYLZOaDhkruadDFmc=
cosmossdk.io/schema v0.3.1-0.20241010135032-192601639cac/go.mod h1:RDAhxIeNB4bYqAlF4NBJwRrgtnciMcyyg0DOKnhNZQQ=
cosmossdk.io/server/v2/appmanager v0.0.0-20241107153845-4e240908dd60 h1:M26/YrNRru59kzRkW/Mm7bnpEEsnR9rIFdNFGTXicGQ=
cosmossdk.io/server/v2/appmanager v0.0.0-20241107153845-4e240908dd60/go.mod h1:mONOF8GRbxs5R04zMscuQQI1gx/XHTY7imKjcwLI5uo=
cosmossdk.io/server/v2/stf v0.0.0-20241107153845-4e240908dd60 h1:+PYG1Wjsu8ZX5vvINPu7UjRxzT9m9sJ9SlS8MVApmOU=
cosmossdk.io/server/v2/stf v0.0.0-20241107153845-4e240908dd60/go.mod h1:njNsfl5hKTylxDgSiBTLuOM1tic04aPk0k5Af6ybPN0=
cosmossdk.io/store v1.0.0-rc.0.0.20241106093505-9611c5a0e6e3 h1:p69ThBO2dqCHKA3GcVoGr18Q4oH04asl1TsDPloxtSI=
cosmossdk.io/store v1.0.0-rc.0.0.20241106093505-9611c5a0e6e3/go.mod h1:uaysXSQHWUykekFPvS1JqQ7HM58Zuqcby1DNlLQPWSg=
cosmossdk.io/store/v2 v2.0.0-20241107153845-4e240908dd60 h1:2DnhJcJEoVDqz1aoqmy4RXiAnpqy9GNW1gzgnkesJmI=
cosmossdk.io/store/v2 v2.0.0-20241107153845-4e240908dd60/go.mod h1:calxpXVRO0AZRYdV+Kup74XP7rhDWAzVHKwHWm2TQZc=
cosmossdk.io/x/tx v1.0.0-alpha.2 h1:UW80FMm7B0fiAMsrfe5+HabSJ3XBg+tQa6/GK9prqWk=
cosmossdk.io/x/tx v1.0.0-alpha.2/go.mod h1:r4yTKSJ7ZCCR95YbBfY3nfvbgNw6m9F6f25efWYYQWo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
@ -395,8 +405,8 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/getsentry/sentry-go v0.29.0 h1:YtWluuCFg9OfcqnaujpY918N/AhCCwarIDWOYSBAjCA=
github.com/getsentry/sentry-go v0.29.0/go.mod h1:jhPesDAL0Q0W2+2YEuVOvdWmVtdsr1+jtBrlDEVWwLY=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@ -665,6 +675,8 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
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/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q=
github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ=
@ -732,8 +744,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@ -742,8 +754,8 @@ github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.59.1 h1:LXb1quJHWm1P6wq/U824uxYi4Sg0oGvNeUm1z5dJoX0=
github.com/prometheus/common v0.59.1/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0=
github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
@ -992,8 +1004,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

411
tests/integration/v2/app.go Normal file
View File

@ -0,0 +1,411 @@
package integration
import (
"context"
"crypto/sha256"
"errors"
"fmt"
"math/rand"
"testing"
"time"
cmtproto "github.com/cometbft/cometbft/api/cometbft/types/v1"
cmtjson "github.com/cometbft/cometbft/libs/json"
cmttypes "github.com/cometbft/cometbft/types"
"github.com/stretchr/testify/require"
"cosmossdk.io/core/comet"
corecontext "cosmossdk.io/core/context"
"cosmossdk.io/core/server"
corestore "cosmossdk.io/core/store"
"cosmossdk.io/core/transaction"
"cosmossdk.io/depinject"
sdkmath "cosmossdk.io/math"
"cosmossdk.io/runtime/v2"
"cosmossdk.io/runtime/v2/services"
"cosmossdk.io/server/v2/stf"
"cosmossdk.io/server/v2/stf/branch"
"cosmossdk.io/store/v2"
"cosmossdk.io/store/v2/root"
bankkeeper "cosmossdk.io/x/bank/keeper"
banktypes "cosmossdk.io/x/bank/types"
consensustypes "cosmossdk.io/x/consensus/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/std"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/simulation"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsign "github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/cosmos/cosmos-sdk/x/auth/tx"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)
const DefaultGenTxGas = 10000000
const (
Genesis_COMMIT = iota
Genesis_NOCOMMIT
Genesis_SKIP
)
type stateMachineTx = transaction.Tx
// DefaultConsensusParams defines the default CometBFT consensus params used in
// SimApp testing.
var DefaultConsensusParams = &cmtproto.ConsensusParams{
Version: &cmtproto.VersionParams{
App: 1,
},
Block: &cmtproto.BlockParams{
MaxBytes: 200000,
MaxGas: 100_000_000,
},
Evidence: &cmtproto.EvidenceParams{
MaxAgeNumBlocks: 302400,
MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration
MaxBytes: 10000,
},
Validator: &cmtproto.ValidatorParams{
PubKeyTypes: []string{
cmttypes.ABCIPubKeyTypeEd25519,
cmttypes.ABCIPubKeyTypeSecp256k1,
},
},
}
// StartupConfig defines the startup configuration of a new test app.
type StartupConfig struct {
// ValidatorSet defines a custom validator set to be validating the app.
ValidatorSet func() (*cmttypes.ValidatorSet, error)
// AppOption defines the additional operations that will be run in the app builder phase.
AppOption runtime.AppBuilderOption[stateMachineTx]
// GenesisBehavior defines the behavior of the app at genesis.
GenesisBehavior int
// GenesisAccounts defines the genesis accounts to be used in the app.
GenesisAccounts []GenesisAccount
// HomeDir defines the home directory of the app where config and data will be stored.
HomeDir string
}
func DefaultStartUpConfig(t *testing.T) StartupConfig {
t.Helper()
priv := secp256k1.GenPrivKey()
ba := authtypes.NewBaseAccount(
priv.PubKey().Address().Bytes(),
priv.PubKey(),
0,
0,
)
ga := GenesisAccount{
ba,
sdk.NewCoins(
sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100000000000000)),
),
}
homedir := t.TempDir()
t.Logf("generated integration test app config; HomeDir=%s", homedir)
return StartupConfig{
ValidatorSet: CreateRandomValidatorSet,
GenesisBehavior: Genesis_COMMIT,
GenesisAccounts: []GenesisAccount{ga},
HomeDir: homedir,
}
}
// NewApp initializes a new runtime.App. A Nop logger is set in runtime.App.
// appConfig defines the application configuration (f.e. app_config.go).
// extraOutputs defines the extra outputs to be assigned by the dependency injector (depinject).
func NewApp(
appConfig depinject.Config,
startupConfig StartupConfig,
extraOutputs ...interface{},
) (*App, error) {
// create the app with depinject
var (
storeBuilder = root.NewBuilder()
app *runtime.App[stateMachineTx]
appBuilder *runtime.AppBuilder[stateMachineTx]
txConfig client.TxConfig
txConfigOptions tx.ConfigOptions
cometService comet.Service = &cometServiceImpl{}
kvFactory corestore.KVStoreServiceFactory = func(actor []byte) corestore.KVStoreService {
return services.NewGenesisKVService(actor, &storeService{actor, stf.NewKVStoreService(actor)})
}
cdc codec.Codec
err error
)
if err := depinject.Inject(
depinject.Configs(
appConfig,
codec.DefaultProviders,
depinject.Supply(
&root.Config{
Home: startupConfig.HomeDir,
AppDBBackend: "goleveldb",
Options: root.DefaultStoreOptions(),
},
runtime.GlobalConfig{
"server": server.ConfigMap{
"minimum-gas-prices": "0stake",
},
},
services.NewGenesisHeaderService(stf.HeaderService{}),
cometService,
kvFactory,
&eventService{},
storeBuilder,
),
depinject.Invoke(
std.RegisterInterfaces,
),
),
append(extraOutputs, &appBuilder, &cdc, &txConfigOptions, &txConfig, &storeBuilder)...); err != nil {
return nil, fmt.Errorf("failed to inject dependencies: %w", err)
}
app, err = appBuilder.Build()
if err != nil {
return nil, fmt.Errorf("failed to build app: %w", err)
}
if err := app.LoadLatest(); err != nil {
return nil, fmt.Errorf("failed to load app: %w", err)
}
store := storeBuilder.Get()
if store == nil {
return nil, fmt.Errorf("failed to build store: %w", err)
}
err = store.SetInitialVersion(1)
if err != nil {
return nil, fmt.Errorf("failed to set initial version: %w", err)
}
integrationApp := &App{App: app, Store: store, txConfig: txConfig, lastHeight: 1}
if startupConfig.GenesisBehavior == Genesis_SKIP {
return integrationApp, nil
}
// create validator set
valSet, err := startupConfig.ValidatorSet()
if err != nil {
return nil, errors.New("failed to create validator set")
}
var (
balances []banktypes.Balance
genAccounts []authtypes.GenesisAccount
)
for _, ga := range startupConfig.GenesisAccounts {
genAccounts = append(genAccounts, ga.GenesisAccount)
balances = append(
balances,
banktypes.Balance{
Address: ga.GenesisAccount.GetAddress().String(),
Coins: ga.Coins,
},
)
}
genesisJSON, err := genesisStateWithValSet(
cdc,
app.DefaultGenesis(),
valSet,
genAccounts,
balances...)
if err != nil {
return nil, fmt.Errorf("failed to create genesis state: %w", err)
}
// init chain must be called to stop deliverState from being nil
genesisJSONBytes, err := cmtjson.MarshalIndent(genesisJSON, "", " ")
if err != nil {
return nil, fmt.Errorf(
"failed to marshal default genesis state: %w",
err,
)
}
ctx := context.WithValue(
context.Background(),
corecontext.CometParamsInitInfoKey,
&consensustypes.MsgUpdateParams{
Authority: "consensus",
Block: DefaultConsensusParams.Block,
Evidence: DefaultConsensusParams.Evidence,
Validator: DefaultConsensusParams.Validator,
Abci: DefaultConsensusParams.Abci,
Synchrony: DefaultConsensusParams.Synchrony,
Feature: DefaultConsensusParams.Feature,
},
)
emptyHash := sha256.Sum256(nil)
_, genesisState, err := app.InitGenesis(
ctx,
&server.BlockRequest[stateMachineTx]{
Height: 1,
Time: time.Now(),
Hash: emptyHash[:],
ChainId: "test-chain",
AppHash: emptyHash[:],
IsGenesis: true,
},
genesisJSONBytes,
&genesisTxCodec{txConfigOptions},
)
if err != nil {
return nil, fmt.Errorf("failed init genesis: %w", err)
}
if startupConfig.GenesisBehavior == Genesis_NOCOMMIT {
integrationApp.lastHeight = 0
return integrationApp, nil
}
_, err = integrationApp.Commit(genesisState)
if err != nil {
return nil, fmt.Errorf("failed to commit initial version: %w", err)
}
return integrationApp, nil
}
// App is a wrapper around runtime.App that provides additional testing utilities.
type App struct {
*runtime.App[stateMachineTx]
lastHeight uint64
Store store.RootStore
txConfig client.TxConfig
}
// Deliver delivers a block with the given transactions and returns the resulting state.
func (a *App) Deliver(
t *testing.T, ctx context.Context, txs []stateMachineTx,
) (*server.BlockResponse, corestore.WriterMap) {
t.Helper()
req := &server.BlockRequest[stateMachineTx]{
Height: a.lastHeight + 1,
Txs: txs,
Hash: make([]byte, 32),
AppHash: make([]byte, 32),
}
resp, state, err := a.DeliverBlock(ctx, req)
require.NoError(t, err)
a.lastHeight++
return resp, state
}
// StateLatestContext creates returns a new context from context.Background() with the latest state.
func (a *App) StateLatestContext(t *testing.T) context.Context {
t.Helper()
_, state, err := a.Store.StateLatest()
require.NoError(t, err)
writeableState := branch.DefaultNewWriterMap(state)
iCtx := &integrationContext{state: writeableState}
return context.WithValue(context.Background(), contextKey, iCtx)
}
// Commit commits the given state and returns the new state hash.
func (a *App) Commit(state corestore.WriterMap) ([]byte, error) {
changes, err := state.GetStateChanges()
if err != nil {
return nil, fmt.Errorf("failed to get state changes: %w", err)
}
cs := &corestore.Changeset{Changes: changes}
return a.Store.Commit(cs)
}
// SignCheckDeliver signs and checks the given messages and delivers them.
func (a *App) SignCheckDeliver(
t *testing.T, ctx context.Context, msgs []sdk.Msg,
chainID string, accNums, accSeqs []uint64, privateKeys []cryptotypes.PrivKey,
txErrString string,
) server.TxResult {
t.Helper()
r := rand.New(rand.NewSource(time.Now().UnixNano()))
sigs := make([]signing.SignatureV2, len(privateKeys))
// create a random length memo
memo := simulation.RandStringOfLength(r, simulation.RandIntBetween(r, 0, 100))
signMode, err := authsign.APISignModeToInternal(a.txConfig.SignModeHandler().DefaultMode())
require.NoError(t, err)
// 1st round: set SignatureV2 with empty signatures, to set correct
// signer infos.
for i, p := range privateKeys {
sigs[i] = signing.SignatureV2{
PubKey: p.PubKey(),
Data: &signing.SingleSignatureData{
SignMode: signMode,
},
Sequence: accSeqs[i],
}
}
txBuilder := a.txConfig.NewTxBuilder()
err = txBuilder.SetMsgs(msgs...)
require.NoError(t, err)
err = txBuilder.SetSignatures(sigs...)
require.NoError(t, err)
txBuilder.SetMemo(memo)
txBuilder.SetFeeAmount(sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)})
txBuilder.SetGasLimit(DefaultGenTxGas)
// 2nd round: once all signer infos are set, every signer can sign.
for i, p := range privateKeys {
signerData := authsign.SignerData{
Address: sdk.AccAddress(p.PubKey().Address()).String(),
ChainID: chainID,
AccountNumber: accNums[i],
Sequence: accSeqs[i],
PubKey: p.PubKey(),
}
signBytes, err := authsign.GetSignBytesAdapter(
ctx, a.txConfig.SignModeHandler(), signMode, signerData,
// todo why fetch twice?
txBuilder.GetTx())
require.NoError(t, err)
sig, err := p.Sign(signBytes)
require.NoError(t, err)
sigs[i].Data.(*signing.SingleSignatureData).Signature = sig
}
err = txBuilder.SetSignatures(sigs...)
require.NoError(t, err)
builtTx := txBuilder.GetTx()
blockResponse, blockState := a.Deliver(t, ctx, []stateMachineTx{builtTx})
require.Equal(t, 1, len(blockResponse.TxResults))
txResult := blockResponse.TxResults[0]
if txErrString != "" {
require.ErrorContains(t, txResult.Error, txErrString)
} else {
require.NoError(t, txResult.Error)
}
_, err = a.Commit(blockState)
require.NoError(t, err)
return txResult
}
// CheckBalance checks the balance of the given address.
func (a *App) CheckBalance(
t *testing.T, ctx context.Context, addr sdk.AccAddress, expected sdk.Coins, keeper bankkeeper.Keeper,
) {
t.Helper()
balances := keeper.GetAllBalances(ctx, addr)
require.Equal(t, expected, balances)
}
func (a *App) Close() error {
return a.Store.Close()
}

View File

@ -0,0 +1,469 @@
package bank
import (
"testing"
"github.com/stretchr/testify/require"
secp256k1_internal "gitlab.com/yawning/secp256k1-voi"
"gitlab.com/yawning/secp256k1-voi/secec"
"cosmossdk.io/depinject"
"cosmossdk.io/log"
sdkmath "cosmossdk.io/math"
_ "cosmossdk.io/x/accounts"
_ "cosmossdk.io/x/bank"
bankkeeper "cosmossdk.io/x/bank/keeper"
"cosmossdk.io/x/bank/testutil"
"cosmossdk.io/x/bank/types"
_ "cosmossdk.io/x/consensus"
_ "cosmossdk.io/x/distribution"
distrkeeper "cosmossdk.io/x/distribution/keeper"
_ "cosmossdk.io/x/gov"
govv1 "cosmossdk.io/x/gov/types/v1"
_ "cosmossdk.io/x/protocolpool"
_ "cosmossdk.io/x/staking"
stakingtypes "cosmossdk.io/x/staking/types"
"github.com/cosmos/cosmos-sdk/client"
cdctestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/tests/integration/v2"
"github.com/cosmos/cosmos-sdk/testutil/configurator"
sdk "github.com/cosmos/cosmos-sdk/types"
_ "github.com/cosmos/cosmos-sdk/x/auth"
_ "github.com/cosmos/cosmos-sdk/x/auth/tx/config"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)
var (
stablePrivateKey, _ = secec.NewPrivateKeyFromScalar(secp256k1_internal.NewScalarFromUint64(100))
priv1 = &secp256k1.PrivKey{Key: stablePrivateKey.Bytes()}
addr1 = sdk.AccAddress(priv1.PubKey().Address())
priv2 = secp256k1.GenPrivKey()
addr2 = sdk.AccAddress(priv2.PubKey().Address())
addr3 = sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
coins = sdk.Coins{sdk.NewInt64Coin("foocoin", 10)}
halfCoins = sdk.Coins{sdk.NewInt64Coin("foocoin", 5)}
moduleAccAddr = authtypes.NewModuleAddress(stakingtypes.BondedPoolName)
)
type suite struct {
BankKeeper bankkeeper.Keeper
AccountKeeper types.AccountKeeper
DistributionKeeper distrkeeper.Keeper
App *integration.App
TxConfig client.TxConfig
}
type expectedBalance struct {
addr sdk.AccAddress
coins sdk.Coins
}
type appTestCase struct {
desc string
msgs []sdk.Msg
accNums []uint64
accSeqs []uint64
privKeys []cryptotypes.PrivKey
expectedBalances []expectedBalance
expInError []string
}
func createTestSuite(t *testing.T, genesisAccounts []authtypes.GenesisAccount) suite {
t.Helper()
res := suite{}
moduleConfigs := []configurator.ModuleOption{
configurator.AccountsModule(),
configurator.AuthModule(),
configurator.StakingModule(),
configurator.TxModule(),
configurator.ValidateModule(),
configurator.ConsensusModule(),
configurator.BankModule(),
configurator.GovModule(),
configurator.DistributionModule(),
configurator.ProtocolPoolModule(),
}
var err error
startupCfg := integration.DefaultStartUpConfig(t)
var genAccounts []integration.GenesisAccount
for _, acc := range genesisAccounts {
genAccounts = append(genAccounts, integration.GenesisAccount{GenesisAccount: acc})
}
startupCfg.GenesisAccounts = genAccounts
res.App, err = integration.NewApp(
depinject.Configs(configurator.NewAppV2Config(moduleConfigs...), depinject.Supply(log.NewNopLogger())),
startupCfg,
&res.BankKeeper, &res.AccountKeeper, &res.DistributionKeeper, &res.TxConfig)
require.NoError(t, err)
return res
}
func TestSendNotEnoughBalance(t *testing.T) {
acc := &authtypes.BaseAccount{
Address: addr1.String(),
}
genAccs := []authtypes.GenesisAccount{acc}
s := createTestSuite(t, genAccs)
ctx := s.App.StateLatestContext(t)
err := testutil.FundAccount(
ctx, s.BankKeeper, addr1,
sdk.NewCoins(sdk.NewInt64Coin("foocoin", 67)))
require.NoError(t, err)
res1 := s.AccountKeeper.GetAccount(ctx, addr1)
require.NotNil(t, res1)
require.Equal(t, acc, res1.(*authtypes.BaseAccount))
origAccNum := res1.GetAccountNumber()
origSeq := res1.GetSequence()
addr1Str, err := s.AccountKeeper.AddressCodec().BytesToString(addr1)
require.NoError(t, err)
addr2Str, err := s.AccountKeeper.AddressCodec().BytesToString(addr2)
require.NoError(t, err)
sendMsg := types.NewMsgSend(addr1Str, addr2Str, sdk.Coins{sdk.NewInt64Coin("foocoin", 100)})
// TODO how to auto-advance height with app v2 interface?
s.App.SignCheckDeliver(
t, ctx, []sdk.Msg{sendMsg}, "", []uint64{origAccNum}, []uint64{origSeq},
[]cryptotypes.PrivKey{priv1},
"spendable balance 67foocoin is smaller than 100foocoin",
)
s.App.CheckBalance(t, ctx, addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 67)}, s.BankKeeper)
res2 := s.AccountKeeper.GetAccount(ctx, addr1)
require.NotNil(t, res2)
require.Equal(t, origAccNum, res2.GetAccountNumber())
require.Equal(t, origSeq+1, res2.GetSequence())
}
func TestMsgMultiSendWithAccounts(t *testing.T) {
addr1Str, err := cdctestutil.CodecOptions{}.GetAddressCodec().BytesToString(addr1)
require.NoError(t, err)
acc := &authtypes.BaseAccount{
Address: addr1Str,
}
addr2Str, err := cdctestutil.CodecOptions{}.GetAddressCodec().BytesToString(addr2)
require.NoError(t, err)
moduleStrAddr, err := cdctestutil.CodecOptions{}.GetAddressCodec().BytesToString(moduleAccAddr)
require.NoError(t, err)
genAccs := []authtypes.GenesisAccount{acc}
s := createTestSuite(t, genAccs)
ctx := s.App.StateLatestContext(t)
require.NoError(t, testutil.FundAccount(ctx, s.BankKeeper, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 67))))
_, state := s.App.Deliver(t, ctx, nil)
_, err = s.App.Commit(state)
require.NoError(t, err)
res1 := s.AccountKeeper.GetAccount(ctx, addr1)
require.NotNil(t, res1)
require.Equal(t, acc, res1.(*authtypes.BaseAccount))
testCases := []appTestCase{
{
desc: "make a valid tx",
msgs: []sdk.Msg{&types.MsgMultiSend{
Inputs: []types.Input{types.NewInput(addr1Str, coins)},
Outputs: []types.Output{types.NewOutput(addr2Str, coins)},
}},
accNums: []uint64{0},
accSeqs: []uint64{0},
privKeys: []cryptotypes.PrivKey{priv1},
expectedBalances: []expectedBalance{
{addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 57)}},
{addr2, sdk.Coins{sdk.NewInt64Coin("foocoin", 10)}},
},
},
{
desc: "wrong accNum should pass Simulate, but not Deliver",
msgs: []sdk.Msg{&types.MsgMultiSend{
Inputs: []types.Input{types.NewInput(addr1Str, coins)},
Outputs: []types.Output{types.NewOutput(addr2Str, coins)},
}},
accNums: []uint64{1}, // wrong account number
accSeqs: []uint64{1},
expInError: []string{"signature verification failed; please verify account number"},
privKeys: []cryptotypes.PrivKey{priv1},
},
{
desc: "wrong accSeq should not pass Simulate",
msgs: []sdk.Msg{&types.MsgMultiSend{
Inputs: []types.Input{types.NewInput(addr1Str, coins)},
Outputs: []types.Output{
types.NewOutput(moduleStrAddr, coins),
},
}},
accNums: []uint64{0},
accSeqs: []uint64{0}, // wrong account sequence
expInError: []string{"account sequence mismatch"},
privKeys: []cryptotypes.PrivKey{priv1},
},
{
desc: "multiple inputs not allowed",
msgs: []sdk.Msg{&types.MsgMultiSend{
Inputs: []types.Input{types.NewInput(addr1Str, coins), types.NewInput(addr2Str, coins)},
Outputs: []types.Output{},
}},
accNums: []uint64{0},
accSeqs: []uint64{0},
expInError: []string{"invalid number of signatures"},
privKeys: []cryptotypes.PrivKey{priv1},
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
var errString string
if len(tc.expInError) > 0 {
errString = tc.expInError[0]
}
s.App.SignCheckDeliver(t, ctx, tc.msgs, "", tc.accNums, tc.accSeqs, tc.privKeys, errString)
for _, eb := range tc.expectedBalances {
s.App.CheckBalance(t, ctx, eb.addr, eb.coins, s.BankKeeper)
}
})
}
}
func TestMsgMultiSendMultipleOut(t *testing.T) {
ac := cdctestutil.CodecOptions{}.GetAddressCodec()
addr1Str, err := ac.BytesToString(addr1)
require.NoError(t, err)
acc1 := &authtypes.BaseAccount{
Address: addr1Str,
}
addr2Str, err := ac.BytesToString(addr2)
require.NoError(t, err)
acc2 := &authtypes.BaseAccount{
Address: addr2Str,
}
addr3Str, err := ac.BytesToString(addr3)
require.NoError(t, err)
genAccs := []authtypes.GenesisAccount{acc1, acc2}
s := createTestSuite(t, genAccs)
ctx := s.App.StateLatestContext(t)
require.NoError(t, testutil.FundAccount(ctx, s.BankKeeper, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42))))
require.NoError(t, testutil.FundAccount(ctx, s.BankKeeper, addr2, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42))))
_, state := s.App.Deliver(t, ctx, nil)
_, err = s.App.Commit(state)
require.NoError(t, err)
testCases := []appTestCase{
{
msgs: []sdk.Msg{&types.MsgMultiSend{
Inputs: []types.Input{types.NewInput(addr1Str, coins)},
Outputs: []types.Output{
types.NewOutput(addr2Str, halfCoins),
types.NewOutput(addr3Str, halfCoins),
},
}},
accNums: []uint64{0},
accSeqs: []uint64{0},
privKeys: []cryptotypes.PrivKey{priv1},
expectedBalances: []expectedBalance{
{addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 32)}},
{addr2, sdk.Coins{sdk.NewInt64Coin("foocoin", 47)}},
{addr3, sdk.Coins{sdk.NewInt64Coin("foocoin", 5)}},
},
},
}
for _, tc := range testCases {
s.App.SignCheckDeliver(t, ctx, tc.msgs, "", tc.accNums, tc.accSeqs, tc.privKeys, "")
for _, eb := range tc.expectedBalances {
s.App.CheckBalance(t, ctx, eb.addr, eb.coins, s.BankKeeper)
}
}
}
func TestMsgMultiSendDependent(t *testing.T) {
ac := cdctestutil.CodecOptions{}.GetAddressCodec()
addr1Str, err := ac.BytesToString(addr1)
require.NoError(t, err)
addr2Str, err := ac.BytesToString(addr2)
require.NoError(t, err)
acc1 := authtypes.NewBaseAccountWithAddress(addr1)
acc2 := authtypes.NewBaseAccountWithAddress(addr2)
err = acc2.SetAccountNumber(1)
require.NoError(t, err)
genAccs := []authtypes.GenesisAccount{acc1, acc2}
s := createTestSuite(t, genAccs)
ctx := s.App.StateLatestContext(t)
require.NoError(t, testutil.FundAccount(ctx, s.BankKeeper, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42))))
_, state := s.App.Deliver(t, ctx, nil)
_, err = s.App.Commit(state)
require.NoError(t, err)
testCases := []appTestCase{
{
msgs: []sdk.Msg{&types.MsgMultiSend{
Inputs: []types.Input{types.NewInput(addr1Str, coins)},
Outputs: []types.Output{types.NewOutput(addr2Str, coins)},
}},
accNums: []uint64{0},
accSeqs: []uint64{0},
privKeys: []cryptotypes.PrivKey{priv1},
expectedBalances: []expectedBalance{
{addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 32)}},
{addr2, sdk.Coins{sdk.NewInt64Coin("foocoin", 10)}},
},
},
{
msgs: []sdk.Msg{&types.MsgMultiSend{
Inputs: []types.Input{types.NewInput(addr2Str, coins)},
Outputs: []types.Output{
types.NewOutput(addr1Str, coins),
},
}},
accNums: []uint64{1},
accSeqs: []uint64{0},
privKeys: []cryptotypes.PrivKey{priv2},
expectedBalances: []expectedBalance{
{addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 42)}},
},
},
}
for _, tc := range testCases {
s.App.SignCheckDeliver(t, ctx, tc.msgs, "", tc.accNums, tc.accSeqs, tc.privKeys, "")
for _, eb := range tc.expectedBalances {
s.App.CheckBalance(t, ctx, eb.addr, eb.coins, s.BankKeeper)
}
}
}
func TestMsgSetSendEnabled(t *testing.T) {
acc1 := authtypes.NewBaseAccountWithAddress(addr1)
genAccs := []authtypes.GenesisAccount{acc1}
s := createTestSuite(t, genAccs)
ctx := s.App.StateLatestContext(t)
require.NoError(t, testutil.FundAccount(ctx, s.BankKeeper, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 101))))
require.NoError(t, testutil.FundAccount(ctx, s.BankKeeper, addr1, sdk.NewCoins(sdk.NewInt64Coin("stake", 100000))))
addr1Str := addr1.String()
govAddr := s.BankKeeper.GetAuthority()
goodGovProp, err := govv1.NewMsgSubmitProposal(
[]sdk.Msg{
types.NewMsgSetSendEnabled(govAddr, nil, nil),
},
sdk.Coins{{Denom: "stake", Amount: sdkmath.NewInt(100000)}},
addr1Str,
"set default send enabled to true",
"Change send enabled",
"Modify send enabled and set to true",
govv1.ProposalType_PROPOSAL_TYPE_STANDARD,
)
require.NoError(t, err, "making goodGovProp")
testCases := []appTestCase{
{
desc: "wrong authority",
msgs: []sdk.Msg{
types.NewMsgSetSendEnabled(addr1Str, nil, nil),
},
accSeqs: []uint64{0},
expInError: []string{
"invalid authority",
"cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn",
addr1Str,
"expected authority account as only signer for proposal message",
},
},
{
desc: "right authority wrong signer",
msgs: []sdk.Msg{
types.NewMsgSetSendEnabled(govAddr, nil, nil),
},
accSeqs: []uint64{1}, // wrong signer, so this sequence doesn't actually get used.
expInError: []string{
"cannot be claimed by public key with address",
govAddr,
},
},
{
desc: "submitted good as gov prop",
msgs: []sdk.Msg{
goodGovProp,
},
accSeqs: []uint64{1},
expInError: nil,
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(tt *testing.T) {
var errString string
if len(tc.expInError) > 0 {
errString = tc.expInError[0]
}
txResult := s.App.SignCheckDeliver(
tt, ctx, tc.msgs, "", []uint64{0}, tc.accSeqs, []cryptotypes.PrivKey{priv1}, errString)
if len(tc.expInError) > 0 {
require.Error(tt, txResult.Error)
for _, exp := range tc.expInError {
require.ErrorContains(tt, txResult.Error, exp)
}
} else {
require.NoError(tt, txResult.Error)
}
})
}
}
// TestSendToNonExistingAccount tests sending coins to an account that does not exist, and this account
// must not be created.
func TestSendToNonExistingAccount(t *testing.T) {
acc1 := authtypes.NewBaseAccountWithAddress(addr1)
genAccs := []authtypes.GenesisAccount{acc1}
s := createTestSuite(t, genAccs)
ctx := s.App.StateLatestContext(t)
require.NoError(t, testutil.FundAccount(ctx, s.BankKeeper, addr1, sdk.NewCoins(sdk.NewInt64Coin("foocoin", 42))))
_, state := s.App.Deliver(t, ctx, nil)
_, err := s.App.Commit(state)
require.NoError(t, err)
addr2Str, err := s.AccountKeeper.AddressCodec().BytesToString(addr2)
require.NoError(t, err)
sendMsg := types.NewMsgSend(addr1.String(), addr2Str, coins)
res := s.App.SignCheckDeliver(t, ctx, []sdk.Msg{sendMsg}, "", []uint64{0}, []uint64{0}, []cryptotypes.PrivKey{priv1}, "")
require.NoError(t, res.Error)
// Check that the account was not created
acc2 := s.AccountKeeper.GetAccount(ctx, addr2)
require.Nil(t, acc2)
// But it does have a balance
s.App.CheckBalance(t, ctx, addr2, coins, s.BankKeeper)
// Now we send coins back and the account should be created
sendMsg = types.NewMsgSend(addr2Str, addr1.String(), coins)
res = s.App.SignCheckDeliver(t, ctx, []sdk.Msg{sendMsg}, "", []uint64{0}, []uint64{0}, []cryptotypes.PrivKey{priv2}, "")
require.NoError(t, res.Error)
// Balance has been reduced
s.App.CheckBalance(t, ctx, addr2, sdk.NewCoins(), s.BankKeeper)
// Check that the account was created
acc2 = s.AccountKeeper.GetAccount(ctx, addr2)
require.NotNil(t, acc2, "account should have been created %s", addr2.String())
}

View File

@ -0,0 +1,570 @@
package bank
import (
"context"
"fmt"
"testing"
"github.com/cosmos/gogoproto/proto"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"pgregory.net/rapid"
"cosmossdk.io/core/gas"
"cosmossdk.io/depinject"
"cosmossdk.io/log"
"cosmossdk.io/math"
bankkeeper "cosmossdk.io/x/bank/keeper"
banktestutil "cosmossdk.io/x/bank/testutil"
banktypes "cosmossdk.io/x/bank/types"
minttypes "cosmossdk.io/x/mint/types"
codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
"github.com/cosmos/cosmos-sdk/tests/integration/v2"
"github.com/cosmos/cosmos-sdk/testutil/configurator"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
authtestutil "github.com/cosmos/cosmos-sdk/x/auth/testutil"
)
var (
denomRegex = `[a-zA-Z][a-zA-Z0-9/:._-]{2,127}`
coin1 = sdk.NewCoin("denom", math.NewInt(10))
metadataAtom = banktypes.Metadata{
Description: "The native staking token of the Cosmos Hub.",
DenomUnits: []*banktypes.DenomUnit{
{
Denom: "uatom",
Exponent: 0,
Aliases: []string{"microatom"},
},
{
Denom: "atom",
Exponent: 6,
Aliases: []string{"ATOM"},
},
},
Base: "uatom",
Display: "atom",
}
)
type deterministicFixture struct {
*testing.T
ctx context.Context
app *integration.App
bankKeeper bankkeeper.Keeper
}
func queryFnFactory[RequestT, ResponseT proto.Message](
f *deterministicFixture,
) func(RequestT) (ResponseT, error) {
return func(req RequestT) (ResponseT, error) {
var emptyResponse ResponseT
res, err := f.app.Query(f.ctx, 0, req)
if err != nil {
return emptyResponse, err
}
castedRes, ok := res.(ResponseT)
if !ok {
return emptyResponse, fmt.Errorf("unexpected response type: %T", res)
}
return castedRes, nil
}
}
func fundAccount(f *deterministicFixture, addr sdk.AccAddress, coin ...sdk.Coin) {
err := banktestutil.FundAccount(f.ctx, f.bankKeeper, addr, sdk.NewCoins(coin...))
require.NoError(f.T, err)
}
func getCoin(rt *rapid.T) sdk.Coin {
return sdk.NewCoin(
rapid.StringMatching(denomRegex).Draw(rt, "denom"),
math.NewInt(rapid.Int64Min(1).Draw(rt, "amount")),
)
}
func initDeterministicFixture(t *testing.T) *deterministicFixture {
t.Helper()
ctrl := gomock.NewController(t)
acctsModKeeper := authtestutil.NewMockAccountsModKeeper(ctrl)
accNum := uint64(0)
acctsModKeeper.EXPECT().NextAccountNumber(gomock.Any()).AnyTimes().DoAndReturn(func(ctx context.Context) (
uint64, error,
) {
currentNum := accNum
accNum++
return currentNum, nil
})
startupConfig := integration.DefaultStartUpConfig(t)
startupConfig.GenesisBehavior = integration.Genesis_SKIP
diConfig := configurator.NewAppV2Config(
configurator.TxModule(),
configurator.AuthModule(),
configurator.BankModule(),
)
var bankKeeper bankkeeper.Keeper
diConfig = depinject.Configs(diConfig, depinject.Supply(acctsModKeeper, log.NewNopLogger()))
app, err := integration.NewApp(diConfig, startupConfig, &bankKeeper)
require.NoError(t, err)
require.NotNil(t, app)
return &deterministicFixture{app: app, bankKeeper: bankKeeper, T: t}
}
func assertNonZeroGas(t *testing.T, gasUsed gas.Gas) {
t.Helper()
require.NotZero(t, gasUsed)
}
func TestQueryBalance(t *testing.T) {
t.Parallel()
f := initDeterministicFixture(t)
f.ctx = f.app.StateLatestContext(t)
gasMeterFactory := integration.GasMeterFactory(f.ctx)
queryFn := queryFnFactory[*banktypes.QueryBalanceRequest, *banktypes.QueryBalanceResponse](f)
assertBalance := func(coin sdk.Coin) func(t *testing.T, res *banktypes.QueryBalanceResponse) {
return func(t *testing.T, res *banktypes.QueryBalanceResponse) {
t.Helper()
require.Equal(t, coin.Denom, res.Balance.Denom)
require.Truef(t, coin.Amount.Equal(res.Balance.Amount),
"expected %s, got %s", coin.Amount, res.Balance.Amount)
}
}
rapid.Check(t, func(rt *rapid.T) {
addr := testdata.AddressGenerator(rt).Draw(rt, "address")
coin := getCoin(rt)
fundAccount(f, addr, coin)
addrStr, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(addr)
require.NoError(t, err)
req := banktypes.NewQueryBalanceRequest(addrStr, coin.GetDenom())
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, assertBalance(coin))
})
fundAccount(f, addr1, coin1)
addr1Str, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(addr1)
require.NoError(t, err)
req := banktypes.NewQueryBalanceRequest(addr1Str, coin1.GetDenom())
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, assertBalance(coin1))
}
func TestQueryAllBalances(t *testing.T) {
t.Parallel()
f := initDeterministicFixture(t)
f.ctx = f.app.StateLatestContext(t)
gasMeterFactory := integration.GasMeterFactory(f.ctx)
addressCodec := codectestutil.CodecOptions{}.GetAddressCodec()
queryFn := queryFnFactory[*banktypes.QueryAllBalancesRequest, *banktypes.QueryAllBalancesResponse](f)
rapid.Check(t, func(rt *rapid.T) {
addr := testdata.AddressGenerator(rt).Draw(rt, "address")
numCoins := rapid.IntRange(1, 10).Draw(rt, "num-count")
coins := make(sdk.Coins, 0, numCoins)
addrStr, err := addressCodec.BytesToString(addr)
require.NoError(t, err)
for i := 0; i < numCoins; i++ {
coin := getCoin(rt)
if exists, _ := coins.Find(coin.Denom); exists {
t.Skip("duplicate denom")
}
// NewCoins sorts the denoms
coins = sdk.NewCoins(append(coins, coin)...)
}
fundAccount(f, addr, coins...)
req := banktypes.NewQueryAllBalancesRequest(
addrStr, testdata.PaginationGenerator(rt, uint64(numCoins)).Draw(rt, "pagination"), false)
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
})
coins := sdk.NewCoins(
sdk.NewCoin("stake", math.NewInt(10)),
sdk.NewCoin("denom", math.NewInt(100)),
)
fundAccount(f, addr1, coins...)
addr1Str, err := addressCodec.BytesToString(addr1)
require.NoError(t, err)
req := banktypes.NewQueryAllBalancesRequest(addr1Str, nil, false)
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
}
func TestQuerySpendableBalances(t *testing.T) {
t.Parallel()
f := initDeterministicFixture(t)
f.ctx = f.app.StateLatestContext(t)
gasMeterFactory := integration.GasMeterFactory(f.ctx)
queryFn := queryFnFactory[*banktypes.QuerySpendableBalancesRequest, *banktypes.QuerySpendableBalancesResponse](f)
rapid.Check(t, func(rt *rapid.T) {
addr := testdata.AddressGenerator(rt).Draw(rt, "address")
addrStr, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(addr)
require.NoError(t, err)
// Denoms must be unique, otherwise sdk.NewCoins will panic.
denoms := rapid.SliceOfNDistinct(rapid.StringMatching(denomRegex), 1, 10, rapid.ID[string]).Draw(rt, "denoms")
coins := make(sdk.Coins, 0, len(denoms))
for _, denom := range denoms {
coin := sdk.NewCoin(
denom,
math.NewInt(rapid.Int64Min(1).Draw(rt, "amount")),
)
// NewCoins sorts the denoms
coins = sdk.NewCoins(append(coins, coin)...)
}
err = banktestutil.FundAccount(f.ctx, f.bankKeeper, addr, coins)
require.NoError(t, err)
req := banktypes.NewQuerySpendableBalancesRequest(addrStr, testdata.PaginationGenerator(rt, uint64(len(denoms))).Draw(rt, "pagination"))
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
})
coins := sdk.NewCoins(
sdk.NewCoin("stake", math.NewInt(10)),
sdk.NewCoin("denom", math.NewInt(100)),
)
err := banktestutil.FundAccount(f.ctx, f.bankKeeper, addr1, coins)
require.NoError(t, err)
addr1Str, err := codectestutil.CodecOptions{}.GetAddressCodec().BytesToString(addr1)
require.NoError(t, err)
req := banktypes.NewQuerySpendableBalancesRequest(addr1Str, nil)
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
}
func TestQueryTotalSupply(t *testing.T) {
t.Parallel()
f := initDeterministicFixture(t)
f.ctx = f.app.StateLatestContext(t)
gasMeterFactory := integration.GasMeterFactory(f.ctx)
queryFn := queryFnFactory[*banktypes.QueryTotalSupplyRequest, *banktypes.QueryTotalSupplyResponse](f)
res, err := queryFn(&banktypes.QueryTotalSupplyRequest{})
require.NoError(t, err)
initialSupply := res.GetSupply()
rapid.Check(t, func(rt *rapid.T) {
numCoins := rapid.IntRange(1, 3).Draw(rt, "num-count")
coins := make(sdk.Coins, 0, numCoins)
for i := 0; i < numCoins; i++ {
coin := sdk.NewCoin(
rapid.StringMatching(denomRegex).Draw(rt, "denom"),
math.NewInt(rapid.Int64Min(1).Draw(rt, "amount")),
)
coins = coins.Add(coin)
}
require.NoError(t, f.bankKeeper.MintCoins(f.ctx, minttypes.ModuleName, coins))
initialSupply = initialSupply.Add(coins...)
req := &banktypes.QueryTotalSupplyRequest{
Pagination: testdata.PaginationGenerator(rt, uint64(len(initialSupply))).Draw(rt, "pagination"),
}
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
})
f = initDeterministicFixture(t) // reset
f.ctx = f.app.StateLatestContext(t)
gasMeterFactory = integration.GasMeterFactory(f.ctx)
queryFn = queryFnFactory[*banktypes.QueryTotalSupplyRequest, *banktypes.QueryTotalSupplyResponse](f)
coins := sdk.NewCoins(
sdk.NewCoin("foo", math.NewInt(10)),
sdk.NewCoin("bar", math.NewInt(100)),
)
require.NoError(t, f.bankKeeper.MintCoins(f.ctx, minttypes.ModuleName, coins))
req := &banktypes.QueryTotalSupplyRequest{}
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
}
func TestQueryTotalSupplyOf(t *testing.T) {
t.Parallel()
f := initDeterministicFixture(t)
f.ctx = f.app.StateLatestContext(t)
gasMeterFactory := integration.GasMeterFactory(f.ctx)
queryFn := queryFnFactory[*banktypes.QuerySupplyOfRequest, *banktypes.QuerySupplyOfResponse](f)
rapid.Check(t, func(rt *rapid.T) {
coin := sdk.NewCoin(
rapid.StringMatching(denomRegex).Draw(rt, "denom"),
math.NewInt(rapid.Int64Min(1).Draw(rt, "amount")),
)
require.NoError(t, f.bankKeeper.MintCoins(f.ctx, minttypes.ModuleName, sdk.NewCoins(coin)))
req := &banktypes.QuerySupplyOfRequest{Denom: coin.GetDenom()}
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
})
coin := sdk.NewCoin("bar", math.NewInt(100))
require.NoError(t, f.bankKeeper.MintCoins(f.ctx, minttypes.ModuleName, sdk.NewCoins(coin)))
req := &banktypes.QuerySupplyOfRequest{Denom: coin.GetDenom()}
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
}
func TestQueryParams(t *testing.T) {
t.Parallel()
f := initDeterministicFixture(t)
f.ctx = f.app.StateLatestContext(t)
gasMeterFactory := integration.GasMeterFactory(f.ctx)
queryFn := queryFnFactory[*banktypes.QueryParamsRequest, *banktypes.QueryParamsResponse](f)
rapid.Check(t, func(rt *rapid.T) {
enabledStatus := banktypes.SendEnabled{
Denom: rapid.StringMatching(denomRegex).Draw(rt, "denom"),
Enabled: rapid.Bool().Draw(rt, "status"),
}
params := banktypes.Params{
SendEnabled: []*banktypes.SendEnabled{&enabledStatus},
DefaultSendEnabled: rapid.Bool().Draw(rt, "send"),
}
err := f.bankKeeper.SetParams(f.ctx, params)
require.NoError(t, err)
req := &banktypes.QueryParamsRequest{}
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
})
enabledStatus := banktypes.SendEnabled{
Denom: "denom",
Enabled: true,
}
params := banktypes.Params{
SendEnabled: []*banktypes.SendEnabled{&enabledStatus},
DefaultSendEnabled: false,
}
err := f.bankKeeper.SetParams(f.ctx, params)
require.NoError(t, err)
req := &banktypes.QueryParamsRequest{}
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
}
func createAndReturnMetadatas(t *rapid.T, count int) []banktypes.Metadata {
denomsMetadata := make([]banktypes.Metadata, 0, count)
for i := 0; i < count; i++ {
denom := rapid.StringMatching(denomRegex).Draw(t, "denom")
aliases := rapid.SliceOf(rapid.String()).Draw(t, "aliases")
// In the GRPC server code, empty arrays are returned as nil
if len(aliases) == 0 {
aliases = nil
}
metadata := banktypes.Metadata{
Description: rapid.StringN(1, 100, 100).Draw(t, "desc"),
DenomUnits: []*banktypes.DenomUnit{
{
Denom: denom,
Exponent: rapid.Uint32().Draw(t, "exponent"),
Aliases: aliases,
},
},
Base: denom,
Display: denom,
Name: rapid.String().Draw(t, "name"),
Symbol: rapid.String().Draw(t, "symbol"),
URI: rapid.String().Draw(t, "uri"),
URIHash: rapid.String().Draw(t, "uri-hash"),
}
denomsMetadata = append(denomsMetadata, metadata)
}
return denomsMetadata
}
func TestDenomsMetadata(t *testing.T) {
t.Parallel()
f := initDeterministicFixture(t)
f.ctx = f.app.StateLatestContext(t)
gasMeterFactory := integration.GasMeterFactory(f.ctx)
queryFn := queryFnFactory[*banktypes.QueryDenomsMetadataRequest, *banktypes.QueryDenomsMetadataResponse](f)
rapid.Check(t, func(rt *rapid.T) {
count := rapid.IntRange(1, 3).Draw(rt, "count")
denomsMetadata := createAndReturnMetadatas(rt, count)
require.True(t, len(denomsMetadata) == count)
for i := 0; i < count; i++ {
f.bankKeeper.SetDenomMetaData(f.ctx, denomsMetadata[i])
}
req := &banktypes.QueryDenomsMetadataRequest{
Pagination: testdata.PaginationGenerator(rt, uint64(count)).Draw(rt, "pagination"),
}
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
})
require.NoError(t, f.app.Close())
f = initDeterministicFixture(t) // reset
f.ctx = f.app.StateLatestContext(t)
gasMeterFactory = integration.GasMeterFactory(f.ctx)
queryFn = queryFnFactory[*banktypes.QueryDenomsMetadataRequest, *banktypes.QueryDenomsMetadataResponse](f)
f.bankKeeper.SetDenomMetaData(f.ctx, metadataAtom)
req := &banktypes.QueryDenomsMetadataRequest{}
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
}
func TestDenomMetadata(t *testing.T) {
t.Parallel()
f := initDeterministicFixture(t)
f.ctx = f.app.StateLatestContext(t)
gasMeterFactory := integration.GasMeterFactory(f.ctx)
queryFn := queryFnFactory[*banktypes.QueryDenomMetadataRequest, *banktypes.QueryDenomMetadataResponse](f)
rapid.Check(t, func(rt *rapid.T) {
denomMetadata := createAndReturnMetadatas(rt, 1)
require.True(t, len(denomMetadata) == 1)
f.bankKeeper.SetDenomMetaData(f.ctx, denomMetadata[0])
req := &banktypes.QueryDenomMetadataRequest{
Denom: denomMetadata[0].Base,
}
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
})
f.bankKeeper.SetDenomMetaData(f.ctx, metadataAtom)
req := &banktypes.QueryDenomMetadataRequest{
Denom: metadataAtom.Base,
}
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
}
func TestSendEnabled(t *testing.T) {
t.Parallel()
f := initDeterministicFixture(t)
f.ctx = f.app.StateLatestContext(t)
gasMeterFactory := integration.GasMeterFactory(f.ctx)
queryFn := queryFnFactory[*banktypes.QuerySendEnabledRequest, *banktypes.QuerySendEnabledResponse](f)
allDenoms := []string{}
rapid.Check(t, func(rt *rapid.T) {
count := rapid.IntRange(0, 10).Draw(rt, "count")
denoms := make([]string, 0, count)
for i := 0; i < count; i++ {
coin := banktypes.SendEnabled{
Denom: rapid.StringMatching(denomRegex).Draw(rt, "denom"),
Enabled: rapid.Bool().Draw(rt, "enabled-status"),
}
f.bankKeeper.SetSendEnabled(f.ctx, coin.Denom, coin.Enabled)
denoms = append(denoms, coin.Denom)
}
allDenoms = append(allDenoms, denoms...)
req := &banktypes.QuerySendEnabledRequest{
Denoms: denoms,
// Pagination is only taken into account when `denoms` is an empty array
Pagination: testdata.PaginationGenerator(rt, uint64(len(allDenoms))).Draw(rt, "pagination"),
}
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
})
coin1 := banktypes.SendEnabled{
Denom: "falsecoin",
Enabled: false,
}
coin2 := banktypes.SendEnabled{
Denom: "truecoin",
Enabled: true,
}
f.bankKeeper.SetSendEnabled(f.ctx, coin1.Denom, false)
f.bankKeeper.SetSendEnabled(f.ctx, coin2.Denom, true)
req := &banktypes.QuerySendEnabledRequest{
Denoms: []string{coin1.GetDenom(), coin2.GetDenom()},
}
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
}
func TestDenomOwners(t *testing.T) {
t.Parallel()
f := initDeterministicFixture(t)
f.ctx = f.app.StateLatestContext(t)
gasMeterFactory := integration.GasMeterFactory(f.ctx)
queryFn := queryFnFactory[*banktypes.QueryDenomOwnersRequest, *banktypes.QueryDenomOwnersResponse](f)
rapid.Check(t, func(rt *rapid.T) {
denom := rapid.StringMatching(denomRegex).Draw(rt, "denom")
numAddr := rapid.IntRange(1, 10).Draw(rt, "number-address")
for i := 0; i < numAddr; i++ {
addr := testdata.AddressGenerator(rt).Draw(rt, "address")
coin := sdk.NewCoin(
denom,
math.NewInt(rapid.Int64Min(1).Draw(rt, "amount")),
)
err := banktestutil.FundAccount(f.ctx, f.bankKeeper, addr, sdk.NewCoins(coin))
require.NoError(t, err)
}
req := &banktypes.QueryDenomOwnersRequest{
Denom: denom,
Pagination: testdata.PaginationGenerator(rt, uint64(numAddr)).Draw(rt, "pagination"),
}
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
})
denomOwners := []*banktypes.DenomOwner{
{
Address: "cosmos1qg65a9q6k2sqq7l3ycp428sqqpmqcucgzze299",
Balance: coin1,
},
{
Address: "cosmos1qglnsqgpq48l7qqzgs8qdshr6fh3gqq9ej3qut",
Balance: coin1,
},
}
for i := 0; i < len(denomOwners); i++ {
addr, err := sdk.AccAddressFromBech32(denomOwners[i].Address)
require.NoError(t, err)
err = banktestutil.FundAccount(f.ctx, f.bankKeeper, addr, sdk.NewCoins(coin1))
require.NoError(t, err)
}
req := &banktypes.QueryDenomOwnersRequest{
Denom: coin1.GetDenom(),
}
testdata.DeterministicIterationsV2(t, req, gasMeterFactory, queryFn, assertNonZeroGas, nil)
}

View File

@ -0,0 +1,182 @@
package integration
import (
"encoding/json"
"errors"
"fmt"
"time"
cmttypes "github.com/cometbft/cometbft/types"
sdkmath "cosmossdk.io/math"
banktypes "cosmossdk.io/x/bank/types"
stakingtypes "cosmossdk.io/x/staking/types"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
"github.com/cosmos/cosmos-sdk/testutil/mock"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/tx"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)
// genesisStateWithValSet returns a new genesis state with the validator set
func genesisStateWithValSet(
codec codec.Codec,
genesisState map[string]json.RawMessage,
valSet *cmttypes.ValidatorSet,
genAccs []authtypes.GenesisAccount,
balances ...banktypes.Balance,
) (map[string]json.RawMessage, error) {
if len(genAccs) == 0 {
return nil, errors.New("no genesis accounts provided")
}
// set genesis accounts
authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs)
genesisState[authtypes.ModuleName] = codec.MustMarshalJSON(authGenesis)
validators := make([]stakingtypes.Validator, 0, len(valSet.Validators))
delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators))
bondAmt := sdk.DefaultPowerReduction
for _, val := range valSet.Validators {
pk, err := cryptocodec.FromCmtPubKeyInterface(val.PubKey)
if err != nil {
return nil, fmt.Errorf("failed to convert pubkey: %w", err)
}
pkAny, err := codectypes.NewAnyWithValue(pk)
if err != nil {
return nil, fmt.Errorf("failed to create new any: %w", err)
}
validator := stakingtypes.Validator{
OperatorAddress: sdk.ValAddress(val.Address).String(),
ConsensusPubkey: pkAny,
Jailed: false,
Status: stakingtypes.Bonded,
Tokens: bondAmt,
DelegatorShares: sdkmath.LegacyOneDec(),
Description: stakingtypes.Description{},
UnbondingHeight: int64(0),
UnbondingTime: time.Unix(0, 0).UTC(),
Commission: stakingtypes.NewCommission(
sdkmath.LegacyZeroDec(),
sdkmath.LegacyZeroDec(),
sdkmath.LegacyZeroDec(),
),
MinSelfDelegation: sdkmath.ZeroInt(),
}
validators = append(validators, validator)
delegations = append(
delegations,
stakingtypes.NewDelegation(
genAccs[0].GetAddress().String(),
sdk.ValAddress(val.Address).String(),
sdkmath.LegacyOneDec(),
),
)
}
// set validators and delegations
stakingGenesis := stakingtypes.NewGenesisState(
stakingtypes.DefaultParams(),
validators,
delegations,
)
genesisState[stakingtypes.ModuleName] = codec.MustMarshalJSON(
stakingGenesis,
)
totalSupply := sdk.NewCoins()
for _, b := range balances {
// add genesis acc tokens to total supply
totalSupply = totalSupply.Add(b.Coins...)
}
for range delegations {
// add delegated tokens to total supply
totalSupply = totalSupply.Add(
sdk.NewCoin(sdk.DefaultBondDenom, bondAmt),
)
}
// add bonded amount to bonded pool module account
balances = append(balances, banktypes.Balance{
Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).
String(),
Coins: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)},
})
// update total supply
bankGenesis := banktypes.NewGenesisState(
banktypes.DefaultGenesisState().Params,
balances,
totalSupply,
[]banktypes.Metadata{},
[]banktypes.SendEnabled{},
)
genesisState[banktypes.ModuleName] = codec.MustMarshalJSON(bankGenesis)
return genesisState, nil
}
// CreateRandomValidatorSet creates a validator set with one random validator
func CreateRandomValidatorSet() (*cmttypes.ValidatorSet, error) {
privVal := mock.NewPV()
pubKey, err := privVal.GetPubKey()
if err != nil {
return nil, fmt.Errorf("failed to get pub key: %w", err)
}
// create validator set with single validator
validator := cmttypes.NewValidator(pubKey, 1)
return cmttypes.NewValidatorSet([]*cmttypes.Validator{validator}), nil
}
type GenesisAccount struct {
authtypes.GenesisAccount
Coins sdk.Coins
}
type genesisTxCodec struct {
tx.ConfigOptions
}
// Decode implements transaction.Codec.
func (t *genesisTxCodec) Decode(bz []byte) (stateMachineTx, error) {
var out stateMachineTx
tx, err := t.ProtoDecoder(bz)
if err != nil {
return out, err
}
var ok bool
out, ok = tx.(stateMachineTx)
if !ok {
return out, errors.New("unexpected Tx type")
}
return out, nil
}
// DecodeJSON implements transaction.Codec.
func (t *genesisTxCodec) DecodeJSON(bz []byte) (stateMachineTx, error) {
var out stateMachineTx
tx, err := t.JSONDecoder(bz)
if err != nil {
return out, err
}
var ok bool
out, ok = tx.(stateMachineTx)
if !ok {
return out, errors.New("unexpected Tx type")
}
return out, nil
}

View File

@ -0,0 +1,119 @@
package integration
import (
"context"
"fmt"
"cosmossdk.io/core/comet"
"cosmossdk.io/core/event"
"cosmossdk.io/core/gas"
"cosmossdk.io/core/server"
corestore "cosmossdk.io/core/store"
"cosmossdk.io/core/transaction"
stfgas "cosmossdk.io/server/v2/stf/gas"
)
func (c cometServiceImpl) CometInfo(context.Context) comet.Info {
return comet.Info{}
}
// Services
var _ server.DynamicConfig = &dynamicConfigImpl{}
type dynamicConfigImpl struct {
homeDir string
}
func (d *dynamicConfigImpl) Get(key string) any {
return d.GetString(key)
}
func (d *dynamicConfigImpl) GetString(key string) string {
switch key {
case "home":
return d.homeDir
case "store.app-db-backend":
return "goleveldb"
case "server.minimum-gas-prices":
return "0stake"
default:
panic(fmt.Sprintf("unknown key: %s", key))
}
}
func (d *dynamicConfigImpl) UnmarshalSub(string, any) (bool, error) {
return false, nil
}
var _ comet.Service = &cometServiceImpl{}
type cometServiceImpl struct{}
type storeService struct {
actor []byte
executionService corestore.KVStoreService
}
type contextKeyType struct{}
var contextKey = contextKeyType{}
type integrationContext struct {
state corestore.WriterMap
gasMeter gas.Meter
}
func GasMeterFromContext(ctx context.Context) gas.Meter {
iCtx, ok := ctx.Value(contextKey).(*integrationContext)
if !ok {
return nil
}
return iCtx.gasMeter
}
func GasMeterFactory(ctx context.Context) func() gas.Meter {
return func() gas.Meter {
return GasMeterFromContext(ctx)
}
}
func (s storeService) OpenKVStore(ctx context.Context) corestore.KVStore {
const gasLimit = 100_000
iCtx, ok := ctx.Value(contextKey).(*integrationContext)
if !ok {
return s.executionService.OpenKVStore(ctx)
}
iCtx.gasMeter = stfgas.NewMeter(gasLimit)
writerMap := stfgas.NewMeteredWriterMap(stfgas.DefaultConfig, iCtx.gasMeter, iCtx.state)
state, err := writerMap.GetWriter(s.actor)
if err != nil {
panic(err)
}
return state
}
var (
_ event.Service = &eventService{}
_ event.Manager = &eventManager{}
)
type eventService struct{}
// EventManager implements event.Service.
func (e *eventService) EventManager(context.Context) event.Manager {
return &eventManager{}
}
type eventManager struct{}
// Emit implements event.Manager.
func (e *eventManager) Emit(event transaction.Msg) error {
return nil
}
// EmitKV implements event.Manager.
func (e *eventManager) EmitKV(eventType string, attrs ...event.Attribute) error {
return nil
}

View File

@ -3,6 +3,7 @@ package configurator
import (
accountsmodulev1 "cosmossdk.io/api/cosmos/accounts/module/v1"
runtimev1alpha1 "cosmossdk.io/api/cosmos/app/runtime/v1alpha1"
runtimev2 "cosmossdk.io/api/cosmos/app/runtime/v2"
appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1"
authmodulev1 "cosmossdk.io/api/cosmos/auth/module/v1"
authzmodulev1 "cosmossdk.io/api/cosmos/authz/module/v1"
@ -440,3 +441,71 @@ func NewAppConfig(opts ...ModuleOption) depinject.Config {
return appconfig.Compose(&appv1alpha1.Config{Modules: modules})
}
func NewAppV2Config(opts ...ModuleOption) depinject.Config {
cfg := defaultConfig()
for _, opt := range opts {
opt(cfg)
}
preBlockers := make([]string, 0)
beginBlockers := make([]string, 0)
endBlockers := make([]string, 0)
initGenesis := make([]string, 0)
overrides := make([]*runtimev2.StoreKeyConfig, 0)
for _, s := range cfg.PreBlockersOrder {
if _, ok := cfg.ModuleConfigs[s]; ok {
preBlockers = append(preBlockers, s)
}
}
for _, s := range cfg.BeginBlockersOrder {
if _, ok := cfg.ModuleConfigs[s]; ok {
beginBlockers = append(beginBlockers, s)
}
}
for _, s := range cfg.EndBlockersOrder {
if _, ok := cfg.ModuleConfigs[s]; ok {
endBlockers = append(endBlockers, s)
}
}
for _, s := range cfg.InitGenesisOrder {
if _, ok := cfg.ModuleConfigs[s]; ok {
initGenesis = append(initGenesis, s)
}
}
if _, ok := cfg.ModuleConfigs[testutil.AuthModuleName]; ok {
overrides = append(overrides, &runtimev2.StoreKeyConfig{ModuleName: testutil.AuthModuleName, KvStoreKey: "acc"})
}
runtimeConfig := &runtimev2.Module{
AppName: "TestApp",
PreBlockers: preBlockers,
BeginBlockers: beginBlockers,
EndBlockers: endBlockers,
OverrideStoreKeys: overrides,
GasConfig: &runtimev2.GasConfig{
ValidateTxGasLimit: 100_000,
QueryGasLimit: 100_000,
SimulationGasLimit: 100_000,
},
}
if cfg.setInitGenesis {
runtimeConfig.InitGenesis = initGenesis
}
modules := []*appv1alpha1.ModuleConfig{{
Name: "runtime",
Config: appconfig.WrapAny(runtimeConfig),
}}
for _, m := range cfg.ModuleConfigs {
modules = append(modules, m)
}
return appconfig.Compose(&appv1alpha1.Config{Modules: modules})
}

View File

@ -5,13 +5,15 @@ import (
"fmt"
"testing"
"github.com/cosmos/gogoproto/proto"
gogoprotoany "github.com/cosmos/gogoproto/types/any"
"github.com/cosmos/gogoproto/types/any/test"
"github.com/cosmos/gogoproto/proto"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"gotest.tools/v3/assert"
"cosmossdk.io/core/gas"
"github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -84,6 +86,7 @@ func DeterministicIterations[request, response proto.Message](
if gasOverwrite { // to handle regressions, i.e. check that gas consumption didn't change
gasConsumed = ctx.GasMeter().GasConsumed() - before
}
t.Logf("gas consumed: %d", gasConsumed)
for i := 0; i < iterCount; i++ {
before := ctx.GasMeter().GasConsumed()
@ -93,3 +96,30 @@ func DeterministicIterations[request, response proto.Message](
assert.DeepEqual(t, res, prevRes)
}
}
func DeterministicIterationsV2[request, response proto.Message](
t *testing.T,
req request,
meterFn func() gas.Meter,
queryFn func(request) (response, error),
assertGas func(*testing.T, gas.Gas),
assertResponse func(*testing.T, response),
) {
t.Helper()
prevRes, err := queryFn(req)
gasMeter := meterFn()
gasConsumed := gasMeter.Consumed()
require.NoError(t, err)
assertGas(t, gasConsumed)
for i := 0; i < iterCount; i++ {
res, err := queryFn(req)
require.NoError(t, err)
sameGas := gasMeter.Consumed()
require.Equal(t, gasConsumed, sameGas)
require.Equal(t, res, prevRes)
if assertResponse != nil {
assertResponse(t, res)
}
}
}

View File

@ -66,8 +66,7 @@ func newMockContext(t *testing.T) (context.Context, store.KVStoreService) {
)
}
type transactionService struct {
}
type transactionService struct{}
func (t transactionService) ExecMode(ctx context.Context) transaction.ExecMode {
return transaction.ExecModeFinalize