From 5e1ff06821ab137284d4559fc6b013d53cc130f6 Mon Sep 17 00:00:00 2001 From: Matt Kocubinski Date: Thu, 21 Jul 2022 15:01:23 -0500 Subject: [PATCH 1/2] refactor: remove simapp from client/server tests (#12676) --- client/context_test.go | 20 +- client/grpc_query_test.go | 124 ++++++++---- client/query_test.go | 63 ------ client/rpc/rpc_test.go | 82 +++++++- server/grpc/grpc_web_test.go | 12 +- server/grpc/server_test.go | 17 +- server/util_test.go | 7 +- .../client}/grpc/tmservice/service_test.go | 21 +- .../integration/server}/export_test.go | 0 testutil/configurator/configurator.go | 189 +++++++++++++++--- testutil/network/network.go | 20 ++ testutil/sims/app_helpers.go | 10 +- types/query/pagination_test.go | 4 +- 13 files changed, 399 insertions(+), 170 deletions(-) delete mode 100644 client/query_test.go rename {client => simapp/integration/client}/grpc/tmservice/service_test.go (95%) rename {server => simapp/integration/server}/export_test.go (100%) diff --git a/client/context_test.go b/client/context_test.go index fedef9cc2d23..0af1ccc6bd40 100644 --- a/client/context_test.go +++ b/client/context_test.go @@ -2,7 +2,6 @@ package client_test import ( "bytes" - "context" "encoding/json" "os" "strings" @@ -17,9 +16,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" - "github.com/cosmos/cosmos-sdk/simapp" - "github.com/cosmos/cosmos-sdk/testutil/network" "github.com/cosmos/cosmos-sdk/testutil/testdata" + "github.com/cosmos/cosmos-sdk/types/module/testutil" ) func TestMain(m *testing.M) { @@ -145,22 +143,8 @@ x: "10" `, buf.String()) } -func TestCLIQueryConn(t *testing.T) { - cfg := network.DefaultConfig(simapp.NewTestNetworkFixture) - cfg.NumValidators = 1 - - n, err := network.New(t, t.TempDir(), cfg) - require.NoError(t, err) - defer n.Cleanup() - - testClient := testdata.NewQueryClient(n.Validators[0].ClientCtx) - res, err := testClient.Echo(context.Background(), &testdata.EchoRequest{Message: "hello"}) - require.NoError(t, err) - require.Equal(t, "hello", res.Message) -} - func TestGetFromFields(t *testing.T) { - cfg := network.DefaultConfig(simapp.NewTestNetworkFixture) + cfg := testutil.MakeTestEncodingConfig() path := hd.CreateHDPath(118, 0, 0).String() testCases := []struct { diff --git a/client/grpc_query_test.go b/client/grpc_query_test.go index 6b6ca21cbe06..b0b2b0afbcfe 100644 --- a/client/grpc_query_test.go +++ b/client/grpc_query_test.go @@ -1,83 +1,131 @@ -//go:build norace -// +build norace - package client_test import ( "context" - "fmt" "testing" "github.com/stretchr/testify/suite" + abci "github.com/tendermint/tendermint/abci/types" + tmjson "github.com/tendermint/tendermint/libs/json" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + dbm "github.com/tendermint/tm-db" "google.golang.org/grpc" "google.golang.org/grpc/metadata" - "github.com/cosmos/cosmos-sdk/simapp" - "github.com/cosmos/cosmos-sdk/testutil/network" + "cosmossdk.io/depinject" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/runtime" + "github.com/cosmos/cosmos-sdk/testutil/sims" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" - grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + "github.com/cosmos/cosmos-sdk/x/auth/testutil" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + "github.com/cosmos/cosmos-sdk/x/bank/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) type IntegrationTestSuite struct { suite.Suite - network *network.Network + ctx sdk.Context + genesisAccount *authtypes.BaseAccount + bankClient types.QueryClient + testClient testdata.QueryClient + genesisAccountBalance int64 } func (s *IntegrationTestSuite) SetupSuite() { s.T().Log("setting up integration test suite") + var ( + interfaceRegistry codectypes.InterfaceRegistry + bankKeeper bankkeeper.BaseKeeper + appBuilder *runtime.AppBuilder + cdc codec.Codec + ) - var err error - s.network, err = network.New(s.T(), s.T().TempDir(), network.DefaultConfig(simapp.NewTestNetworkFixture)) - s.Require().NoError(err) + // TODO duplicated from testutils/sims/app_helpers.go + // need more composable startup options for simapp, this test needed a handle to the closed over genesis account + // to query balances + err := depinject.Inject(testutil.AppConfig, &interfaceRegistry, &bankKeeper, &appBuilder, &cdc) + s.NoError(err) - _, err = s.network.WaitForHeight(2) - s.Require().NoError(err) + app := appBuilder.Build(log.NewNopLogger(), dbm.NewMemDB(), nil) + err = app.Load(true) + s.NoError(err) + + valSet, err := sims.CreateRandomValidatorSet() + s.NoError(err) + + // generate genesis account + s.genesisAccountBalance = 100000000000000 + senderPrivKey := secp256k1.GenPrivKey() + acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) + balance := banktypes.Balance{ + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(s.genesisAccountBalance))), + } + + genesisState, err := sims.GenesisStateWithValSet(cdc, appBuilder.DefaultGenesis(), valSet, []authtypes.GenesisAccount{acc}, balance) + s.NoError(err) + + stateBytes, err := tmjson.MarshalIndent(genesisState, "", " ") + s.NoError(err) + + // init chain will set the validator set and initialize the genesis accounts + app.InitChain( + abci.RequestInitChain{ + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: sims.DefaultConsensusParams, + AppStateBytes: stateBytes, + }, + ) + + app.Commit() + app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{ + Height: app.LastBlockHeight() + 1, + AppHash: app.LastCommitID().Hash, + ValidatorsHash: valSet.Hash(), + NextValidatorsHash: valSet.Hash(), + }}) + + // end of app init + + s.ctx = app.BaseApp.NewContext(false, tmproto.Header{}) + queryHelper := baseapp.NewQueryServerTestHelper(s.ctx, interfaceRegistry) + types.RegisterQueryServer(queryHelper, bankKeeper) + testdata.RegisterQueryServer(queryHelper, testdata.QueryImpl{}) + s.bankClient = types.NewQueryClient(queryHelper) + s.testClient = testdata.NewQueryClient(queryHelper) + s.genesisAccount = acc } func (s *IntegrationTestSuite) TearDownSuite() { s.T().Log("tearing down integration test suite") - s.network.Cleanup() } func (s *IntegrationTestSuite) TestGRPCQuery() { - val0 := s.network.Validators[0] + denom := sdk.DefaultBondDenom // gRPC query to test service should work - testClient := testdata.NewQueryClient(val0.ClientCtx) - testRes, err := testClient.Echo(context.Background(), &testdata.EchoRequest{Message: "hello"}) + testRes, err := s.testClient.Echo(context.Background(), &testdata.EchoRequest{Message: "hello"}) s.Require().NoError(err) s.Require().Equal("hello", testRes.Message) // gRPC query to bank service should work - denom := fmt.Sprintf("%stoken", val0.Moniker) - bankClient := banktypes.NewQueryClient(val0.ClientCtx) var header metadata.MD - bankRes, err := bankClient.Balance( + res, err := s.bankClient.Balance( context.Background(), - &banktypes.QueryBalanceRequest{Address: val0.Address.String(), Denom: denom}, + &banktypes.QueryBalanceRequest{Address: s.genesisAccount.GetAddress().String(), Denom: denom}, grpc.Header(&header), // Also fetch grpc header ) s.Require().NoError(err) - s.Require().Equal( - sdk.NewCoin(denom, s.network.Config.AccountTokens), - *bankRes.GetBalance(), - ) - blockHeight := header.Get(grpctypes.GRPCBlockHeightHeader) - s.Require().NotEmpty(blockHeight[0]) // Should contain the block height - - // Request metadata should work - val0.ClientCtx = val0.ClientCtx.WithHeight(1) // We set clientCtx to height 1 - bankClient = banktypes.NewQueryClient(val0.ClientCtx) - bankRes, err = bankClient.Balance( - context.Background(), - &banktypes.QueryBalanceRequest{Address: val0.Address.String(), Denom: denom}, - grpc.Header(&header), - ) - blockHeight = header.Get(grpctypes.GRPCBlockHeightHeader) - s.Require().Equal([]string{"1"}, blockHeight) + bal := res.GetBalance() + s.Equal(sdk.NewCoin(denom, sdk.NewInt(s.genesisAccountBalance)), *bal) } func TestIntegrationTestSuite(t *testing.T) { diff --git a/client/query_test.go b/client/query_test.go deleted file mode 100644 index 04532f566ba3..000000000000 --- a/client/query_test.go +++ /dev/null @@ -1,63 +0,0 @@ -//go:build norace -// +build norace - -package client_test - -import ( - "fmt" - - abci "github.com/tendermint/tendermint/abci/types" - - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" -) - -func (s *IntegrationTestSuite) TestQueryABCIHeight() { - testCases := []struct { - name string - reqHeight int64 - ctxHeight int64 - expHeight int64 - }{ - { - name: "non zero request height", - reqHeight: 3, - ctxHeight: 1, // query at height 1 or 2 would cause an error - expHeight: 3, - }, - { - name: "empty request height - use context height", - reqHeight: 0, - ctxHeight: 3, - expHeight: 3, - }, - { - name: "empty request height and context height - use latest height", - reqHeight: 0, - ctxHeight: 0, - expHeight: 4, - }, - } - - for _, tc := range testCases { - s.Run(tc.name, func() { - s.network.WaitForHeight(tc.expHeight) - - val := s.network.Validators[0] - - clientCtx := val.ClientCtx - clientCtx = clientCtx.WithHeight(tc.ctxHeight) - - req := abci.RequestQuery{ - Path: fmt.Sprintf("store/%s/key", banktypes.StoreKey), - Height: tc.reqHeight, - Data: banktypes.CreateAccountBalancesPrefix(val.Address), - Prove: true, - } - - res, err := clientCtx.QueryABCI(req) - s.Require().NoError(err) - - s.Require().Equal(tc.expHeight, res.Height) - }) - } -} diff --git a/client/rpc/rpc_test.go b/client/rpc/rpc_test.go index 11a4ef2e4aa7..e1d947977e6d 100644 --- a/client/rpc/rpc_test.go +++ b/client/rpc/rpc_test.go @@ -1,15 +1,24 @@ +//go:build norace +// +build norace + package rpc_test import ( + "context" "fmt" "testing" "github.com/stretchr/testify/suite" + abci "github.com/tendermint/tendermint/abci/types" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" "github.com/cosmos/cosmos-sdk/client/rpc" - "github.com/cosmos/cosmos-sdk/simapp" clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" "github.com/cosmos/cosmos-sdk/testutil/network" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) type IntegrationTestSuite struct { @@ -21,8 +30,11 @@ type IntegrationTestSuite struct { func (s *IntegrationTestSuite) SetupSuite() { s.T().Log("setting up integration test suite") - var err error - s.network, err = network.New(s.T(), s.T().TempDir(), network.DefaultConfig(simapp.NewTestNetworkFixture)) + cfg, err := network.DefaultConfigWithAppConfig(network.MinimumAppConfig()) + + s.NoError(err) + + s.network, err = network.New(s.T(), s.T().TempDir(), cfg) s.Require().NoError(err) s.Require().NoError(s.network.WaitForNextBlock()) @@ -44,6 +56,70 @@ func (s *IntegrationTestSuite) TestStatusCommand() { s.Require().Contains(out.String(), fmt.Sprintf("\"moniker\":\"%s\"", val0.Moniker)) } +func (s *IntegrationTestSuite) TestCLIQueryConn() { + var header metadata.MD + + testClient := testdata.NewQueryClient(s.network.Validators[0].ClientCtx) + res, err := testClient.Echo(context.Background(), &testdata.EchoRequest{Message: "hello"}, grpc.Header(&header)) + s.NoError(err) + + blockHeight := header.Get(grpctypes.GRPCBlockHeightHeader) + s.Require().Equal([]string{"1"}, blockHeight) + + s.Equal("hello", res.Message) +} + +func (s *IntegrationTestSuite) TestQueryABCIHeight() { + testCases := []struct { + name string + reqHeight int64 + ctxHeight int64 + expHeight int64 + }{ + { + name: "non zero request height", + reqHeight: 3, + ctxHeight: 1, // query at height 1 or 2 would cause an error + expHeight: 3, + }, + { + name: "empty request height - use context height", + reqHeight: 0, + ctxHeight: 3, + expHeight: 3, + }, + { + name: "empty request height and context height - use latest height", + reqHeight: 0, + ctxHeight: 0, + expHeight: 4, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + s.network.WaitForHeight(tc.expHeight) + + val := s.network.Validators[0] + + clientCtx := val.ClientCtx + clientCtx = clientCtx.WithHeight(tc.ctxHeight) + + req := abci.RequestQuery{ + Path: fmt.Sprintf("store/%s/key", banktypes.StoreKey), + Height: tc.reqHeight, + Data: banktypes.CreateAccountBalancesPrefix(val.Address), + Prove: true, + } + + res, err := clientCtx.QueryABCI(req) + s.Require().NoError(err) + + s.Require().Equal(tc.expHeight, res.Height) + }) + } +} + func TestIntegrationTestSuite(t *testing.T) { suite.Run(t, new(IntegrationTestSuite)) } diff --git a/server/grpc/grpc_web_test.go b/server/grpc/grpc_web_test.go index 12bc1fce8fe9..4abad560155c 100644 --- a/server/grpc/grpc_web_test.go +++ b/server/grpc/grpc_web_test.go @@ -21,9 +21,14 @@ import ( "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" "github.com/cosmos/cosmos-sdk/codec" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/testutil/network" + _ "github.com/cosmos/cosmos-sdk/x/auth" + _ "github.com/cosmos/cosmos-sdk/x/auth/tx/module" + _ "github.com/cosmos/cosmos-sdk/x/bank" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + _ "github.com/cosmos/cosmos-sdk/x/genutil" + _ "github.com/cosmos/cosmos-sdk/x/params" + _ "github.com/cosmos/cosmos-sdk/x/staking" ) // https://github.com/improbable-eng/grpc-web/blob/master/go/grpcweb/wrapper_test.go used as a reference @@ -42,11 +47,12 @@ type GRPCWebTestSuite struct { func (s *GRPCWebTestSuite) SetupSuite() { s.T().Log("setting up integration test suite") - cfg := network.DefaultConfig(simapp.NewTestNetworkFixture) + cfg, err := network.DefaultConfigWithAppConfig(network.MinimumAppConfig()) + + s.NoError(err) cfg.NumValidators = 1 s.cfg = cfg - var err error s.network, err = network.New(s.T(), s.T().TempDir(), s.cfg) s.Require().NoError(err) diff --git a/server/grpc/server_test.go b/server/grpc/server_test.go index d7cd2b39be80..26c09102af22 100644 --- a/server/grpc/server_test.go +++ b/server/grpc/server_test.go @@ -9,13 +9,13 @@ import ( "testing" "time" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/jhump/protoreflect/grpcreflect" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" + "github.com/cosmos/cosmos-sdk/codec" + "google.golang.org/grpc" "google.golang.org/grpc/metadata" rpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha" @@ -24,7 +24,6 @@ import ( reflectionv1 "github.com/cosmos/cosmos-sdk/client/grpc/reflection" clienttx "github.com/cosmos/cosmos-sdk/client/tx" reflectionv2 "github.com/cosmos/cosmos-sdk/server/grpc/reflection/v2alpha1" - "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/testutil/network" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" @@ -40,19 +39,19 @@ import ( type IntegrationTestSuite struct { suite.Suite - app *simapp.SimApp cfg network.Config network *network.Network conn *grpc.ClientConn } func (s *IntegrationTestSuite) SetupSuite() { + var err error s.T().Log("setting up integration test suite") - s.app = simapp.Setup(s.T(), false) - s.cfg = network.DefaultConfig(simapp.NewTestNetworkFixture) + + s.cfg, err = network.DefaultConfigWithAppConfig(network.MinimumAppConfig()) + s.NoError(err) s.cfg.NumValidators = 1 - var err error s.network, err = network.New(s.T(), s.T().TempDir(), s.cfg) s.Require().NoError(err) @@ -63,7 +62,7 @@ func (s *IntegrationTestSuite) SetupSuite() { s.conn, err = grpc.Dial( val0.AppConfig.GRPC.Address, grpc.WithInsecure(), // Or else we get "no transport security set" - grpc.WithDefaultCallOptions(grpc.ForceCodec(codec.NewProtoCodec(s.app.InterfaceRegistry()).GRPCCodec())), + grpc.WithDefaultCallOptions(grpc.ForceCodec(codec.NewProtoCodec(s.cfg.InterfaceRegistry).GRPCCodec())), ) s.Require().NoError(err) } @@ -226,7 +225,7 @@ func (s *IntegrationTestSuite) TestGRPCServerInvalidHeaderHeights() { // TestGRPCUnpacker - tests the grpc endpoint for Validator and using the interface registry unpack and extract the // ConsAddr. (ref: https://github.com/cosmos/cosmos-sdk/issues/8045) func (s *IntegrationTestSuite) TestGRPCUnpacker() { - ir := s.app.InterfaceRegistry() + ir := s.cfg.InterfaceRegistry queryClient := stakingtypes.NewQueryClient(s.conn) validator, err := queryClient.Validator(context.Background(), &stakingtypes.QueryValidatorRequest{ValidatorAddr: s.network.Validators[0].ValAddress.String()}) diff --git a/server/util_test.go b/server/util_test.go index c3bd0f9063cd..86c106920be6 100644 --- a/server/util_test.go +++ b/server/util_test.go @@ -19,8 +19,9 @@ import ( "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" - "github.com/cosmos/cosmos-sdk/simapp" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/types/module/testutil" genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" ) @@ -413,14 +414,14 @@ func TestEmptyMinGasPrices(t *testing.T) { tempDir := t.TempDir() err := os.Mkdir(filepath.Join(tempDir, "config"), os.ModePerm) require.NoError(t, err) - encCfg := simapp.MakeTestEncodingConfig() + encCfg := testutil.MakeTestEncodingConfig() // Run InitCmd to create necessary config files. clientCtx := client.Context{}.WithHomeDir(tempDir).WithCodec(encCfg.Codec) serverCtx := server.NewDefaultContext() ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx) ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx) - cmd := genutilcli.InitCmd(simapp.ModuleBasics, tempDir) + cmd := genutilcli.InitCmd(module.NewBasicManager(), tempDir) cmd.SetArgs([]string{"appnode-test"}) err = cmd.ExecuteContext(ctx) require.NoError(t, err) diff --git a/client/grpc/tmservice/service_test.go b/simapp/integration/client/grpc/tmservice/service_test.go similarity index 95% rename from client/grpc/tmservice/service_test.go rename to simapp/integration/client/grpc/tmservice/service_test.go index 14821a64dfb4..10b2db205922 100644 --- a/client/grpc/tmservice/service_test.go +++ b/simapp/integration/client/grpc/tmservice/service_test.go @@ -1,3 +1,6 @@ +//go:build norace +// +build norace + package tmservice_test import ( @@ -10,12 +13,13 @@ import ( "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil/configurator" "github.com/cosmos/cosmos-sdk/testutil/network" "github.com/cosmos/cosmos-sdk/testutil/rest" "github.com/cosmos/cosmos-sdk/types" qtypes "github.com/cosmos/cosmos-sdk/types/query" "github.com/cosmos/cosmos-sdk/version" + _ "github.com/cosmos/cosmos-sdk/x/gov" ) type IntegrationTestSuite struct { @@ -27,7 +31,7 @@ type IntegrationTestSuite struct { } func TestIntegrationTestSuite(t *testing.T) { - t.Skip() // to be re-enabled in https://github.com/cosmos/cosmos-sdk/pull/12482/ + //t.Skip() // to be re-enabled in https://github.com/cosmos/cosmos-sdk/pull/12482/ suite.Run(t, new(IntegrationTestSuite)) } @@ -35,12 +39,21 @@ func TestIntegrationTestSuite(t *testing.T) { func (s *IntegrationTestSuite) SetupSuite() { s.T().Log("setting up integration test suite") - cfg := network.DefaultConfig(simapp.NewTestNetworkFixture) + appConfig := configurator.NewAppConfig( + configurator.AuthModule(), + configurator.ParamsModule(), + configurator.BankModule(), + configurator.GenutilModule(), + configurator.StakingModule(), + configurator.GovModule(), + configurator.TxModule()) + + cfg, err := network.DefaultConfigWithAppConfig(appConfig) + s.NoError(err) cfg.NumValidators = 1 s.cfg = cfg - var err error s.network, err = network.New(s.T(), s.T().TempDir(), s.cfg) s.Require().NoError(err) diff --git a/server/export_test.go b/simapp/integration/server/export_test.go similarity index 100% rename from server/export_test.go rename to simapp/integration/server/export_test.go diff --git a/testutil/configurator/configurator.go b/testutil/configurator/configurator.go index a69d138e42e5..53000c383423 100644 --- a/testutil/configurator/configurator.go +++ b/testutil/configurator/configurator.go @@ -5,32 +5,91 @@ import ( appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1" authmodulev1 "cosmossdk.io/api/cosmos/auth/module/v1" bankmodulev1 "cosmossdk.io/api/cosmos/bank/module/v1" + distrmodulev1 "cosmossdk.io/api/cosmos/distribution/module/v1" + feegrantmodulev1 "cosmossdk.io/api/cosmos/feegrant/module/v1" + genutilmodulev1 "cosmossdk.io/api/cosmos/genutil/module/v1" + govmodulev1 "cosmossdk.io/api/cosmos/gov/module/v1" paramsmodulev1 "cosmossdk.io/api/cosmos/params/module/v1" + stakingmodulev1 "cosmossdk.io/api/cosmos/staking/module/v1" + txmodulev1 "cosmossdk.io/api/cosmos/tx/module/v1" + vestingmodulev1 "cosmossdk.io/api/cosmos/vesting/module/v1" "cosmossdk.io/core/appconfig" "cosmossdk.io/depinject" ) var beginBlockOrder = []string{ + "upgrade", + "capability", "mint", + "distribution", + "slashing", + "evidence", "staking", "auth", "bank", + "gov", + "crisis", + "genutil", + "authz", + "feegrant", + "nft", + "group", "params", + "vesting", } var endBlockersOrder = []string{ + "crisis", + "gov", "staking", + "capability", "auth", "bank", + "distribution", + "slashing", "mint", + "genutil", + "evidence", + "authz", + "feegrant", + "nft", + "group", "params", + "upgrade", + "vesting", } -type ModuleOption func(options map[string]*appv1alpha1.ModuleConfig) +var initGenesisOrder = []string{ + "capability", + "auth", + "bank", + "distribution", + "staking", + "slashing", + "gov", + "mint", + "crisis", + "genutil", + "evidence", + "authz", + "feegrant", + "nft", + "group", + "params", + "upgrade", + "vesting", +} + +type appConfig struct { + moduleConfigs map[string]*appv1alpha1.ModuleConfig + setInitGenesis bool +} + +type ModuleOption func(config *appConfig) func BankModule() ModuleOption { - return func(options map[string]*appv1alpha1.ModuleConfig) { - options["bank"] = &appv1alpha1.ModuleConfig{ + return func(config *appConfig) { + config.moduleConfigs["bank"] = &appv1alpha1.ModuleConfig{ Name: "bank", Config: appconfig.WrapAny(&bankmodulev1.Module{}), } @@ -38,16 +97,18 @@ func BankModule() ModuleOption { } func AuthModule() ModuleOption { - return func(options map[string]*appv1alpha1.ModuleConfig) { - options["auth"] = &appv1alpha1.ModuleConfig{ + return func(config *appConfig) { + config.moduleConfigs["auth"] = &appv1alpha1.ModuleConfig{ Name: "auth", Config: appconfig.WrapAny(&authmodulev1.Module{ Bech32Prefix: "cosmos", ModuleAccountPermissions: []*authmodulev1.ModuleAccountPermission{ {Account: "fee_collector"}, + {Account: "distribution"}, {Account: "mint", Permissions: []string{"minter"}}, {Account: "bonded_tokens_pool", Permissions: []string{"burner", "staking"}}, {Account: "not_bonded_tokens_pool", Permissions: []string{"burner", "staking"}}, + {Account: "gov", Permissions: []string{"burner"}}, }, }), } @@ -55,53 +116,135 @@ func AuthModule() ModuleOption { } func ParamsModule() ModuleOption { - return func(options map[string]*appv1alpha1.ModuleConfig) { - options["params"] = &appv1alpha1.ModuleConfig{ + return func(config *appConfig) { + config.moduleConfigs["params"] = &appv1alpha1.ModuleConfig{ Name: "params", Config: appconfig.WrapAny(¶msmodulev1.Module{}), } } } +func TxModule() ModuleOption { + return func(config *appConfig) { + config.moduleConfigs["tx"] = &appv1alpha1.ModuleConfig{ + Name: "tx", + Config: appconfig.WrapAny(&txmodulev1.Module{}), + } + } +} + +func StakingModule() ModuleOption { + return func(config *appConfig) { + config.moduleConfigs["staking"] = &appv1alpha1.ModuleConfig{ + Name: "staking", + Config: appconfig.WrapAny(&stakingmodulev1.Module{}), + } + } +} + +func GenutilModule() ModuleOption { + return func(config *appConfig) { + config.moduleConfigs["genutil"] = &appv1alpha1.ModuleConfig{ + Name: "genutil", + Config: appconfig.WrapAny(&genutilmodulev1.Module{}), + } + } +} + +func DistributionModule() ModuleOption { + return func(config *appConfig) { + config.moduleConfigs["distribution"] = &appv1alpha1.ModuleConfig{ + Name: "distribution", + Config: appconfig.WrapAny(&distrmodulev1.Module{}), + } + } +} + +func FeegrantModule() ModuleOption { + return func(config *appConfig) { + config.moduleConfigs["feegrant"] = &appv1alpha1.ModuleConfig{ + Name: "feegrant", + Config: appconfig.WrapAny(&feegrantmodulev1.Module{}), + } + } +} + +func VestingModule() ModuleOption { + return func(config *appConfig) { + config.moduleConfigs["vesting"] = &appv1alpha1.ModuleConfig{ + Name: "vesting", + Config: appconfig.WrapAny(&vestingmodulev1.Module{}), + } + } +} + +func GovModule() ModuleOption { + return func(config *appConfig) { + config.moduleConfigs["gov"] = &appv1alpha1.ModuleConfig{ + Name: "gov", + Config: appconfig.WrapAny(&govmodulev1.Module{}), + } + } +} + +func OmitInitGenesis() ModuleOption { + return func(config *appConfig) { + config.setInitGenesis = false + } +} + func NewAppConfig(opts ...ModuleOption) depinject.Config { - options := make(map[string]*appv1alpha1.ModuleConfig) + cfg := &appConfig{ + moduleConfigs: make(map[string]*appv1alpha1.ModuleConfig), + setInitGenesis: true, + } for _, opt := range opts { - opt(options) + opt(cfg) } beginBlockers := make([]string, 0) endBlockers := make([]string, 0) + initGenesis := make([]string, 0) overrides := make([]*runtimev1alpha1.StoreKeyConfig, 0) for _, s := range beginBlockOrder { - if _, ok := options[s]; ok { + if _, ok := cfg.moduleConfigs[s]; ok { beginBlockers = append(beginBlockers, s) } } for _, s := range endBlockersOrder { - if _, ok := options[s]; ok { + if _, ok := cfg.moduleConfigs[s]; ok { endBlockers = append(endBlockers, s) } } - if _, ok := options["auth"]; ok { + for _, s := range initGenesisOrder { + if _, ok := cfg.moduleConfigs[s]; ok { + initGenesis = append(initGenesis, s) + } + } + + if _, ok := cfg.moduleConfigs["auth"]; ok { overrides = append(overrides, &runtimev1alpha1.StoreKeyConfig{ModuleName: "auth", KvStoreKey: "acc"}) } - modules := []*appv1alpha1.ModuleConfig{ - { - Name: "runtime", - Config: appconfig.WrapAny(&runtimev1alpha1.Module{ - AppName: "TestApp", - BeginBlockers: beginBlockers, - EndBlockers: endBlockers, - OverrideStoreKeys: overrides, - }), - }, + runtimeConfig := &runtimev1alpha1.Module{ + AppName: "TestApp", + BeginBlockers: beginBlockers, + EndBlockers: endBlockers, + OverrideStoreKeys: overrides, } + if cfg.setInitGenesis { + runtimeConfig.InitGenesis = initGenesis + } + + modules := []*appv1alpha1.ModuleConfig{{ + Name: "runtime", + Config: appconfig.WrapAny(runtimeConfig), + }} - for _, m := range options { + for _, m := range cfg.moduleConfigs { modules = append(modules, m) } diff --git a/testutil/network/network.go b/testutil/network/network.go index c9fcb4ba643d..0939f83ab057 100644 --- a/testutil/network/network.go +++ b/testutil/network/network.go @@ -25,6 +25,8 @@ import ( "google.golang.org/grpc" "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/testutil/configurator" + "github.com/cosmos/cosmos-sdk/testutil/testdata" moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" "cosmossdk.io/depinject" @@ -44,9 +46,14 @@ import ( servertypes "github.com/cosmos/cosmos-sdk/server/types" "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/x/auth" + _ "github.com/cosmos/cosmos-sdk/x/auth/tx/module" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + _ "github.com/cosmos/cosmos-sdk/x/bank" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/genutil" + _ "github.com/cosmos/cosmos-sdk/x/params" + _ "github.com/cosmos/cosmos-sdk/x/staking" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -124,6 +131,17 @@ func DefaultConfig(factory TestFixtureFactory) Config { } } +// MinimumAppConfig defines the minimum of modules required for a call to New to succeed +func MinimumAppConfig() depinject.Config { + return configurator.NewAppConfig( + configurator.AuthModule(), + configurator.ParamsModule(), + configurator.BankModule(), + configurator.GenutilModule(), + configurator.StakingModule(), + configurator.TxModule()) +} + func DefaultConfigWithAppConfig(appConfig depinject.Config) (Config, error) { var ( appBuilder *runtime.AppBuilder @@ -165,6 +183,8 @@ func DefaultConfigWithAppConfig(appConfig depinject.Config) (Config, error) { baseapp.SetMinGasPrices(val.GetAppConfig().MinGasPrices), ) + testdata.RegisterQueryServer(app.GRPCQueryRouter(), testdata.QueryImpl{}) + if err := app.Load(true); err != nil { panic(err) } diff --git a/testutil/sims/app_helpers.go b/testutil/sims/app_helpers.go index 03d6323ebfa7..4a689324b532 100644 --- a/testutil/sims/app_helpers.go +++ b/testutil/sims/app_helpers.go @@ -53,8 +53,8 @@ var DefaultConsensusParams = &tmproto.ConsensusParams{ }, } -// createDefaultRandomValidatorSet creates a validator set with one random validator -func createDefaultRandomValidatorSet() (*tmtypes.ValidatorSet, error) { +// CreateRandomValidatorSet creates a validator set with one random validator +func CreateRandomValidatorSet() (*tmtypes.ValidatorSet, error) { privVal := mock.NewPV() pubKey, err := privVal.GetPubKey(context.TODO()) if err != nil { @@ -70,19 +70,19 @@ func createDefaultRandomValidatorSet() (*tmtypes.ValidatorSet, error) { // Setup initializes a new runtime.App and can inject values into extraOutputs. // It uses SetupWithConfiguration under the hood. func Setup(appConfig depinject.Config, extraOutputs ...interface{}) (*runtime.App, error) { - return SetupWithConfiguration(appConfig, createDefaultRandomValidatorSet, nil, false, extraOutputs...) + return SetupWithConfiguration(appConfig, CreateRandomValidatorSet, nil, false, extraOutputs...) } // SetupAtGenesis initializes a new runtime.App at genesis and can inject values into extraOutputs. // It uses SetupWithConfiguration under the hood. func SetupAtGenesis(appConfig depinject.Config, extraOutputs ...interface{}) (*runtime.App, error) { - return SetupWithConfiguration(appConfig, createDefaultRandomValidatorSet, nil, true, extraOutputs...) + return SetupWithConfiguration(appConfig, CreateRandomValidatorSet, nil, true, extraOutputs...) } // SetupWithBaseAppOption initializes a new runtime.App and can inject values into extraOutputs. // With specific baseApp options. It uses SetupWithConfiguration under the hood. func SetupWithBaseAppOption(appConfig depinject.Config, baseAppOption runtime.BaseAppOption, extraOutputs ...interface{}) (*runtime.App, error) { - return SetupWithConfiguration(appConfig, createDefaultRandomValidatorSet, baseAppOption, false, extraOutputs...) + return SetupWithConfiguration(appConfig, CreateRandomValidatorSet, baseAppOption, false, extraOutputs...) } // SetupWithConfiguration initializes a new runtime.App. A Nop logger is set in runtime.App. diff --git a/types/query/pagination_test.go b/types/query/pagination_test.go index fe79af04c434..c632492c37e1 100644 --- a/types/query/pagination_test.go +++ b/types/query/pagination_test.go @@ -65,7 +65,9 @@ func (s *paginationTestSuite) SetupTest() { configurator.NewAppConfig( configurator.AuthModule(), configurator.BankModule(), - configurator.ParamsModule()), + configurator.ParamsModule(), + configurator.OmitInitGenesis(), + ), &bankKeeper, &accountKeeper, ®, &cdc) s.NoError(err) From 5019459b1b2028119c6ca1d80714caa7858c2076 Mon Sep 17 00:00:00 2001 From: likhita-809 <78951027+likhita-809@users.noreply.github.com> Date: Fri, 22 Jul 2022 03:59:48 +0530 Subject: [PATCH 2/2] fix: revert proto changes from #12610 (#12670) ## Description ref:https://github.com/cosmos/cosmos-sdk/pull/12610#discussion_r925450127 --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- api/cosmos/bank/v1beta1/tx.pulsar.go | 257 +++++++++++++++++---------- proto/cosmos/bank/v1beta1/tx.proto | 8 +- x/bank/app_test.go | 21 ++- x/bank/client/cli/tx.go | 2 +- x/bank/keeper/keeper_test.go | 27 +-- x/bank/keeper/msg_server.go | 8 +- x/bank/keeper/send.go | 35 ++-- x/bank/simulation/operations.go | 98 +++++----- x/bank/simulation/operations_test.go | 9 +- x/bank/types/errors.go | 2 + x/bank/types/msgs.go | 34 ++-- x/bank/types/msgs_test.go | 45 +++-- x/bank/types/tx.pb.go | 115 ++++++------ 13 files changed, 392 insertions(+), 269 deletions(-) diff --git a/api/cosmos/bank/v1beta1/tx.pulsar.go b/api/cosmos/bank/v1beta1/tx.pulsar.go index 78c894e60b8f..246fbd81df0d 100644 --- a/api/cosmos/bank/v1beta1/tx.pulsar.go +++ b/api/cosmos/bank/v1beta1/tx.pulsar.go @@ -994,6 +994,57 @@ func (x *fastReflection_MsgSendResponse) ProtoMethods() *protoiface.Methods { } } +var _ protoreflect.List = (*_MsgMultiSend_1_list)(nil) + +type _MsgMultiSend_1_list struct { + list *[]*Input +} + +func (x *_MsgMultiSend_1_list) Len() int { + if x.list == nil { + return 0 + } + return len(*x.list) +} + +func (x *_MsgMultiSend_1_list) Get(i int) protoreflect.Value { + return protoreflect.ValueOfMessage((*x.list)[i].ProtoReflect()) +} + +func (x *_MsgMultiSend_1_list) Set(i int, value protoreflect.Value) { + valueUnwrapped := value.Message() + concreteValue := valueUnwrapped.Interface().(*Input) + (*x.list)[i] = concreteValue +} + +func (x *_MsgMultiSend_1_list) Append(value protoreflect.Value) { + valueUnwrapped := value.Message() + concreteValue := valueUnwrapped.Interface().(*Input) + *x.list = append(*x.list, concreteValue) +} + +func (x *_MsgMultiSend_1_list) AppendMutable() protoreflect.Value { + v := new(Input) + *x.list = append(*x.list, v) + return protoreflect.ValueOfMessage(v.ProtoReflect()) +} + +func (x *_MsgMultiSend_1_list) Truncate(n int) { + for i := n; i < len(*x.list); i++ { + (*x.list)[i] = nil + } + *x.list = (*x.list)[:n] +} + +func (x *_MsgMultiSend_1_list) NewElement() protoreflect.Value { + v := new(Input) + return protoreflect.ValueOfMessage(v.ProtoReflect()) +} + +func (x *_MsgMultiSend_1_list) IsValid() bool { + return x.list != nil +} + var _ protoreflect.List = (*_MsgMultiSend_2_list)(nil) type _MsgMultiSend_2_list struct { @@ -1047,14 +1098,14 @@ func (x *_MsgMultiSend_2_list) IsValid() bool { var ( md_MsgMultiSend protoreflect.MessageDescriptor - fd_MsgMultiSend_input protoreflect.FieldDescriptor + fd_MsgMultiSend_inputs protoreflect.FieldDescriptor fd_MsgMultiSend_outputs protoreflect.FieldDescriptor ) func init() { file_cosmos_bank_v1beta1_tx_proto_init() md_MsgMultiSend = File_cosmos_bank_v1beta1_tx_proto.Messages().ByName("MsgMultiSend") - fd_MsgMultiSend_input = md_MsgMultiSend.Fields().ByName("input") + fd_MsgMultiSend_inputs = md_MsgMultiSend.Fields().ByName("inputs") fd_MsgMultiSend_outputs = md_MsgMultiSend.Fields().ByName("outputs") } @@ -1123,9 +1174,9 @@ func (x *fastReflection_MsgMultiSend) Interface() protoreflect.ProtoMessage { // While iterating, mutating operations may only be performed // on the current field descriptor. func (x *fastReflection_MsgMultiSend) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { - if x.Input != nil { - value := protoreflect.ValueOfMessage(x.Input.ProtoReflect()) - if !f(fd_MsgMultiSend_input, value) { + if len(x.Inputs) != 0 { + value := protoreflect.ValueOfList(&_MsgMultiSend_1_list{list: &x.Inputs}) + if !f(fd_MsgMultiSend_inputs, value) { return } } @@ -1150,8 +1201,8 @@ func (x *fastReflection_MsgMultiSend) Range(f func(protoreflect.FieldDescriptor, // a repeated field is populated if it is non-empty. func (x *fastReflection_MsgMultiSend) Has(fd protoreflect.FieldDescriptor) bool { switch fd.FullName() { - case "cosmos.bank.v1beta1.MsgMultiSend.input": - return x.Input != nil + case "cosmos.bank.v1beta1.MsgMultiSend.inputs": + return len(x.Inputs) != 0 case "cosmos.bank.v1beta1.MsgMultiSend.outputs": return len(x.Outputs) != 0 default: @@ -1170,8 +1221,8 @@ func (x *fastReflection_MsgMultiSend) Has(fd protoreflect.FieldDescriptor) bool // Clear is a mutating operation and unsafe for concurrent use. func (x *fastReflection_MsgMultiSend) Clear(fd protoreflect.FieldDescriptor) { switch fd.FullName() { - case "cosmos.bank.v1beta1.MsgMultiSend.input": - x.Input = nil + case "cosmos.bank.v1beta1.MsgMultiSend.inputs": + x.Inputs = nil case "cosmos.bank.v1beta1.MsgMultiSend.outputs": x.Outputs = nil default: @@ -1190,9 +1241,12 @@ func (x *fastReflection_MsgMultiSend) Clear(fd protoreflect.FieldDescriptor) { // of the value; to obtain a mutable reference, use Mutable. func (x *fastReflection_MsgMultiSend) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { switch descriptor.FullName() { - case "cosmos.bank.v1beta1.MsgMultiSend.input": - value := x.Input - return protoreflect.ValueOfMessage(value.ProtoReflect()) + case "cosmos.bank.v1beta1.MsgMultiSend.inputs": + if len(x.Inputs) == 0 { + return protoreflect.ValueOfList(&_MsgMultiSend_1_list{}) + } + listValue := &_MsgMultiSend_1_list{list: &x.Inputs} + return protoreflect.ValueOfList(listValue) case "cosmos.bank.v1beta1.MsgMultiSend.outputs": if len(x.Outputs) == 0 { return protoreflect.ValueOfList(&_MsgMultiSend_2_list{}) @@ -1219,8 +1273,10 @@ func (x *fastReflection_MsgMultiSend) Get(descriptor protoreflect.FieldDescripto // Set is a mutating operation and unsafe for concurrent use. func (x *fastReflection_MsgMultiSend) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { switch fd.FullName() { - case "cosmos.bank.v1beta1.MsgMultiSend.input": - x.Input = value.Message().Interface().(*Input) + case "cosmos.bank.v1beta1.MsgMultiSend.inputs": + lv := value.List() + clv := lv.(*_MsgMultiSend_1_list) + x.Inputs = *clv.list case "cosmos.bank.v1beta1.MsgMultiSend.outputs": lv := value.List() clv := lv.(*_MsgMultiSend_2_list) @@ -1245,11 +1301,12 @@ func (x *fastReflection_MsgMultiSend) Set(fd protoreflect.FieldDescriptor, value // Mutable is a mutating operation and unsafe for concurrent use. func (x *fastReflection_MsgMultiSend) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { switch fd.FullName() { - case "cosmos.bank.v1beta1.MsgMultiSend.input": - if x.Input == nil { - x.Input = new(Input) + case "cosmos.bank.v1beta1.MsgMultiSend.inputs": + if x.Inputs == nil { + x.Inputs = []*Input{} } - return protoreflect.ValueOfMessage(x.Input.ProtoReflect()) + value := &_MsgMultiSend_1_list{list: &x.Inputs} + return protoreflect.ValueOfList(value) case "cosmos.bank.v1beta1.MsgMultiSend.outputs": if x.Outputs == nil { x.Outputs = []*Output{} @@ -1269,9 +1326,9 @@ func (x *fastReflection_MsgMultiSend) Mutable(fd protoreflect.FieldDescriptor) p // For lists, maps, and messages, this returns a new, empty, mutable value. func (x *fastReflection_MsgMultiSend) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { switch fd.FullName() { - case "cosmos.bank.v1beta1.MsgMultiSend.input": - m := new(Input) - return protoreflect.ValueOfMessage(m.ProtoReflect()) + case "cosmos.bank.v1beta1.MsgMultiSend.inputs": + list := []*Input{} + return protoreflect.ValueOfList(&_MsgMultiSend_1_list{list: &list}) case "cosmos.bank.v1beta1.MsgMultiSend.outputs": list := []*Output{} return protoreflect.ValueOfList(&_MsgMultiSend_2_list{list: &list}) @@ -1344,9 +1401,11 @@ func (x *fastReflection_MsgMultiSend) ProtoMethods() *protoiface.Methods { var n int var l int _ = l - if x.Input != nil { - l = options.Size(x.Input) - n += 1 + l + runtime.Sov(uint64(l)) + if len(x.Inputs) > 0 { + for _, e := range x.Inputs { + l = options.Size(e) + n += 1 + l + runtime.Sov(uint64(l)) + } } if len(x.Outputs) > 0 { for _, e := range x.Outputs { @@ -1399,19 +1458,21 @@ func (x *fastReflection_MsgMultiSend) ProtoMethods() *protoiface.Methods { dAtA[i] = 0x12 } } - if x.Input != nil { - encoded, err := options.Marshal(x.Input) - if err != nil { - return protoiface.MarshalOutput{ - NoUnkeyedLiterals: input.NoUnkeyedLiterals, - Buf: input.Buf, - }, err + if len(x.Inputs) > 0 { + for iNdEx := len(x.Inputs) - 1; iNdEx >= 0; iNdEx-- { + encoded, err := options.Marshal(x.Inputs[iNdEx]) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0xa } - i -= len(encoded) - copy(dAtA[i:], encoded) - i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) - i-- - dAtA[i] = 0xa } if input.Buf != nil { input.Buf = append(input.Buf, dAtA...) @@ -1464,7 +1525,7 @@ func (x *fastReflection_MsgMultiSend) ProtoMethods() *protoiface.Methods { switch fieldNum { case 1: if wireType != 2 { - return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Input", wireType) + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Inputs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1491,10 +1552,8 @@ func (x *fastReflection_MsgMultiSend) ProtoMethods() *protoiface.Methods { if postIndex > l { return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF } - if x.Input == nil { - x.Input = &Input{} - } - if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Input); err != nil { + x.Inputs = append(x.Inputs, &Input{}) + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Inputs[len(x.Inputs)-1]); err != nil { return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err } iNdEx = postIndex @@ -2870,13 +2929,15 @@ func (*MsgSendResponse) Descriptor() ([]byte, []int) { return file_cosmos_bank_v1beta1_tx_proto_rawDescGZIP(), []int{1} } -// MsgMultiSend represents a single input, multi-out send message. +// MsgMultiSend represents an arbitrary multi-in, multi-out send message. type MsgMultiSend struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Input *Input `protobuf:"bytes,1,opt,name=input,proto3" json:"input,omitempty"` + // Inputs, despite being `repeated`, only allows one sender input. This is + // checked in MsgMultiSend's ValidateBasic. + Inputs []*Input `protobuf:"bytes,1,rep,name=inputs,proto3" json:"inputs,omitempty"` Outputs []*Output `protobuf:"bytes,2,rep,name=outputs,proto3" json:"outputs,omitempty"` } @@ -2900,9 +2961,9 @@ func (*MsgMultiSend) Descriptor() ([]byte, []int) { return file_cosmos_bank_v1beta1_tx_proto_rawDescGZIP(), []int{2} } -func (x *MsgMultiSend) GetInput() *Input { +func (x *MsgMultiSend) GetInputs() []*Input { if x != nil { - return x.Input + return x.Inputs } return nil } @@ -3053,59 +3114,59 @@ var file_cosmos_bank_v1beta1_tx_proto_rawDesc = []byte{ 0x6f, 0x75, 0x6e, 0x74, 0x3a, 0x19, 0x88, 0xa0, 0x1f, 0x00, 0xe8, 0xa0, 0x1f, 0x00, 0x82, 0xe7, 0xb0, 0x2a, 0x0c, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x4d, 0x73, 0x67, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x93, 0x01, 0x0a, 0x0c, 0x4d, 0x73, 0x67, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x53, - 0x65, 0x6e, 0x64, 0x12, 0x36, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x6e, 0x6b, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x42, 0x04, - 0xc8, 0xde, 0x1f, 0x00, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x3b, 0x0a, 0x07, 0x6f, - 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, - 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x6e, 0x6b, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x42, 0x04, 0xc8, 0xde, 0x1f, 0x00, 0x52, - 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x3a, 0x0e, 0xe8, 0xa0, 0x1f, 0x00, 0x82, 0xe7, - 0xb0, 0x2a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x16, 0x0a, 0x14, 0x4d, 0x73, 0x67, 0x4d, - 0x75, 0x6c, 0x74, 0x69, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x94, 0x01, 0x0a, 0x0f, 0x4d, 0x73, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x73, 0x12, 0x36, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, - 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x06, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, - 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x6e, 0x6b, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x42, 0x04, 0xc8, 0xde, 0x1f, 0x00, 0x52, - 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3a, 0x0e, 0x82, 0xe7, 0xb0, 0x2a, 0x09, 0x61, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x19, 0x0a, 0x17, 0x4d, 0x73, 0x67, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x32, 0x90, 0x02, 0x0a, 0x03, 0x4d, 0x73, 0x67, 0x12, 0x4a, 0x0a, 0x04, 0x53, 0x65, - 0x6e, 0x64, 0x12, 0x1c, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x6e, 0x6b, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x53, 0x65, 0x6e, 0x64, - 0x1a, 0x24, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x6e, 0x6b, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x09, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x53, - 0x65, 0x6e, 0x64, 0x12, 0x21, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x6e, - 0x6b, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x4d, 0x75, 0x6c, - 0x74, 0x69, 0x53, 0x65, 0x6e, 0x64, 0x1a, 0x29, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, - 0x62, 0x61, 0x6e, 0x6b, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x73, 0x67, - 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x62, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x12, 0x24, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x6e, 0x6b, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x2c, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, - 0x2e, 0x62, 0x61, 0x6e, 0x6b, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x73, + 0x73, 0x65, 0x22, 0x96, 0x01, 0x0a, 0x0c, 0x4d, 0x73, 0x67, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x53, + 0x65, 0x6e, 0x64, 0x12, 0x38, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x6e, + 0x6b, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x42, + 0x04, 0xc8, 0xde, 0x1f, 0x00, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x3b, 0x0a, + 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x6e, 0x6b, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x42, 0x04, 0xc8, 0xde, 0x1f, + 0x00, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x3a, 0x0f, 0xe8, 0xa0, 0x1f, 0x00, + 0x82, 0xe7, 0xb0, 0x2a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x22, 0x16, 0x0a, 0x14, 0x4d, + 0x73, 0x67, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x94, 0x01, 0x0a, 0x0f, 0x4d, 0x73, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x36, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, + 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, + 0x39, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1b, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x6e, 0x6b, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x42, 0x04, 0xc8, 0xde, + 0x1f, 0x00, 0x52, 0x06, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3a, 0x0e, 0x82, 0xe7, 0xb0, 0x2a, + 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x19, 0x0a, 0x17, 0x4d, 0x73, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0xc2, 0x01, 0x0a, 0x17, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6f, - 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x6e, 0x6b, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x42, 0x07, 0x54, 0x78, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x63, 0x6f, - 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, - 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x3b, 0x62, 0x61, 0x6e, 0x6b, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xa2, 0x02, - 0x03, 0x43, 0x42, 0x58, 0xaa, 0x02, 0x13, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x42, 0x61, - 0x6e, 0x6b, 0x2e, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xca, 0x02, 0x13, 0x43, 0x6f, 0x73, - 0x6d, 0x6f, 0x73, 0x5c, 0x42, 0x61, 0x6e, 0x6b, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0xe2, 0x02, 0x1f, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x42, 0x61, 0x6e, 0x6b, 0x5c, 0x56, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0xea, 0x02, 0x15, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x42, 0x61, 0x6e, - 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x90, 0x02, 0x0a, 0x03, 0x4d, 0x73, 0x67, 0x12, 0x4a, 0x0a, + 0x04, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x1c, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, + 0x61, 0x6e, 0x6b, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x53, + 0x65, 0x6e, 0x64, 0x1a, 0x24, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x6e, + 0x6b, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x53, 0x65, 0x6e, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x09, 0x4d, 0x75, 0x6c, + 0x74, 0x69, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x21, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, + 0x62, 0x61, 0x6e, 0x6b, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x73, 0x67, + 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x53, 0x65, 0x6e, 0x64, 0x1a, 0x29, 0x2e, 0x63, 0x6f, 0x73, 0x6d, + 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x6e, 0x6b, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x4d, 0x73, 0x67, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x12, 0x24, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, + 0x6e, 0x6b, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x2c, 0x2e, 0x63, 0x6f, 0x73, + 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x6e, 0x6b, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x4d, 0x73, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0xc2, 0x01, 0x0a, 0x17, 0x63, 0x6f, 0x6d, + 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x6e, 0x6b, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x42, 0x07, 0x54, 0x78, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, + 0x30, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, 0x62, 0x61, 0x6e, 0x6b, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0xa2, 0x02, 0x03, 0x43, 0x42, 0x58, 0xaa, 0x02, 0x13, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, + 0x2e, 0x42, 0x61, 0x6e, 0x6b, 0x2e, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xca, 0x02, 0x13, + 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x42, 0x61, 0x6e, 0x6b, 0x5c, 0x56, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0xe2, 0x02, 0x1f, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x42, 0x61, 0x6e, + 0x6b, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x15, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, + 0x42, 0x61, 0x6e, 0x6b, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -3135,7 +3196,7 @@ var file_cosmos_bank_v1beta1_tx_proto_goTypes = []interface{}{ } var file_cosmos_bank_v1beta1_tx_proto_depIdxs = []int32{ 6, // 0: cosmos.bank.v1beta1.MsgSend.amount:type_name -> cosmos.base.v1beta1.Coin - 7, // 1: cosmos.bank.v1beta1.MsgMultiSend.input:type_name -> cosmos.bank.v1beta1.Input + 7, // 1: cosmos.bank.v1beta1.MsgMultiSend.inputs:type_name -> cosmos.bank.v1beta1.Input 8, // 2: cosmos.bank.v1beta1.MsgMultiSend.outputs:type_name -> cosmos.bank.v1beta1.Output 9, // 3: cosmos.bank.v1beta1.MsgUpdateParams.params:type_name -> cosmos.bank.v1beta1.Params 0, // 4: cosmos.bank.v1beta1.Msg.Send:input_type -> cosmos.bank.v1beta1.MsgSend diff --git a/proto/cosmos/bank/v1beta1/tx.proto b/proto/cosmos/bank/v1beta1/tx.proto index 9cd92426d953..a67e76d27098 100644 --- a/proto/cosmos/bank/v1beta1/tx.proto +++ b/proto/cosmos/bank/v1beta1/tx.proto @@ -40,13 +40,15 @@ message MsgSend { // MsgSendResponse defines the Msg/Send response type. message MsgSendResponse {} -// MsgMultiSend represents a single input, multi-out send message. +// MsgMultiSend represents an arbitrary multi-in, multi-out send message. message MsgMultiSend { - option (cosmos.msg.v1.signer) = "input"; + option (cosmos.msg.v1.signer) = "inputs"; option (gogoproto.equal) = false; - Input input = 1 [(gogoproto.nullable) = false]; + // Inputs, despite being `repeated`, only allows one sender input. This is + // checked in MsgMultiSend's ValidateBasic. + repeated Input inputs = 1 [(gogoproto.nullable) = false]; repeated Output outputs = 2 [(gogoproto.nullable) = false]; } diff --git a/x/bank/app_test.go b/x/bank/app_test.go index 1dcd269b7d4a..ef5621d34641 100644 --- a/x/bank/app_test.go +++ b/x/bank/app_test.go @@ -46,28 +46,32 @@ var ( sendMsg1 = types.NewMsgSend(addr1, addr2, coins) multiSendMsg1 = &types.MsgMultiSend{ - Input: types.NewInput(addr1, coins), + Inputs: []types.Input{types.NewInput(addr1, coins)}, Outputs: []types.Output{types.NewOutput(addr2, coins)}, } multiSendMsg2 = &types.MsgMultiSend{ - Input: types.NewInput(addr1, coins), + Inputs: []types.Input{types.NewInput(addr1, coins)}, Outputs: []types.Output{ types.NewOutput(addr2, halfCoins), types.NewOutput(addr3, halfCoins), }, } multiSendMsg3 = &types.MsgMultiSend{ - Input: types.NewInput(addr2, coins), + Inputs: []types.Input{types.NewInput(addr2, coins)}, Outputs: []types.Output{ types.NewOutput(addr1, coins), }, } multiSendMsg4 = &types.MsgMultiSend{ - Input: types.NewInput(addr1, coins), + Inputs: []types.Input{types.NewInput(addr1, coins)}, Outputs: []types.Output{ types.NewOutput(moduleAccAddr, coins), }, } + invalidMultiSendMsg = &types.MsgMultiSend{ + Inputs: []types.Input{types.NewInput(addr1, coins), types.NewInput(addr2, coins)}, + Outputs: []types.Output{}, + } ) func TestSendNotEnoughBalance(t *testing.T) { @@ -154,6 +158,15 @@ func TestMsgMultiSendWithAccounts(t *testing.T) { expPass: false, privKeys: []cryptotypes.PrivKey{priv1}, }, + { + desc: "multiple inputs not allowed", + msgs: []sdk.Msg{invalidMultiSendMsg}, + accNums: []uint64{0}, + accSeqs: []uint64{0}, + expSimPass: false, + expPass: false, + privKeys: []cryptotypes.PrivKey{priv1}, + }, } for _, tc := range testCases { diff --git a/x/bank/client/cli/tx.go b/x/bank/client/cli/tx.go index 156bdaf5b8b2..5deb617b8952 100644 --- a/x/bank/client/cli/tx.go +++ b/x/bank/client/cli/tx.go @@ -131,7 +131,7 @@ When using '--dry-run' a key name cannot be used, only a bech32 address. amount = coins.MulInt(totalAddrs) } - msg := types.NewMsgMultiSend(types.NewInput(clientCtx.FromAddress, amount), output) + msg := types.NewMsgMultiSend([]types.Input{types.NewInput(clientCtx.FromAddress, amount)}, output) return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, diff --git a/x/bank/keeper/keeper_test.go b/x/bank/keeper/keeper_test.go index fd49dc2d51c9..f294ed470c3a 100644 --- a/x/bank/keeper/keeper_test.go +++ b/x/bank/keeper/keeper_test.go @@ -361,15 +361,14 @@ func (suite *IntegrationTestSuite) TestInputOutputNewAccount() { suite.Require().Nil(app.AccountKeeper.GetAccount(ctx, addr2)) suite.Require().Empty(app.BankKeeper.GetAllBalances(ctx, addr2)) - input := types.Input{ - Address: addr1.String(), - Coins: sdk.NewCoins(newFooCoin(30), newBarCoin(10)), + inputs := []types.Input{ + {Address: addr1.String(), Coins: sdk.NewCoins(newFooCoin(30), newBarCoin(10))}, } outputs := []types.Output{ {Address: addr2.String(), Coins: sdk.NewCoins(newFooCoin(30), newBarCoin(10))}, } - suite.Require().NoError(app.BankKeeper.InputOutputCoins(ctx, input, outputs)) + suite.Require().NoError(app.BankKeeper.InputOutputCoins(ctx, inputs, outputs)) expected := sdk.NewCoins(newFooCoin(30), newBarCoin(10)) acc2Balances := app.BankKeeper.GetAllBalances(ctx, addr2) @@ -393,9 +392,8 @@ func (suite *IntegrationTestSuite) TestInputOutputCoins() { acc3 := app.AccountKeeper.NewAccountWithAddress(ctx, addr3) app.AccountKeeper.SetAccount(ctx, acc3) - input := types.Input{ - Address: addr1.String(), - Coins: sdk.NewCoins(newFooCoin(60), newBarCoin(20)), + input := []types.Input{ + {Address: addr1.String(), Coins: sdk.NewCoins(newFooCoin(60), newBarCoin(20))}, } outputs := []types.Output{ {Address: addr2.String(), Coins: sdk.NewCoins(newFooCoin(30), newBarCoin(10))}, @@ -407,9 +405,11 @@ func (suite *IntegrationTestSuite) TestInputOutputCoins() { suite.Require().NoError(testutil.FundAccount(app.BankKeeper, ctx, addr1, balances)) - insufficientInput := types.Input{ - Address: addr1.String(), - Coins: sdk.NewCoins(newFooCoin(300), newBarCoin(100)), + insufficientInput := []types.Input{ + { + Address: addr1.String(), + Coins: sdk.NewCoins(newFooCoin(300), newBarCoin(100)), + }, } insufficientOutputs := []types.Output{ {Address: addr2.String(), Coins: sdk.NewCoins(newFooCoin(300), newBarCoin(100))}, @@ -613,9 +613,10 @@ func (suite *IntegrationTestSuite) TestMsgMultiSendEvents() { coins := sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50), sdk.NewInt64Coin(barDenom, 100)) newCoins := sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50)) newCoins2 := sdk.NewCoins(sdk.NewInt64Coin(barDenom, 100)) - input := types.Input{ - Address: addr.String(), - Coins: coins, + input := []types.Input{ + {Address: addr.String(), + Coins: coins, + }, } outputs := []types.Output{ {Address: addr3.String(), Coins: newCoins}, diff --git a/x/bank/keeper/msg_server.go b/x/bank/keeper/msg_server.go index b33e3394f49b..7555dc506cb6 100644 --- a/x/bank/keeper/msg_server.go +++ b/x/bank/keeper/msg_server.go @@ -76,8 +76,10 @@ func (k msgServer) MultiSend(goCtx context.Context, msg *types.MsgMultiSend) (*t ctx := sdk.UnwrapSDKContext(goCtx) // NOTE: totalIn == totalOut should already have been checked - if err := k.IsSendEnabledCoins(ctx, msg.Input.Coins...); err != nil { - return nil, err + for _, in := range msg.Inputs { + if err := k.IsSendEnabledCoins(ctx, in.Coins...); err != nil { + return nil, err + } } for _, out := range msg.Outputs { @@ -88,7 +90,7 @@ func (k msgServer) MultiSend(goCtx context.Context, msg *types.MsgMultiSend) (*t } } - err := k.InputOutputCoins(ctx, msg.Input, msg.Outputs) + err := k.InputOutputCoins(ctx, msg.Inputs, msg.Outputs) if err != nil { return nil, err } diff --git a/x/bank/keeper/send.go b/x/bank/keeper/send.go index 16bd31e661ec..88ff044898c2 100644 --- a/x/bank/keeper/send.go +++ b/x/bank/keeper/send.go @@ -18,7 +18,7 @@ import ( type SendKeeper interface { ViewKeeper - InputOutputCoins(ctx sdk.Context, input types.Input, outputs []types.Output) error + InputOutputCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) error SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error GetParams(ctx sdk.Context) types.Params @@ -120,30 +120,31 @@ func (k BaseSendKeeper) SetParams(ctx sdk.Context, params types.Params) error { // InputOutputCoins performs multi-send functionality. It accepts a series of // inputs that correspond to a series of outputs. It returns an error if the // inputs and outputs don't line up or if any single transfer of tokens fails. -func (k BaseSendKeeper) InputOutputCoins(ctx sdk.Context, input types.Input, outputs []types.Output) error { +func (k BaseSendKeeper) InputOutputCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) error { // Safety check ensuring that when sending coins the keeper must maintain the // Check supply invariant and validity of Coins. - if err := types.ValidateInputsOutputs(input, outputs); err != nil { + if err := types.ValidateInputsOutputs(inputs, outputs); err != nil { return err } - inAddress, err := sdk.AccAddressFromBech32(input.Address) - if err != nil { - return err - } + for _, in := range inputs { + inAddress, err := sdk.AccAddressFromBech32(in.Address) + if err != nil { + return err + } - err = k.subUnlockedCoins(ctx, inAddress, input.Coins) - if err != nil { - return err + err = k.subUnlockedCoins(ctx, inAddress, in.Coins) + if err != nil { + return err + } + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(types.AttributeKeySender, in.Address), + ), + ) } - ctx.EventManager().EmitEvent( - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(types.AttributeKeySender, input.Address), - ), - ) - for _, out := range outputs { outAddress, err := sdk.AccAddressFromBech32(out.Address) if err != nil { diff --git a/x/bank/simulation/operations.go b/x/bank/simulation/operations.go index 76dbdd7297de..f5ab63b2bc7e 100644 --- a/x/bank/simulation/operations.go +++ b/x/bank/simulation/operations.go @@ -176,25 +176,40 @@ func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simtypes.Ope r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { - // random number of outputs between [1, 3] + // random number of inputs/outputs between [1, 3] + inputs := make([]types.Input, r.Intn(1)+1) outputs := make([]types.Output, r.Intn(3)+1) + // collect signer privKeys + privs := make([]cryptotypes.PrivKey, len(inputs)) + + // use map to check if address already exists as input + usedAddrs := make(map[string]bool) + var totalSentCoins sdk.Coins - // generate random input fields, ignore to address - from, _, coins, skip := randomSendFields(r, ctx, accs, bk, ak) + for i := range inputs { + // generate random input fields, ignore to address + from, _, coins, skip := randomSendFields(r, ctx, accs, bk, ak) - // if coins slice is empty, we can not create valid types.MsgMultiSend - if len(coins) == 0 { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgMultiSend, "empty coins slice"), nil, nil - } + // make sure account is fresh and not used in previous input + for usedAddrs[from.Address.String()] { + from, _, coins, skip = randomSendFields(r, ctx, accs, bk, ak) + } - if skip { - return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgMultiSend, "skip all transfers"), nil, nil - } + if skip { + return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgMultiSend, "skip all transfers"), nil, nil + } - // set next input and accumulate total sent coins - input := types.NewInput(from.Address, coins) - totalSentCoins = totalSentCoins.Add(coins...) + // set input address in used address map + usedAddrs[from.Address.String()] = true + + // set signer privkey + privs[i] = from.PrivKey + + // set next input and accumulate total sent coins + inputs[i] = types.NewInput(from.Address, coins) + totalSentCoins = totalSentCoins.Add(coins...) + } // Check send_enabled status of each sent coin denom if err := bk.IsSendEnabledCoins(ctx, totalSentCoins...); err != nil { @@ -231,10 +246,10 @@ func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simtypes.Ope } msg := &types.MsgMultiSend{ - Input: input, + Inputs: inputs, Outputs: outputs, } - err := sendMsgMultiSend(r, app, bk, ak, msg, ctx, chainID, []cryptotypes.PrivKey{from.PrivKey}) + err := sendMsgMultiSend(r, app, bk, ak, msg, ctx, chainID, privs) if err != nil { return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "invalid transfers"), nil, err } @@ -249,20 +264,22 @@ func SimulateMsgMultiSendToModuleAccount(ak types.AccountKeeper, bk keeper.Keepe r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { + inputs := make([]types.Input, 2) outputs := make([]types.Output, moduleAccCount) - + // collect signer privKeys + privs := make([]cryptotypes.PrivKey, len(inputs)) var totalSentCoins sdk.Coins - - sender := accs[0] - spendable := bk.SpendableCoins(ctx, sender.Address) - coins := simtypes.RandSubsetCoins(r, spendable) - input := types.NewInput(sender.Address, coins) - totalSentCoins = totalSentCoins.Add(coins...) - + for i := range inputs { + sender := accs[i] + privs[i] = sender.PrivKey + spendable := bk.SpendableCoins(ctx, sender.Address) + coins := simtypes.RandSubsetCoins(r, spendable) + inputs[i] = types.NewInput(sender.Address, coins) + totalSentCoins = totalSentCoins.Add(coins...) + } if err := bk.IsSendEnabledCoins(ctx, totalSentCoins...); err != nil { return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgMultiSend, err.Error()), nil, nil } - moduleAccounts := getModuleAccounts(ak, ctx, moduleAccCount) for i := range outputs { var outCoins sdk.Coins @@ -275,12 +292,9 @@ func SimulateMsgMultiSendToModuleAccount(ak types.AccountKeeper, bk keeper.Keepe outCoins = simtypes.RandSubsetCoins(r, totalSentCoins) totalSentCoins = totalSentCoins.Sub(outCoins...) } - outputs[i] = types.NewOutput(moduleAccounts[i].Address, outCoins) } - // remove any output that has no coins - for i := 0; i < len(outputs); { if outputs[i].Coins.Empty() { outputs[i] = outputs[len(outputs)-1] @@ -290,16 +304,14 @@ func SimulateMsgMultiSendToModuleAccount(ak types.AccountKeeper, bk keeper.Keepe i++ } } - msg := &types.MsgMultiSend{ - Input: input, + Inputs: inputs, Outputs: outputs, } - err := sendMsgMultiSend(r, app, bk, ak, msg, ctx, chainID, []cryptotypes.PrivKey{sender.PrivKey}) + err := sendMsgMultiSend(r, app, bk, ak, msg, ctx, chainID, privs) if err != nil { return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "invalid transfers"), nil, err } - return simtypes.NewOperationMsg(msg, true, "", nil), nil, nil } } @@ -310,27 +322,29 @@ func sendMsgMultiSend( r *rand.Rand, app *baseapp.BaseApp, bk keeper.Keeper, ak types.AccountKeeper, msg *types.MsgMultiSend, ctx sdk.Context, chainID string, privkeys []cryptotypes.PrivKey, ) error { - - addr := sdk.MustAccAddressFromBech32(msg.Input.Address) - acc := ak.GetAccount(ctx, addr) - + accountNumbers := make([]uint64, len(msg.Inputs)) + sequenceNumbers := make([]uint64, len(msg.Inputs)) + for i := 0; i < len(msg.Inputs); i++ { + addr := sdk.MustAccAddressFromBech32(msg.Inputs[i].Address) + acc := ak.GetAccount(ctx, addr) + accountNumbers[i] = acc.GetAccountNumber() + sequenceNumbers[i] = acc.GetSequence() + } var ( fees sdk.Coins err error ) - + addr := sdk.MustAccAddressFromBech32(msg.Inputs[0].Address) // feePayer is the first signer, i.e. first input address feePayer := ak.GetAccount(ctx, addr) spendable := bk.SpendableCoins(ctx, feePayer.GetAddress()) - - coins, hasNeg := spendable.SafeSub(msg.Input.Coins...) + coins, hasNeg := spendable.SafeSub(msg.Inputs[0].Coins...) if !hasNeg { fees, err = simtypes.RandomFees(r, ctx, coins) if err != nil { return err } } - txGen := simappparams.MakeTestEncodingConfig().TxConfig tx, err := simtestutil.GenSignedMockTx( r, @@ -339,19 +353,17 @@ func sendMsgMultiSend( fees, simtestutil.DefaultGenTxGas, chainID, - []uint64{acc.GetAccountNumber()}, - []uint64{acc.GetSequence()}, + accountNumbers, + sequenceNumbers, privkeys..., ) if err != nil { return err } - _, _, err = app.SimDeliver(txGen.TxEncoder(), tx) if err != nil { return err } - return nil } diff --git a/x/bank/simulation/operations_test.go b/x/bank/simulation/operations_test.go index a75f2563c809..6a0b8c34ac67 100644 --- a/x/bank/simulation/operations_test.go +++ b/x/bank/simulation/operations_test.go @@ -114,11 +114,12 @@ func (suite *SimTestSuite) TestSimulateMsgMultiSend() { types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) require.True(operationMsg.OK) - require.Equal("cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.Input.Address) - require.Equal("4896096stake", msg.Input.Coins.String()) - require.Len(msg.Outputs, 3) + require.Len(msg.Inputs, 1) + require.Equal("cosmos1tnh2q55v8wyygtt9srz5safamzdengsnqeycj3", msg.Inputs[0].Address) + require.Equal("114949958stake", msg.Inputs[0].Coins.String()) + require.Len(msg.Outputs, 2) require.Equal("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Outputs[1].Address) - require.Equal("891479stake", msg.Outputs[1].Coins.String()) + require.Equal("107287087stake", msg.Outputs[1].Coins.String()) require.Equal(types.TypeMsgMultiSend, msg.Type()) require.Equal(types.ModuleName, msg.Route()) require.Len(futureOperations, 0) diff --git a/x/bank/types/errors.go b/x/bank/types/errors.go index 00851d165f80..3fca352de3f4 100644 --- a/x/bank/types/errors.go +++ b/x/bank/types/errors.go @@ -6,9 +6,11 @@ import ( // x/bank module sentinel errors var ( + ErrNoInputs = sdkerrors.Register(ModuleName, 2, "no inputs to send transaction") ErrNoOutputs = sdkerrors.Register(ModuleName, 3, "no outputs to send transaction") ErrInputOutputMismatch = sdkerrors.Register(ModuleName, 4, "sum inputs != sum outputs") ErrSendDisabled = sdkerrors.Register(ModuleName, 5, "send transactions are disabled") ErrDenomMetadataNotFound = sdkerrors.Register(ModuleName, 6, "client denom metadata not found") ErrInvalidKey = sdkerrors.Register(ModuleName, 7, "invalid key") + ErrMultipleSenders = sdkerrors.Register(ModuleName, 8, "multiple senders not allowed") ) diff --git a/x/bank/types/msgs.go b/x/bank/types/msgs.go index dedcf151392f..881126f9cbf7 100644 --- a/x/bank/types/msgs.go +++ b/x/bank/types/msgs.go @@ -64,8 +64,8 @@ func (msg MsgSend) GetSigners() []sdk.AccAddress { } // NewMsgMultiSend - construct arbitrary multi-in, multi-out send msg. -func NewMsgMultiSend(in Input, out []Output) *MsgMultiSend { - return &MsgMultiSend{Input: in, Outputs: out} +func NewMsgMultiSend(in []Input, out []Output) *MsgMultiSend { + return &MsgMultiSend{Inputs: in, Outputs: out} } // Route Implements Msg @@ -79,15 +79,19 @@ func (msg MsgMultiSend) ValidateBasic() error { // this just makes sure the input and all the outputs are properly formatted, // not that they actually have the money inside - if err := msg.Input.ValidateBasic(); err != nil { - return err + if len(msg.Inputs) == 0 { + return ErrNoInputs + } + + if len(msg.Inputs) != 1 { + return ErrMultipleSenders } if len(msg.Outputs) == 0 { return ErrNoOutputs } - return ValidateInputsOutputs(msg.Input, msg.Outputs) + return ValidateInputsOutputs(msg.Inputs, msg.Outputs) } // GetSignBytes Implements Msg. @@ -97,8 +101,13 @@ func (msg MsgMultiSend) GetSignBytes() []byte { // GetSigners Implements Msg. func (msg MsgMultiSend) GetSigners() []sdk.AccAddress { - addrs, _ := sdk.AccAddressFromBech32(msg.Input.Address) - return []sdk.AccAddress{addrs} + addrs := make([]sdk.AccAddress, len(msg.Inputs)) + for i, in := range msg.Inputs { + inAddr, _ := sdk.AccAddressFromBech32(in.Address) + addrs[i] = inAddr + } + + return addrs } // ValidateBasic - validate transaction input @@ -155,15 +164,16 @@ func NewOutput(addr sdk.AccAddress, coins sdk.Coins) Output { // ValidateInputsOutputs validates that each respective input and output is // valid and that the sum of inputs is equal to the sum of outputs. -func ValidateInputsOutputs(input Input, outputs []Output) error { +func ValidateInputsOutputs(inputs []Input, outputs []Output) error { var totalIn, totalOut sdk.Coins - if err := input.ValidateBasic(); err != nil { - return err + for _, in := range inputs { + if err := in.ValidateBasic(); err != nil { + return err + } + totalIn = totalIn.Add(in.Coins...) } - totalIn = totalIn.Add(input.Coins...) - for _, out := range outputs { if err := out.ValidateBasic(); err != nil { return err diff --git a/x/bank/types/msgs_test.go b/x/bank/types/msgs_test.go index 27054fc06cb8..d9ce4e60345d 100644 --- a/x/bank/types/msgs_test.go +++ b/x/bank/types/msgs_test.go @@ -70,7 +70,7 @@ func TestMsgMultiSendRoute(t *testing.T) { addr2 := sdk.AccAddress([]byte("output")) coins := sdk.NewCoins(sdk.NewInt64Coin("atom", 10)) msg := MsgMultiSend{ - Input: NewInput(addr1, coins), + Inputs: []Input{NewInput(addr1, coins)}, Outputs: []Output{NewOutput(addr2, coins)}, } @@ -183,13 +183,13 @@ func TestMsgMultiSendValidation(t *testing.T) { tx MsgMultiSend expErrMsg string }{ - {false, MsgMultiSend{}, "invalid input address"}, // no input or output - {false, MsgMultiSend{Input: input1}, "no outputs to send transaction"}, // just input - {false, MsgMultiSend{Outputs: []Output{output1}}, "invalid input address"}, // just output + {false, MsgMultiSend{}, "no inputs to send transaction"}, // no input or output + {false, MsgMultiSend{Inputs: []Input{input1}}, "no outputs to send transaction"}, // just input + {false, MsgMultiSend{Outputs: []Output{output1}}, "no inputs to send transaction"}, // just output { false, MsgMultiSend{ - Input: NewInput(emptyAddr, atom123), // invalid input + Inputs: []Input{NewInput(emptyAddr, atom123)}, // invalid input Outputs: []Output{output1}, }, "invalid input address", @@ -197,7 +197,7 @@ func TestMsgMultiSendValidation(t *testing.T) { { false, MsgMultiSend{ - Input: input1, + Inputs: []Input{input1}, Outputs: []Output{{emptyAddr.String(), atom123}}, // invalid output }, "invalid output address", @@ -205,7 +205,7 @@ func TestMsgMultiSendValidation(t *testing.T) { { false, MsgMultiSend{ - Input: input1, + Inputs: []Input{input1}, Outputs: []Output{output2}, // amounts don't match }, "sum inputs != sum outputs", @@ -213,23 +213,23 @@ func TestMsgMultiSendValidation(t *testing.T) { { true, MsgMultiSend{ - Input: input1, + Inputs: []Input{input1}, Outputs: []Output{output1}, }, "", }, { - true, + false, MsgMultiSend{ - Input: input2, + Inputs: []Input{input1, input2}, Outputs: []Output{output3, output4}, }, - "", + "multiple senders not allowed", }, { true, MsgMultiSend{ - Input: NewInput(addr2, atom123.MulInt(sdk.NewInt(2))), + Inputs: []Input{NewInput(addr2, atom123.MulInt(sdk.NewInt(2)))}, Outputs: []Output{output1, output1}, }, "", @@ -253,22 +253,29 @@ func TestMsgMultiSendGetSignBytes(t *testing.T) { addr2 := sdk.AccAddress([]byte("output")) coins := sdk.NewCoins(sdk.NewInt64Coin("atom", 10)) msg := MsgMultiSend{ - Input: NewInput(addr1, coins), + Inputs: []Input{NewInput(addr1, coins)}, Outputs: []Output{NewOutput(addr2, coins)}, } res := msg.GetSignBytes() - expected := `{"type":"cosmos-sdk/MsgMultiSend","value":{"input":{"address":"cosmos1d9h8qat57ljhcm","coins":[{"amount":"10","denom":"atom"}]},"outputs":[{"address":"cosmos1da6hgur4wsmpnjyg","coins":[{"amount":"10","denom":"atom"}]}]}}` + expected := `{"type":"cosmos-sdk/MsgMultiSend","value":{"inputs":[{"address":"cosmos1d9h8qat57ljhcm","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmos1da6hgur4wsmpnjyg","coins":[{"amount":"10","denom":"atom"}]}]}}` require.Equal(t, expected, string(res)) } func TestMsgMultiSendGetSigners(t *testing.T) { - addr := sdk.AccAddress([]byte("input111111111111111")) - input := NewInput(addr, nil) - msg := NewMsgMultiSend(input, nil) + addrs := make([]string, 3) + inputs := make([]Input, 3) + for i, v := range []string{"input111111111111111", "input222222222222222", "input333333333333333"} { + addr := sdk.AccAddress([]byte(v)) + inputs[i] = NewInput(addr, nil) + addrs[i] = addr.String() + } + msg := NewMsgMultiSend(inputs, nil) + res := msg.GetSigners() - require.Equal(t, 1, len(res)) - require.True(t, addr.Equals(res[0])) + for i, signer := range res { + require.Equal(t, signer.String(), addrs[i]) + } } func TestMsgSendGetSigners(t *testing.T) { diff --git a/x/bank/types/tx.pb.go b/x/bank/types/tx.pb.go index dc9b81020149..a0baa198170d 100644 --- a/x/bank/types/tx.pb.go +++ b/x/bank/types/tx.pb.go @@ -109,9 +109,11 @@ func (m *MsgSendResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgSendResponse proto.InternalMessageInfo -// MsgMultiSend represents a single input, multi-out send message. +// MsgMultiSend represents an arbitrary multi-in, multi-out send message. type MsgMultiSend struct { - Input Input `protobuf:"bytes,1,opt,name=input,proto3" json:"input"` + // Inputs, despite being `repeated`, only allows one sender input. This is + // checked in MsgMultiSend's ValidateBasic. + Inputs []Input `protobuf:"bytes,1,rep,name=inputs,proto3" json:"inputs"` Outputs []Output `protobuf:"bytes,2,rep,name=outputs,proto3" json:"outputs"` } @@ -148,11 +150,11 @@ func (m *MsgMultiSend) XXX_DiscardUnknown() { var xxx_messageInfo_MsgMultiSend proto.InternalMessageInfo -func (m *MsgMultiSend) GetInput() Input { +func (m *MsgMultiSend) GetInputs() []Input { if m != nil { - return m.Input + return m.Inputs } - return Input{} + return nil } func (m *MsgMultiSend) GetOutputs() []Output { @@ -310,41 +312,41 @@ func init() { func init() { proto.RegisterFile("cosmos/bank/v1beta1/tx.proto", fileDescriptor_1d8cb1613481f5b7) } var fileDescriptor_1d8cb1613481f5b7 = []byte{ - // 538 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0xbd, 0x6e, 0xd3, 0x50, - 0x14, 0xb6, 0x9b, 0x92, 0x2a, 0x27, 0x51, 0x11, 0x26, 0xa2, 0x89, 0xa9, 0x9c, 0x62, 0x31, 0xa4, - 0x88, 0xda, 0x24, 0x48, 0x45, 0xa4, 0x13, 0xe9, 0x04, 0x52, 0x04, 0x4a, 0xc5, 0x00, 0x4b, 0x65, - 0xc7, 0x17, 0xd7, 0x2a, 0xf6, 0xb5, 0x7c, 0xaf, 0xab, 0x76, 0x65, 0x62, 0xac, 0x04, 0x0f, 0xd0, - 0x99, 0x89, 0x81, 0x87, 0xe8, 0x58, 0x31, 0x31, 0x01, 0x4a, 0x06, 0x78, 0x0b, 0xd0, 0xfd, 0xb1, - 0x93, 0x42, 0x7e, 0x3a, 0x39, 0xca, 0xf7, 0x73, 0xbe, 0xf3, 0x1d, 0x27, 0xb0, 0x3e, 0xc0, 0x24, - 0xc4, 0xc4, 0x76, 0x9d, 0xe8, 0xd0, 0x3e, 0x6a, 0xb9, 0x88, 0x3a, 0x2d, 0x9b, 0x1e, 0x5b, 0x71, - 0x82, 0x29, 0xd6, 0x6e, 0x0a, 0xd4, 0x62, 0xa8, 0x25, 0x51, 0xbd, 0xea, 0x63, 0x1f, 0x73, 0xdc, - 0x66, 0x9f, 0x04, 0x55, 0x37, 0x72, 0x23, 0x82, 0x72, 0xa3, 0x01, 0x0e, 0xa2, 0xff, 0xf0, 0x89, - 0x41, 0xdc, 0x57, 0xe0, 0x75, 0x81, 0xef, 0x0b, 0x63, 0x39, 0x57, 0x40, 0x6b, 0x52, 0x1a, 0x12, - 0xdf, 0x3e, 0x6a, 0xb1, 0x87, 0x00, 0xcc, 0x3f, 0x2a, 0xac, 0xf4, 0x88, 0xbf, 0x87, 0x22, 0x4f, - 0xdb, 0x81, 0xca, 0x9b, 0x04, 0x87, 0xfb, 0x8e, 0xe7, 0x25, 0x88, 0x90, 0x9a, 0xba, 0xa1, 0x36, - 0x4b, 0xdd, 0xda, 0xd7, 0x2f, 0x5b, 0x55, 0x69, 0xf6, 0x44, 0x20, 0x7b, 0x34, 0x09, 0x22, 0xbf, - 0x5f, 0x66, 0x6c, 0xf9, 0x95, 0xf6, 0x08, 0x80, 0xe2, 0x5c, 0xba, 0xb4, 0x40, 0x5a, 0xa2, 0x38, - 0x13, 0x0e, 0xa0, 0xe8, 0x84, 0x38, 0x8d, 0x68, 0xad, 0xb0, 0x51, 0x68, 0x96, 0xdb, 0x75, 0x2b, - 0x6f, 0x8c, 0xa0, 0xac, 0x31, 0x6b, 0x17, 0x07, 0x51, 0xf7, 0xc1, 0xf9, 0xf7, 0x86, 0xf2, 0xe9, - 0x47, 0xa3, 0xe9, 0x07, 0xf4, 0x20, 0x75, 0xad, 0x01, 0x0e, 0xe5, 0x9a, 0xf2, 0xb1, 0x45, 0xbc, - 0x43, 0x9b, 0x9e, 0xc4, 0x88, 0x70, 0x01, 0xe9, 0x4b, 0xeb, 0x4e, 0xfd, 0xfd, 0x59, 0x43, 0xf9, - 0x7d, 0xd6, 0x50, 0xde, 0xfd, 0xfa, 0x7c, 0xef, 0xd2, 0x96, 0xe6, 0x0d, 0xb8, 0x2e, 0x0b, 0xe8, - 0x23, 0x12, 0xe3, 0x88, 0x20, 0xf3, 0x83, 0x0a, 0x95, 0x1e, 0xf1, 0x7b, 0xe9, 0x5b, 0x1a, 0xf0, - 0x66, 0xb6, 0xe1, 0x5a, 0x10, 0xc5, 0x29, 0xe5, 0x95, 0x94, 0xdb, 0xba, 0x35, 0xe5, 0xa8, 0xd6, - 0x53, 0xc6, 0xe8, 0x2e, 0xb3, 0x8c, 0x7d, 0x41, 0xd7, 0x76, 0x60, 0x05, 0xa7, 0x34, 0x4e, 0x29, - 0x6b, 0x84, 0x2d, 0x77, 0x7b, 0xaa, 0xf2, 0x39, 0xe7, 0x48, 0x69, 0xa6, 0xe8, 0xac, 0x66, 0x79, - 0x85, 0x99, 0x79, 0x0b, 0xaa, 0x93, 0xa1, 0xf2, 0xb4, 0x1f, 0x55, 0xbe, 0xc1, 0xcb, 0xd8, 0x73, - 0x28, 0x7a, 0xe1, 0x24, 0x4e, 0x48, 0xb4, 0x6d, 0x28, 0x39, 0x29, 0x3d, 0xc0, 0x49, 0x40, 0x4f, - 0x16, 0xde, 0x71, 0x4c, 0xd5, 0x1e, 0x43, 0x31, 0xe6, 0x0e, 0xfc, 0x82, 0xb3, 0xf2, 0x8a, 0x21, - 0x32, 0xaf, 0x14, 0x74, 0x56, 0x59, 0xd4, 0xb1, 0x95, 0x59, 0x87, 0xb5, 0x7f, 0x52, 0x65, 0x89, - 0xdb, 0xa7, 0x4b, 0x50, 0xe8, 0x11, 0x5f, 0x7b, 0x06, 0xcb, 0xbc, 0xde, 0xf5, 0xa9, 0x53, 0xe4, - 0x55, 0xf4, 0xbb, 0xf3, 0xd0, 0xcc, 0x53, 0x7b, 0x05, 0xa5, 0xf1, 0xbd, 0xee, 0xcc, 0x92, 0xe4, - 0x14, 0x7d, 0x73, 0x21, 0x25, 0xb7, 0x76, 0xa1, 0x72, 0xa9, 0xdc, 0x99, 0x81, 0x26, 0x59, 0xfa, - 0xfd, 0xab, 0xb0, 0xb2, 0x19, 0xdd, 0xdd, 0xf3, 0xa1, 0xa1, 0x5e, 0x0c, 0x0d, 0xf5, 0xe7, 0xd0, - 0x50, 0x4f, 0x47, 0x86, 0x72, 0x31, 0x32, 0x94, 0x6f, 0x23, 0x43, 0x79, 0xbd, 0x39, 0xf7, 0x65, - 0x3f, 0x16, 0xff, 0x06, 0xfc, 0x9d, 0x77, 0x8b, 0xfc, 0x37, 0xfd, 0xf0, 0x6f, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x8a, 0x36, 0x66, 0x7a, 0x92, 0x04, 0x00, 0x00, + // 535 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0x3b, 0x6f, 0xd3, 0x50, + 0x14, 0xb6, 0x93, 0x2a, 0x55, 0x4e, 0x23, 0x2a, 0x4c, 0x44, 0x13, 0x53, 0x39, 0xc5, 0x62, 0x48, + 0x11, 0xb5, 0x49, 0x91, 0x78, 0xa4, 0x13, 0xe9, 0x04, 0x52, 0x04, 0x4a, 0xc5, 0x00, 0x4b, 0xe5, + 0xc7, 0xc5, 0xb5, 0x8a, 0x7d, 0x2d, 0xdf, 0xeb, 0xaa, 0x5d, 0x99, 0x18, 0x3b, 0x20, 0xe6, 0xce, + 0x4c, 0x0c, 0xfc, 0x88, 0x8e, 0x15, 0x13, 0x13, 0xa0, 0x64, 0x80, 0x7f, 0x01, 0xba, 0x0f, 0x3b, + 0x29, 0xe4, 0xc1, 0x74, 0x2d, 0x7f, 0x8f, 0xf3, 0x9d, 0x73, 0xae, 0x0d, 0xeb, 0x1e, 0x26, 0x11, + 0x26, 0xb6, 0xeb, 0xc4, 0x87, 0xf6, 0x51, 0xc7, 0x45, 0xd4, 0xe9, 0xd8, 0xf4, 0xd8, 0x4a, 0x52, + 0x4c, 0xb1, 0x76, 0x4d, 0xa0, 0x16, 0x43, 0x2d, 0x89, 0xea, 0xf5, 0x00, 0x07, 0x98, 0xe3, 0x36, + 0x7b, 0x12, 0x54, 0xdd, 0x28, 0x8c, 0x08, 0x2a, 0x8c, 0x3c, 0x1c, 0xc6, 0xff, 0xe0, 0x13, 0x85, + 0xb8, 0xaf, 0xc0, 0x9b, 0x02, 0xdf, 0x17, 0xc6, 0xb2, 0xae, 0x80, 0xd6, 0xa4, 0x34, 0x22, 0x81, + 0x7d, 0xd4, 0x61, 0x87, 0x00, 0xcc, 0xdf, 0x2a, 0x2c, 0xf7, 0x49, 0xb0, 0x87, 0x62, 0x5f, 0xdb, + 0x81, 0xda, 0xeb, 0x14, 0x47, 0xfb, 0x8e, 0xef, 0xa7, 0x88, 0x90, 0x86, 0xba, 0xa1, 0xb6, 0xab, + 0xbd, 0xc6, 0x97, 0xcf, 0x5b, 0x75, 0x69, 0xf6, 0x58, 0x20, 0x7b, 0x34, 0x0d, 0xe3, 0x60, 0xb0, + 0xc2, 0xd8, 0xf2, 0x95, 0xf6, 0x00, 0x80, 0xe2, 0x42, 0x5a, 0x5a, 0x20, 0xad, 0x52, 0x9c, 0x0b, + 0x3d, 0xa8, 0x38, 0x11, 0xce, 0x62, 0xda, 0x28, 0x6f, 0x94, 0xdb, 0x2b, 0xdb, 0x4d, 0xab, 0x98, + 0x18, 0x41, 0xf9, 0xc4, 0xac, 0x5d, 0x1c, 0xc6, 0xbd, 0xbb, 0xe7, 0xdf, 0x5a, 0xca, 0xc7, 0xef, + 0xad, 0x76, 0x10, 0xd2, 0x83, 0xcc, 0xb5, 0x3c, 0x1c, 0xc9, 0x36, 0xe5, 0xb1, 0x45, 0xfc, 0x43, + 0x9b, 0x9e, 0x24, 0x88, 0x70, 0x01, 0x19, 0x48, 0xeb, 0x6e, 0xf3, 0xdd, 0x59, 0x4b, 0xf9, 0x75, + 0xd6, 0x52, 0xde, 0xfe, 0xfc, 0x74, 0xfb, 0x52, 0x97, 0xe6, 0x55, 0x58, 0x95, 0x03, 0x18, 0x20, + 0x92, 0xe0, 0x98, 0x20, 0xf3, 0x83, 0x0a, 0xb5, 0x3e, 0x09, 0xfa, 0xd9, 0x1b, 0x1a, 0xf2, 0xc9, + 0x3c, 0x84, 0x4a, 0x18, 0x27, 0x19, 0x65, 0x33, 0x61, 0x19, 0x75, 0x6b, 0xca, 0x56, 0xad, 0x27, + 0x8c, 0xd2, 0x5b, 0x62, 0x21, 0x07, 0x92, 0xaf, 0xed, 0xc0, 0x32, 0xce, 0x28, 0x97, 0x96, 0xb8, + 0xf4, 0xc6, 0x54, 0xe9, 0x33, 0xce, 0x91, 0xda, 0x5c, 0xd1, 0x5d, 0xcd, 0x13, 0x4b, 0x37, 0xf3, + 0x3a, 0xd4, 0x27, 0x73, 0x15, 0x81, 0xdf, 0xab, 0xbc, 0x89, 0x17, 0x89, 0xef, 0x50, 0xf4, 0xdc, + 0x49, 0x9d, 0x88, 0x68, 0xf7, 0xa1, 0xea, 0x64, 0xf4, 0x00, 0xa7, 0x21, 0x3d, 0x59, 0xb8, 0xca, + 0x31, 0x55, 0x7b, 0x04, 0x95, 0x84, 0x3b, 0xf0, 0x25, 0xce, 0x0a, 0x2c, 0x8a, 0xe4, 0xcd, 0x0a, + 0x41, 0xf7, 0x0a, 0xcb, 0x3a, 0xb6, 0x32, 0x9b, 0xb0, 0xf6, 0x57, 0xaa, 0x3c, 0xf1, 0xf6, 0x69, + 0x09, 0xca, 0x7d, 0x12, 0x68, 0x4f, 0x61, 0x89, 0x4f, 0x78, 0x7d, 0x6a, 0x15, 0xb9, 0x18, 0xfd, + 0xd6, 0x3c, 0x34, 0xf7, 0xd4, 0x5e, 0x42, 0x75, 0xbc, 0xb2, 0x9b, 0xb3, 0x24, 0x05, 0x45, 0xdf, + 0x5c, 0x48, 0x29, 0xac, 0x5d, 0xa8, 0x5d, 0x1a, 0xee, 0xcc, 0x40, 0x93, 0x2c, 0xfd, 0xce, 0xff, + 0xb0, 0xf2, 0x1a, 0xbd, 0xdd, 0xf3, 0xa1, 0xa1, 0x5e, 0x0c, 0x0d, 0xf5, 0xc7, 0xd0, 0x50, 0x4f, + 0x47, 0x86, 0x72, 0x31, 0x32, 0x94, 0xaf, 0x23, 0x43, 0x79, 0xb5, 0x39, 0xf7, 0xbe, 0x1f, 0x8b, + 0x1f, 0x02, 0xbf, 0xf6, 0x6e, 0x85, 0x7f, 0xd6, 0xf7, 0xfe, 0x04, 0x00, 0x00, 0xff, 0xff, 0x7c, + 0xb9, 0xe1, 0x24, 0x95, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -619,16 +621,20 @@ func (m *MsgMultiSend) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x12 } } - { - size, err := m.Input.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err + if len(m.Inputs) > 0 { + for iNdEx := len(m.Inputs) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Inputs[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa } - i -= size - i = encodeVarintTx(dAtA, i, uint64(size)) } - i-- - dAtA[i] = 0xa return len(dAtA) - i, nil } @@ -767,8 +773,12 @@ func (m *MsgMultiSend) Size() (n int) { } var l int _ = l - l = m.Input.Size() - n += 1 + l + sovTx(uint64(l)) + if len(m.Inputs) > 0 { + for _, e := range m.Inputs { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } if len(m.Outputs) > 0 { for _, e := range m.Outputs { l = e.Size() @@ -1046,7 +1056,7 @@ func (m *MsgMultiSend) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Input", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Inputs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1073,7 +1083,8 @@ func (m *MsgMultiSend) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if err := m.Input.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Inputs = append(m.Inputs, Input{}) + if err := m.Inputs[len(m.Inputs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex