From dfe3e7a8d728bbdbe48a7ad8f95e8dbbda01d610 Mon Sep 17 00:00:00 2001 From: Amaury <1293565+amaurym@users.noreply.github.com> Date: Fri, 30 Apr 2021 13:00:47 +0200 Subject: [PATCH] Remove ServiceMsgs from ADR-031 (#9139) * wip * wip * wip * wip on refactoring adr 031 type URLs * Fix msg_service_router * Fix gov client queries * Fix some modules tests * Remove all instances of "*.Msg/*" * Uncomment code * Remove commented code * // simulation.NewWeightedOperation( * Fix CopyTx * Fix more tests * Fix x/gov test * proto.MessageName->sdk.MsgName * Fix authz tests * Use MsgRoute in feegrant and staking/authz * Fix more tests * Fix sims? * Add norace tag * Add CL * rebuild rosetta api test data * Update ADR * Rename MsgRoute -> MsgTypeURL * Fix codec registration * Remove sdk.GetLegacySignBytes * Update types/tx_msg.go * Update x/authz/simulation/operations.go * Move LegacyMsg to legacytx * Update CHANGELOG.md Co-authored-by: Aaron Craelius * Remove NewAnyWithCustomTypeURL * Keep support for ServiceMsgs * Fix TxBody UnpackInterfaces * Fix test * Address review * Remove support for ServiceMsg typeURLs * Fix lint * Update changelog * Fix tests * Use sdk.MsgTypeURL everywhere * Fix tests * Fix rosetta, run make rosetta-data * Fix rosetta thanks to froydi * Address reviews * Fix test * Remove stray log * Update CL Co-authored-by: Aaron Craelius Co-authored-by: Alessio Treglia Co-authored-by: Aaron Craelius --- CHANGELOG.md | 12 +- baseapp/baseapp.go | 37 +++--- baseapp/msg_service_router.go | 44 +++++-- baseapp/msg_service_router_test.go | 4 +- client/tx/legacy_test.go | 12 +- codec/types/any.go | 10 +- codec/types/any_test.go | 6 +- codec/types/interface_registry.go | 11 -- codec/types/types_test.go | 62 --------- contrib/rosetta/configuration/transfer.ros | 2 +- contrib/rosetta/node/data.tar.gz | Bin 36436 -> 40794 bytes docs/architecture/adr-031-msg-service.md | 73 ++--------- server/grpc/reflection/v2alpha1/reflection.go | 6 +- server/grpc/server_test.go | 66 ++++++++-- server/rosetta/client_online.go | 3 +- server/rosetta/converter.go | 29 +---- testutil/testdata/tx.go | 9 +- types/codec.go | 5 - types/msgservice/msg_service.go | 13 +- types/msgservice/service_msg_client.go | 11 +- types/service_msg.go | 70 ---------- types/simulation/types.go | 19 ++- types/tx/types.go | 36 ++--- types/tx_msg.go | 18 +-- x/auth/client/testutil/suite.go | 24 +--- x/auth/legacy/legacytx/stdsign.go | 20 ++- x/auth/legacy/legacytx/stdtx_builder.go | 20 +-- x/auth/tx/builder.go | 7 +- x/auth/tx/service_test.go | 25 ++-- x/authz/client/cli/tx.go | 14 +- x/authz/client/rest/grpc_query_test.go | 14 +- x/authz/client/testutil/tx.go | 52 ++++---- x/authz/exported/authorizations.go | 2 +- x/authz/keeper/keeper.go | 23 ++-- x/authz/keeper/keeper_test.go | 43 +++--- x/authz/keeper/msg_server.go | 4 +- x/authz/simulation/operations.go | 27 ++-- x/authz/types/codec.go | 2 +- x/authz/types/generic_authorization.go | 7 +- x/authz/types/generic_authorization_test.go | 6 +- x/authz/types/msgs.go | 34 ++--- x/authz/types/msgs_test.go | 19 ++- x/bank/client/testutil/cli_helpers.go | 61 --------- x/bank/client/testutil/suite.go | 61 +-------- x/bank/types/send_authorization.go | 33 +++-- x/bank/types/send_authorization_test.go | 19 +-- x/evidence/types/msgs_test.go | 5 +- x/feegrant/client/cli/tx.go | 2 +- x/feegrant/client/testutil/suite.go | 3 +- x/feegrant/simulation/genesis.go | 2 +- x/feegrant/simulation/operations.go | 9 +- x/feegrant/types/codec.go | 2 +- x/feegrant/types/filtered_fee.go | 2 +- x/feegrant/types/msgs.go | 2 +- x/genutil/client/testutil/suite.go | 3 +- x/gov/client/utils/query.go | 123 ++++++------------ x/gov/client/utils/query_test.go | 3 +- x/gov/types/msgs.go | 6 - x/staking/client/rest/query.go | 8 +- x/staking/types/authz.go | 14 +- x/staking/types/authz_test.go | 65 +++------ x/staking/types/msg.go | 7 - 62 files changed, 428 insertions(+), 903 deletions(-) delete mode 100644 types/service_msg.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 92e9156574f3..ae290136e41b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [Unreleased] * [\#9205](https://github.com/cosmos/cosmos-sdk/pull/9205) Improve readability in `abci` handleQueryP2P -## Features +### Features + * [\#8965](https://github.com/cosmos/cosmos-sdk/pull/8965) cosmos reflection now provides more information on the application such as: deliverable msgs, sdk.Config info etc (still in alpha stage). * [\#8559](https://github.com/cosmos/cosmos-sdk/pull/8559) Added Protobuf compatible secp256r1 ECDSA signatures. * [\#8786](https://github.com/cosmos/cosmos-sdk/pull/8786) Enabled secp256r1 in x/auth. @@ -48,6 +49,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (x/staking) [\#9214](https://github.com/cosmos/cosmos-sdk/pull/9214) Added `new_shares` attribute inside `EventTypeDelegate` event. ### Client Breaking Changes + * [\#8363](https://github.com/cosmos/cosmos-sdk/pull/8363) Addresses no longer have a fixed 20-byte length. From the SDK modules' point of view, any 1-255 bytes-long byte array is a valid address. * [\#8346](https://github.com/cosmos/cosmos-sdk/pull/8346) All CLI `tx` commands generate ServiceMsgs by default. Graceful Amino support has been added to ServiceMsgs to support signing legacy Msgs. * (crypto/ed25519) [\#8690] Adopt zip1215 ed2559 verification rules. @@ -59,6 +61,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ * CLI: removed `--text` flag from `show-node-id` command; the text format for public keys is not used any more - instead we use ProtoJSON. * (types) [\#9079](https://github.com/cosmos/cosmos-sdk/issues/9079) Add `AddAmount`/`SubAmount` methods to `sdk.Coin`. * [\#8628](https://github.com/cosmos/cosmos-sdk/issues/8628) Commands no longer print outputs using `stderr` by default +* [\#9139](https://github.com/cosmos/cosmos-sdk/pull/9139) Querying events: + * via `ServiceMsg` TypeURLs (e.g. `message.action='/cosmos.bank.v1beta1.Msg/Send'`) does not work anymore, + * via legacy `msg.Type()` (e.g. `message.action='send'`) is being deprecated, new `Msg`s won't emit these events. + * Please use concrete `Msg` TypeURLs instead (e.g. `message.action='/cosmos.bank.v1beta1.MsgSend'`). ### API Breaking Changes @@ -92,6 +98,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ * `codec.JSONMarshaler` → `codec.JSONCodec` * Removed `BinaryBare` suffix from `BinaryCodec` methods (`MarshalBinaryBare`, `UnmarshalBinaryBare`, ...) * Removed `Binary` infix from `BinaryCodec` methods (`MarshalBinaryLengthPrefixed`, `UnmarshalBinaryLengthPrefixed`, ...) +* [\#9139](https://github.com/cosmos/cosmos-sdk/pull/9139) `ServiceMsg` TypeURLs (e.g. `/cosmos.bank.v1beta1.Msg/Send`) have been removed, as they don't comply to the Probobuf `Any` spec. Please use `Msg` type TypeURLs (e.g. `/cosmos.bank.v1beta1.MsgSend`). This has multiple consequences: + * The `sdk.ServiceMsg` struct has been removed. + * `sdk.Msg` now only contains `ValidateBasic` and `GetSigners` methods. The remaining methods `GetSignBytes`, `Route` and `Type` are moved to `legacytx.LegacyMsg`. + * The `RegisterCustomTypeURL` function and the `cosmos.base.v1beta1.ServiceMsg` interface have been removed from the interface registry. diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 5ec243ba91b8..6c66e6e008e3 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -19,6 +19,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/rootmulti" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" ) const ( @@ -703,37 +704,39 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (*s } var ( - msgEvents sdk.Events - msgResult *sdk.Result - msgFqName string - err error + msgResult *sdk.Result + eventMsgName string // name to use as value in event `message.action` + err error ) - if svcMsg, ok := msg.(sdk.ServiceMsg); ok { - msgFqName = svcMsg.MethodName - handler := app.msgServiceRouter.Handler(msgFqName) - if handler == nil { - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized message service method: %s; message index: %d", msgFqName, i) - } - msgResult, err = handler(ctx, svcMsg.Request) - } else { + if handler := app.msgServiceRouter.Handler(msg); handler != nil { + // ADR 031 request type routing + msgResult, err = handler(ctx, msg) + eventMsgName = sdk.MsgTypeURL(msg) + } else if legacyMsg, ok := msg.(legacytx.LegacyMsg); ok { // legacy sdk.Msg routing - msgRoute := msg.Route() - msgFqName = msg.Type() + // Assuming that the app developer has migrated all their Msgs to + // proto messages and has registered all `Msg services`, then this + // path should never be called, because all those Msgs should be + // registered within the `msgServiceRouter` already. + msgRoute := legacyMsg.Route() + eventMsgName = legacyMsg.Type() handler := app.router.Route(ctx, msgRoute) if handler == nil { return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized message route: %s; message index: %d", msgRoute, i) } msgResult, err = handler(ctx, msg) + } else { + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "can't route message %+v", msg) } if err != nil { return nil, sdkerrors.Wrapf(err, "failed to execute message; message index: %d", i) } - msgEvents = sdk.Events{ - sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyAction, msgFqName)), + msgEvents := sdk.Events{ + sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyAction, eventMsgName)), } msgEvents = msgEvents.AppendEvents(msgResult.GetEvents()) @@ -743,7 +746,7 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (*s // separate each result. events = events.AppendEvents(msgEvents) - txMsgData.Data = append(txMsgData.Data, &sdk.MsgData{MsgType: msg.Type(), Data: msgResult.Data}) + txMsgData.Data = append(txMsgData.Data, &sdk.MsgData{MsgType: sdk.MsgTypeURL(msg), Data: msgResult.Data}) msgLogs = append(msgLogs, sdk.NewABCIMessageLog(uint32(i), msgResult.Log, msgEvents)) } diff --git a/baseapp/msg_service_router.go b/baseapp/msg_service_router.go index ea2ed4b4eb6a..086bcbd267c5 100644 --- a/baseapp/msg_service_router.go +++ b/baseapp/msg_service_router.go @@ -29,12 +29,17 @@ func NewMsgServiceRouter() *MsgServiceRouter { } // MsgServiceHandler defines a function type which handles Msg service message. -type MsgServiceHandler = func(ctx sdk.Context, req sdk.MsgRequest) (*sdk.Result, error) +type MsgServiceHandler = func(ctx sdk.Context, req sdk.Msg) (*sdk.Result, error) -// Handler returns the MsgServiceHandler for a given query route path or nil +// Handler returns the MsgServiceHandler for a given msg or nil if not found. +func (msr *MsgServiceRouter) Handler(msg sdk.Msg) MsgServiceHandler { + return msr.routes[sdk.MsgTypeURL(msg)] +} + +// HandlerbyTypeURL returns the MsgServiceHandler for a given query route path or nil // if not found. -func (msr *MsgServiceRouter) Handler(methodName string) MsgServiceHandler { - return msr.routes[methodName] +func (msr *MsgServiceRouter) HandlerbyTypeURL(typeURL string) MsgServiceHandler { + return msr.routes[typeURL] } // RegisterService implements the gRPC Server.RegisterService method. sd is a gRPC @@ -50,20 +55,38 @@ func (msr *MsgServiceRouter) RegisterService(sd *grpc.ServiceDesc, handler inter fqMethod := fmt.Sprintf("/%s/%s", sd.ServiceName, method.MethodName) methodHandler := method.Handler + var requestTypeName string + + // NOTE: This is how we pull the concrete request type for each handler for registering in the InterfaceRegistry. + // This approach is maybe a bit hacky, but less hacky than reflecting on the handler object itself. + // We use a no-op interceptor to avoid actually calling into the handler itself. + _, _ = methodHandler(nil, context.Background(), func(i interface{}) error { + msg, ok := i.(sdk.Msg) + if !ok { + // We panic here because there is no other alternative and the app cannot be initialized correctly + // this should only happen if there is a problem with code generation in which case the app won't + // work correctly anyway. + panic(fmt.Errorf("can't register request type %T for service method %s", i, fqMethod)) + } + + requestTypeName = sdk.MsgTypeURL(msg) + return nil + }, noopInterceptor) + // Check that the service Msg fully-qualified method name has already // been registered (via RegisterInterfaces). If the user registers a // service without registering according service Msg type, there might be // some unexpected behavior down the road. Since we can't return an error // (`Server.RegisterService` interface restriction) we panic (at startup). - serviceMsg, err := msr.interfaceRegistry.Resolve(fqMethod) - if err != nil || serviceMsg == nil { + reqType, err := msr.interfaceRegistry.Resolve(requestTypeName) + if err != nil || reqType == nil { panic( fmt.Errorf( "type_url %s has not been registered yet. "+ "Before calling RegisterService, you must register all interfaces by calling the `RegisterInterfaces` "+ "method on module.BasicManager. Each module should call `msgservice.RegisterMsgServiceDesc` inside its "+ "`RegisterInterfaces` method with the `_Msg_serviceDesc` generated by proto-gen", - fqMethod, + requestTypeName, ), ) } @@ -72,7 +95,7 @@ func (msr *MsgServiceRouter) RegisterService(sd *grpc.ServiceDesc, handler inter // registered more than once, then we should error. Since we can't // return an error (`Server.RegisterService` interface restriction) we // panic (at startup). - _, found := msr.routes[fqMethod] + _, found := msr.routes[requestTypeName] if found { panic( fmt.Errorf( @@ -83,7 +106,7 @@ func (msr *MsgServiceRouter) RegisterService(sd *grpc.ServiceDesc, handler inter ) } - msr.routes[fqMethod] = func(ctx sdk.Context, req sdk.MsgRequest) (*sdk.Result, error) { + msr.routes[requestTypeName] = func(ctx sdk.Context, req sdk.Msg) (*sdk.Result, error) { ctx = ctx.WithEventManager(sdk.NewEventManager()) interceptor := func(goCtx context.Context, _ interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { goCtx = context.WithValue(goCtx, sdk.SdkContextKey, ctx) @@ -112,3 +135,6 @@ func (msr *MsgServiceRouter) SetInterfaceRegistry(interfaceRegistry codectypes.I } func noopDecoder(_ interface{}) error { return nil } +func noopInterceptor(_ context.Context, _ interface{}, _ *grpc.UnaryServerInfo, _ grpc.UnaryHandler) (interface{}, error) { + return nil, nil +} diff --git a/baseapp/msg_service_router_test.go b/baseapp/msg_service_router_test.go index 34f9c080277e..d599d0cbe47a 100644 --- a/baseapp/msg_service_router_test.go +++ b/baseapp/msg_service_router_test.go @@ -80,11 +80,11 @@ func TestMsgService(t *testing.T) { ) _ = app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: 1}}) - msg := testdata.NewServiceMsgCreateDog(&testdata.MsgCreateDog{Dog: &testdata.Dog{Name: "Spot"}}) + msg := testdata.MsgCreateDog{Dog: &testdata.Dog{Name: "Spot"}} txBuilder := encCfg.TxConfig.NewTxBuilder() txBuilder.SetFeeAmount(testdata.NewTestFeeAmount()) txBuilder.SetGasLimit(testdata.NewTestGasLimit()) - err := txBuilder.SetMsgs(msg) + err := txBuilder.SetMsgs(&msg) require.NoError(t, err) // First round: we gather all the signer infos. We use the "set empty diff --git a/client/tx/legacy_test.go b/client/tx/legacy_test.go index 5dcab771c495..e3ac1e630ed1 100644 --- a/client/tx/legacy_test.go +++ b/client/tx/legacy_test.go @@ -13,7 +13,6 @@ import ( "github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/testutil/testdata" "github.com/cosmos/cosmos-sdk/types" - sdk "github.com/cosmos/cosmos-sdk/types" signing2 "github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" "github.com/cosmos/cosmos-sdk/x/auth/signing" @@ -39,10 +38,7 @@ var ( }, } msg0 = banktypes.NewMsgSend(addr1, addr2, types.NewCoins(types.NewInt64Coin("wack", 1))) - msg1 = sdk.ServiceMsg{ - MethodName: "/cosmos.bank.v1beta1.Msg/Send", - Request: banktypes.NewMsgSend(addr1, addr2, types.NewCoins(types.NewInt64Coin("wack", 2))), - } + msg1 = banktypes.NewMsgSend(addr1, addr2, types.NewCoins(types.NewInt64Coin("wack", 2))) ) func buildTestTx(t *testing.T, builder client.TxBuilder) { @@ -88,7 +84,7 @@ func (s *TestSuite) TestCopyTx() { s.Require().Equal(sigsV2_1, sigsV2_2) s.Require().Equal(protoBuilder.GetTx().GetSigners(), protoBuilder2.GetTx().GetSigners()) s.Require().Equal(protoBuilder.GetTx().GetMsgs()[0], protoBuilder2.GetTx().GetMsgs()[0]) - s.Require().Equal(protoBuilder.GetTx().GetMsgs()[1].(sdk.ServiceMsg).Request, protoBuilder2.GetTx().GetMsgs()[1]) // We lose the "ServiceMsg" information + s.Require().Equal(protoBuilder.GetTx().GetMsgs()[1], protoBuilder2.GetTx().GetMsgs()[1]) // amino -> proto -> amino aminoBuilder = s.aminoCfg.NewTxBuilder() @@ -120,7 +116,7 @@ func (s *TestSuite) TestConvertTxToStdTx() { s.Require().Equal(gas, stdTx.Fee.Gas) s.Require().Equal(fee, stdTx.Fee.Amount) s.Require().Equal(msg0, stdTx.Msgs[0]) - s.Require().Equal(msg1.Request, stdTx.Msgs[1]) + s.Require().Equal(msg1, stdTx.Msgs[1]) s.Require().Equal(timeoutHeight, stdTx.TimeoutHeight) s.Require().Equal(sig.PubKey, stdTx.Signatures[0].PubKey) s.Require().Equal(sig.Data.(*signing2.SingleSignatureData).Signature, stdTx.Signatures[0].Signature) @@ -140,7 +136,7 @@ func (s *TestSuite) TestConvertTxToStdTx() { s.Require().Equal(gas, stdTx.Fee.Gas) s.Require().Equal(fee, stdTx.Fee.Amount) s.Require().Equal(msg0, stdTx.Msgs[0]) - s.Require().Equal(msg1.Request, stdTx.Msgs[1]) + s.Require().Equal(msg1, stdTx.Msgs[1]) s.Require().Equal(timeoutHeight, stdTx.TimeoutHeight) s.Require().Empty(stdTx.Signatures) diff --git a/codec/types/any.go b/codec/types/any.go index 14087efa8fd6..07c8de98c81e 100644 --- a/codec/types/any.go +++ b/codec/types/any.go @@ -64,20 +64,14 @@ func NewAnyWithValue(v proto.Message) (*Any, error) { if v == nil { return nil, sdkerrors.Wrap(sdkerrors.ErrPackAny, "Expecting non nil value to create a new Any") } - return NewAnyWithCustomTypeURL(v, "/"+proto.MessageName(v)) -} -// NewAnyWithCustomTypeURL same as NewAnyWithValue, but sets a custom type url, instead -// using the one from proto.Message. -// NOTE: This functions should be only used for types with additional logic bundled -// into the protobuf Any serialization. For simple marshaling you should use NewAnyWithValue. -func NewAnyWithCustomTypeURL(v proto.Message, typeURL string) (*Any, error) { bz, err := proto.Marshal(v) if err != nil { return nil, err } + return &Any{ - TypeUrl: typeURL, + TypeUrl: "/" + proto.MessageName(v), Value: bz, cachedValue: v, }, nil diff --git a/codec/types/any_test.go b/codec/types/any_test.go index cea3e0444efb..b9ddbe72ec39 100644 --- a/codec/types/any_test.go +++ b/codec/types/any_test.go @@ -23,8 +23,6 @@ func (eom *errOnMarshal) XXX_Marshal(b []byte, deterministic bool) ([]byte, erro return nil, errAlways } -const fauxURL = "/anyhere" - var eom = &errOnMarshal{} // Ensure that returning an error doesn't suddenly allocate and waste bytes. @@ -32,7 +30,7 @@ var eom = &errOnMarshal{} func TestNewAnyWithCustomTypeURLWithErrorNoAllocation(t *testing.T) { var ms1, ms2 runtime.MemStats runtime.ReadMemStats(&ms1) - any, err := types.NewAnyWithCustomTypeURL(eom, fauxURL) + any, err := types.NewAnyWithValue(eom) runtime.ReadMemStats(&ms2) // Ensure that no fresh allocation was made. if diff := ms2.HeapAlloc - ms1.HeapAlloc; diff > 0 { @@ -52,7 +50,7 @@ func BenchmarkNewAnyWithCustomTypeURLWithErrorReturned(b *testing.B) { b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { - any, err := types.NewAnyWithCustomTypeURL(eom, fauxURL) + any, err := types.NewAnyWithValue(eom) if err == nil { b.Fatal("err wasn't returned") } diff --git a/codec/types/interface_registry.go b/codec/types/interface_registry.go index 0f9eb760beca..5d7e72e890c0 100644 --- a/codec/types/interface_registry.go +++ b/codec/types/interface_registry.go @@ -46,17 +46,6 @@ type InterfaceRegistry interface { // registry.RegisterImplementations((*sdk.Msg)(nil), &MsgSend{}, &MsgMultiSend{}) RegisterImplementations(iface interface{}, impls ...proto.Message) - // RegisterCustomTypeURL allows a protobuf message to be registered as a - // google.protobuf.Any with a custom typeURL (besides its own canonical - // typeURL). iface should be an interface as type, as in RegisterInterface - // and RegisterImplementations. - // - // Ex: - // This will allow us to pack service methods in Any's using the full method name - // as the type URL and the request body as the value, and allow us to unpack - // such packed methods using the normal UnpackAny method for the interface iface. - RegisterCustomTypeURL(iface interface{}, typeURL string, impl proto.Message) - // ListAllInterfaces list the type URLs of all registered interfaces. ListAllInterfaces() []string diff --git a/codec/types/types_test.go b/codec/types/types_test.go index 43f85375508e..24c83b4eb5f9 100644 --- a/codec/types/types_test.go +++ b/codec/types/types_test.go @@ -1,18 +1,13 @@ package types_test import ( - "context" - "fmt" "strings" "testing" - "github.com/gogo/protobuf/grpc" "github.com/gogo/protobuf/jsonpb" "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/require" - grpc2 "google.golang.org/grpc" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/testutil/testdata" ) @@ -185,60 +180,3 @@ func TestAny_ProtoJSON(t *testing.T) { require.NoError(t, err) require.Equal(t, spot, ha2.Animal.GetCachedValue()) } - -// this instance of grpc.ClientConn is used to test packing service method -// requests into Any's -type testAnyPackClient struct { - any types.Any - interfaceRegistry types.InterfaceRegistry -} - -var _ grpc.ClientConn = &testAnyPackClient{} - -func (t *testAnyPackClient) Invoke(_ context.Context, method string, args, _ interface{}, _ ...grpc2.CallOption) error { - reqMsg, ok := args.(proto.Message) - if !ok { - return fmt.Errorf("can't proto marshal %T", args) - } - - // registry the method request type with the interface registry - t.interfaceRegistry.RegisterCustomTypeURL((*interface{})(nil), method, reqMsg) - - bz, err := proto.Marshal(reqMsg) - if err != nil { - return err - } - - t.any.TypeUrl = method - t.any.Value = bz - - return nil -} - -func (t *testAnyPackClient) NewStream(context.Context, *grpc2.StreamDesc, string, ...grpc2.CallOption) (grpc2.ClientStream, error) { - return nil, fmt.Errorf("not supported") -} - -func TestAny_ServiceRequestProtoJSON(t *testing.T) { - interfaceRegistry := types.NewInterfaceRegistry() - anyPacker := &testAnyPackClient{interfaceRegistry: interfaceRegistry} - dogMsgClient := testdata.NewMsgClient(anyPacker) - _, err := dogMsgClient.CreateDog(context.Background(), &testdata.MsgCreateDog{Dog: &testdata.Dog{ - Name: "spot", - }}) - require.NoError(t, err) - - // marshal JSON - cdc := codec.NewProtoCodec(interfaceRegistry) - bz, err := cdc.MarshalJSON(&anyPacker.any) - require.NoError(t, err) - require.Equal(t, - `{"@type":"/testdata.Msg/CreateDog","dog":{"size":"","name":"spot"}}`, - string(bz)) - - // unmarshal JSON - var any2 types.Any - err = cdc.UnmarshalJSON(bz, &any2) - require.NoError(t, err) - require.Equal(t, anyPacker.any, any2) -} diff --git a/contrib/rosetta/configuration/transfer.ros b/contrib/rosetta/configuration/transfer.ros index 74ebd2ddf50c..201a8ab7c449 100644 --- a/contrib/rosetta/configuration/transfer.ros +++ b/contrib/rosetta/configuration/transfer.ros @@ -83,7 +83,7 @@ transfer(3){ transfer.operations = [ { "operation_identifier":{"index":0}, - "type":"cosmos.bank.v1beta1.MsgSend", + "type":"/cosmos.bank.v1beta1.MsgSend", "account":{{sender.account_identifier}}, "metadata": { "amount": [ diff --git a/contrib/rosetta/node/data.tar.gz b/contrib/rosetta/node/data.tar.gz index d96715778251d8388d761d2bf3623bc96c99a8f3..3c37adcc162f05b37a08fd082a7c2c0ecb1919c4 100644 GIT binary patch literal 40794 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
$s&_OSd&EV-5n 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�+4S2|T{ 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 E0Ag?6Qvd(} diff --git a/docs/architecture/adr-031-msg-service.md b/docs/architecture/adr-031-msg-service.md index a36d86c4312f..a4ff9cb54ac9 100644 --- a/docs/architecture/adr-031-msg-service.md +++ b/docs/architecture/adr-031-msg-service.md @@ -3,6 +3,7 @@ ## Changelog - 2020-10-05: Initial Draft +- 2021-04-21: Remove `ServiceMsg`s to follow Protobuf `Any`'s spec, see [#9063](https://github.com/cosmos/cosmos-sdk/issues/9063). ## Status @@ -96,71 +97,28 @@ On the client side, developers could take advantage of this by creating RPC impl logic. Protobuf libraries that use asynchronous callbacks, like [protobuf.js](https://github.com/protobufjs/protobuf.js#using-services) could use this to register callbacks for specific messages even for transactions that include multiple `Msg`s. -For backwards compatibility, existing `Msg` types should be used as the request parameter -for `service` definitions. Newer `Msg` types which only support `service` definitions -should use the more canonical `Msg...Request` names. +Each `Msg` service method should have exactly one request parameter: its corresponding `Msg` type. For example, the `Msg` service method `/cosmos.gov.v1beta1.Msg/SubmitProposal` above has exactly one request parameter, namely the `Msg` type `/cosmos.gov.v1beta1.MsgSubmitProposal`. It is important the reader understands clearly the nomenclature difference between a `Msg` service (a Protobuf service) and a `Msg` type (a Protobuf message), and the differences in their fully-qualified name. -### Encoding - -Currently, we are encoding `Msg`s as `Any` in `Tx`s which involves packing the -binary-encoded `Msg` with its type URL. +This convention has been decided over the more canonical `Msg...Request` names mainly for backwards compatibility, but also for better readability in `TxBody.messages` (see [Encoding section](#encoding) below): transactions containing `/cosmos.gov.MsgSubmitProposal` read better than those containing `/cosmos.gov.v1beta1.MsgSubmitProposalRequest`. -The type URL for `MsgSubmitProposal` based on the proto3 spec is `/cosmos.gov.MsgSubmitProposal`. +One consequence of this convention is that each `Msg` type can be the request parameter of only one `Msg` service method. However, we consider this limitation a good practice in explicitness. -The fully-qualified name for the `SubmitProposal` service method above (also -based on the proto3 and gRPC specs) is `/cosmos.gov.Msg/SubmitProposal` which varies -by a single `/` character. The generated `.pb.go` files for protobuf `service`s -include names of this form and any compliant protobuf/gRPC code generator will -generate the same name. +### Encoding -In order to encode service methods in transactions, we encode them as `Any`s in -the same `TxBody.messages` field as other `Msg`s. We simply set `Any.type_url` -to the full-qualified method name (ex. `/cosmos.gov.Msg/SubmitProposal`) and -set `Any.value` to the protobuf encoding of the request message -(`MsgSubmitProposal` in this case). +Encoding of transactions generated with `Msg` services do not differ from current Protobuf transaction encoding as defined in [ADR-020](./adr-020-protobuf-transaction-encoding.md). We are encoding `Msg` types (which are exactly `Msg` service methods' request parameters) as `Any` in `Tx`s which involves packing the +binary-encoded `Msg` with its type URL. ### Decoding -When decoding, `TxBody.UnpackInterfaces` will need a special case -to detect if `Any` type URLs match the service method format (ex. `/cosmos.gov.Msg/SubmitProposal`) -by checking for two `/` characters. Messages that are method names plus request parameters -instead of a normal `Any` messages will get unpacked into the `ServiceMsg` struct: - -```go -type ServiceMsg struct { - // MethodName is the fully-qualified service name - MethodName string - // Request is the request payload - Request MsgRequest -} -``` +Since `Msg` types are packed into `Any`, decoding transactions messages are done by unpacking `Any`s into `Msg` types. For more information, please refer to [ADR-020](./adr-020-protobuf-transaction-encoding.md#transactions). ### Routing -In the future, `service` definitions may become the primary method for defining -`Msg`s. As a starting point, we need to integrate with the SDK's existing routing -and `Msg` interface. - -To do this, `ServiceMsg` implements the `sdk.Msg` interface and its handler does the -actual method routing, allowing this feature to be added incrementally on top of -existing functionality. - -### `MsgRequest` interface - -All request messages will need to implement the `MsgRequest` interface which is a -simplified version of `Msg`, without `Route()`, `Type()` and `GetSignBytes()` which -are no longer needed: +We propose to add a `msg_service_router` in BaseApp. This router is a key/value map which maps `Msg` types' `type_url`s to their corresponding `Msg` service method handler. Since there is a 1-to-1 mapping between `Msg` types and `Msg` service method, the `msg_service_router` has exactly one entry per `Msg` service method. -```go -type MsgRequest interface { - proto.Message - ValidateBasic() error - GetSigners() []AccAddress -} -``` +When a transaction is processed by BaseApp (in CheckTx or in DeliverTx), its `TxBody.messages` are decoded as `Msg`s. Each `Msg`'s `type_url` is matched against an entry in the `msg_service_router`, and the respective `Msg` service method handler is called. -`ServiceMsg` will forward its `ValidateBasic` and `GetSigners` methods to the `MsgRequest` -methods. +For backward compatability, the old handlers are not removed yet. If BaseApp receives a legacy `Msg` with no correspoding entry in the `msg_service_router`, it will be routed via its legacy `Route()` method into the legacy handler. ### Module Configuration @@ -192,8 +150,8 @@ The `RegisterServices` method and the `Configurator` interface are intended to evolve to satisfy the use cases discussed in [\#7093](https://github.com/cosmos/cosmos-sdk/issues/7093) and [\#7122](https://github.com/cosmos/cosmos-sdk/issues/7421). -When `Msg` services are registered, the framework _should_ verify that all `Msg...Request` types -implement the `MsgRequest` interface described above and throw an error during initialization rather +When `Msg` services are registered, the framework _should_ verify that all `Msg` types +implement the `sdk.Msg` interface and throw an error during initialization rather than later when transactions are processed. ### `Msg` Service Implementation @@ -211,8 +169,7 @@ func (k Keeper) SubmitProposal(goCtx context.Context, params *types.MsgSubmitPro } ``` -The `sdk.Context` should have an `EventManager` already attached by the `ServiceMsg` -router. +The `sdk.Context` should have an `EventManager` already attached by BaseApp's `msg_service_router`. Separate handler definition is no longer needed with this approach. @@ -232,8 +189,6 @@ Finally, closing a module to client API opens desirable OCAP patterns discussed - dramatically reduces and simplifies the code ### Cons -- supporting both this and the current concrete `Msg` type approach simultaneously could be confusing -(we could choose to deprecate the current approach) - using `service` definitions outside the context of gRPC could be confusing (but doesn’t violate the proto3 spec) diff --git a/server/grpc/reflection/v2alpha1/reflection.go b/server/grpc/reflection/v2alpha1/reflection.go index 728d54ad4bbc..e18295c90b33 100644 --- a/server/grpc/reflection/v2alpha1/reflection.go +++ b/server/grpc/reflection/v2alpha1/reflection.go @@ -169,12 +169,12 @@ func newTxDescriptor(ir codectypes.InterfaceRegistry) (*TxDescriptor, error) { return nil, fmt.Errorf("unable to get *tx.Tx protobuf name") } // get msgs - svcMsgImplementers := ir.ListImplementations(sdk.ServiceMsgInterfaceProtoName) + sdkMsgImplementers := ir.ListImplementations(sdk.MsgInterfaceProtoName) - msgsDesc := make([]*MsgDescriptor, 0, len(svcMsgImplementers)) + msgsDesc := make([]*MsgDescriptor, 0, len(sdkMsgImplementers)) // process sdk.ServiceMsg - for _, svcMsg := range svcMsgImplementers { + for _, svcMsg := range sdkMsgImplementers { resolved, err := ir.Resolve(svcMsg) if err != nil { return nil, fmt.Errorf("unable to resolve sdk.ServiceMsg %s: %w", svcMsg, err) diff --git a/server/grpc/server_test.go b/server/grpc/server_test.go index c7bd93982b18..e3bd71fe4feb 100644 --- a/server/grpc/server_test.go +++ b/server/grpc/server_test.go @@ -16,7 +16,9 @@ import ( "google.golang.org/grpc/metadata" rpb "google.golang.org/grpc/reflection/grpc_reflection_v1alpha" + "github.com/cosmos/cosmos-sdk/client" 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/testutil/network" "github.com/cosmos/cosmos-sdk/testutil/testdata" @@ -24,7 +26,8 @@ import ( grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" "github.com/cosmos/cosmos-sdk/types/tx" txtypes "github.com/cosmos/cosmos-sdk/types/tx" - banktestutil "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authclient "github.com/cosmos/cosmos-sdk/x/auth/client" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) @@ -95,6 +98,7 @@ func (s *IntegrationTestSuite) TestGRPCServer_BankBalance() { &banktypes.QueryBalanceRequest{Address: val0.Address.String(), Denom: denom}, grpc.Header(&header), ) + s.Require().NoError(err) blockHeight = header.Get(grpctypes.GRPCBlockHeightHeader) s.Require().NotEmpty(blockHeight[0]) // blockHeight is []string, first element is block height. } @@ -161,9 +165,20 @@ func (s *IntegrationTestSuite) TestGRPCServer_GetTxsEvent() { func (s *IntegrationTestSuite) TestGRPCServer_BroadcastTx() { val0 := s.network.Validators[0] - grpcRes, err := banktestutil.LegacyGRPCProtoMsgSend(val0.ClientCtx, - val0.Moniker, val0.Address, val0.Address, - sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}, sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}, + txBuilder := s.mkTxBuilder() + + txBytes, err := val0.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) + s.Require().NoError(err) + + // Broadcast the tx via gRPC. + queryClient := txtypes.NewServiceClient(s.conn) + + grpcRes, err := queryClient.BroadcastTx( + context.Background(), + &txtypes.BroadcastTxRequest{ + Mode: txtypes.BroadcastMode_BROADCAST_MODE_SYNC, + TxBytes: txBytes, + }, ) s.Require().NoError(err) s.Require().Equal(uint32(0), grpcRes.TxResponse.Code) @@ -174,7 +189,6 @@ func (s *IntegrationTestSuite) TestGRPCServer_BroadcastTx() { // See issue https://github.com/cosmos/cosmos-sdk/issues/7662. func (s *IntegrationTestSuite) TestGRPCServerInvalidHeaderHeights() { t := s.T() - val0 := s.network.Validators[0] // We should reject connections with invalid block heights off the bat. invalidHeightStrs := []struct { @@ -189,13 +203,7 @@ func (s *IntegrationTestSuite) TestGRPCServerInvalidHeaderHeights() { } for _, tt := range invalidHeightStrs { t.Run(tt.value, func(t *testing.T) { - conn, err := grpc.Dial( - val0.AppConfig.GRPC.Address, - grpc.WithInsecure(), // Or else we get "no transport security set" - ) - defer conn.Close() - - testClient := testdata.NewQueryClient(conn) + testClient := testdata.NewQueryClient(s.conn) ctx := metadata.AppendToOutgoingContext(context.Background(), grpctypes.GRPCBlockHeightHeader, tt.value) testRes, err := testClient.Echo(ctx, &testdata.EchoRequest{Message: "hello"}) require.Error(t, err) @@ -205,6 +213,40 @@ func (s *IntegrationTestSuite) TestGRPCServerInvalidHeaderHeights() { } } +// mkTxBuilder creates a TxBuilder containing a signed tx from validator 0. +func (s IntegrationTestSuite) mkTxBuilder() client.TxBuilder { + val := s.network.Validators[0] + s.Require().NoError(s.network.WaitForNextBlock()) + + // prepare txBuilder with msg + txBuilder := val.ClientCtx.TxConfig.NewTxBuilder() + feeAmount := sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)} + gasLimit := testdata.NewTestGasLimit() + s.Require().NoError( + txBuilder.SetMsgs(&banktypes.MsgSend{ + FromAddress: val.Address.String(), + ToAddress: val.Address.String(), + Amount: sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}, + }), + ) + txBuilder.SetFeeAmount(feeAmount) + txBuilder.SetGasLimit(gasLimit) + txBuilder.SetMemo("foobar") + + // setup txFactory + txFactory := clienttx.Factory{}. + WithChainID(val.ClientCtx.ChainID). + WithKeybase(val.ClientCtx.Keyring). + WithTxConfig(val.ClientCtx.TxConfig). + WithSignMode(signing.SignMode_SIGN_MODE_DIRECT) + + // Sign Tx. + err := authclient.SignTx(txFactory, val.ClientCtx, val.Moniker, txBuilder, false, true) + s.Require().NoError(err) + + return txBuilder +} + func TestIntegrationTestSuite(t *testing.T) { suite.Run(t, new(IntegrationTestSuite)) } diff --git a/server/rosetta/client_online.go b/server/rosetta/client_online.go index bd122b4069d7..ac3db862222e 100644 --- a/server/rosetta/client_online.go +++ b/server/rosetta/client_online.go @@ -6,7 +6,6 @@ import ( "encoding/hex" "fmt" "strconv" - "strings" "time" "github.com/cosmos/cosmos-sdk/version" @@ -71,7 +70,7 @@ func NewClient(cfg *Config) (*Client, error) { } if _, ok := resolvedMsg.(sdk.Msg); ok { - supportedOperations = append(supportedOperations, strings.TrimLeft(ii, "/")) + supportedOperations = append(supportedOperations, ii) } } diff --git a/server/rosetta/converter.go b/server/rosetta/converter.go index e9e5db559e64..7d2609a67387 100644 --- a/server/rosetta/converter.go +++ b/server/rosetta/converter.go @@ -18,7 +18,6 @@ import ( "github.com/cosmos/cosmos-sdk/types/tx/signing" rosettatypes "github.com/coinbase/rosetta-sdk-go/types" - "github.com/gogo/protobuf/proto" crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors" abci "github.com/tendermint/tendermint/abci/types" tmtypes "github.com/tendermint/tendermint/types" @@ -147,7 +146,7 @@ func (c converter) UnsignedTx(ops []*rosettatypes.Operation) (tx authsigning.Tx, for i := 0; i < len(ops); i++ { op := ops[i] - protoMessage, err := c.ir.Resolve("/" + op.Type) + protoMessage, err := c.ir.Resolve(op.Type) if err != nil { return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, "operation not found: "+op.Type) } @@ -241,31 +240,7 @@ func (c converter) Meta(msg sdk.Msg) (meta map[string]interface{}, err error) { // with the message proto name as type, and the raw fields // as metadata func (c converter) Ops(status string, msg sdk.Msg) ([]*rosettatypes.Operation, error) { - opName := proto.MessageName(msg) - // in case proto does not recognize the message name - // then we should try to cast it to service msg, to - // check if it was wrapped or not, in case the cast - // from sdk.ServiceMsg to sdk.Msg fails, then a - // codec error is returned - if opName == "" { - unwrappedMsg, ok := msg.(sdk.ServiceMsg) - if !ok { - return nil, crgerrs.WrapError(crgerrs.ErrCodec, fmt.Sprintf("unrecognized message type: %T", msg)) - } - - msg, ok = unwrappedMsg.Request.(sdk.Msg) - if !ok { - return nil, crgerrs.WrapError( - crgerrs.ErrCodec, - fmt.Sprintf("unable to cast %T to sdk.Msg, method: %s", unwrappedMsg.Request, unwrappedMsg.MethodName), - ) - } - - opName = proto.MessageName(msg) - if opName == "" { - return nil, crgerrs.WrapError(crgerrs.ErrCodec, fmt.Sprintf("unrecognized message type: %T", msg)) - } - } + opName := sdk.MsgTypeURL(msg) meta, err := c.Meta(msg) if err != nil { diff --git a/testutil/testdata/tx.go b/testutil/testdata/tx.go index 4da80c9e4e8f..653400ffb67c 100644 --- a/testutil/testdata/tx.go +++ b/testutil/testdata/tx.go @@ -77,14 +77,7 @@ func (msg *TestMsg) GetSigners() []sdk.AccAddress { } func (msg *TestMsg) ValidateBasic() error { return nil } -var _ sdk.MsgRequest = &MsgCreateDog{} +var _ sdk.Msg = &MsgCreateDog{} func (msg *MsgCreateDog) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{} } func (msg *MsgCreateDog) ValidateBasic() error { return nil } - -func NewServiceMsgCreateDog(msg *MsgCreateDog) sdk.Msg { - return sdk.ServiceMsg{ - MethodName: "/testdata.Msg/CreateDog", - Request: msg, - } -} diff --git a/types/codec.go b/types/codec.go index 8123fc7d51a4..69202d488fc6 100644 --- a/types/codec.go +++ b/types/codec.go @@ -8,8 +8,6 @@ import ( const ( // MsgInterfaceProtoName defines the protobuf name of the cosmos Msg interface MsgInterfaceProtoName = "cosmos.base.v1beta1.Msg" - // ServiceMsgInterfaceProtoName defines the protobuf name of the cosmos MsgRequest interface - ServiceMsgInterfaceProtoName = "cosmos.base.v1beta1.ServiceMsg" ) // RegisterLegacyAminoCodec registers the sdk message type. @@ -21,7 +19,4 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { // RegisterInterfaces registers the sdk message type. func RegisterInterfaces(registry types.InterfaceRegistry) { registry.RegisterInterface(MsgInterfaceProtoName, (*Msg)(nil)) - // the interface name for MsgRequest is ServiceMsg because this is most useful for clients - // to understand - it will be the way for clients to introspect on available Msg service methods - registry.RegisterInterface(ServiceMsgInterfaceProtoName, (*MsgRequest)(nil)) } diff --git a/types/msgservice/msg_service.go b/types/msgservice/msg_service.go index ddffed943e8a..382913590cad 100644 --- a/types/msgservice/msg_service.go +++ b/types/msgservice/msg_service.go @@ -3,9 +3,7 @@ package msgservice import ( "context" "fmt" - "strings" - "github.com/gogo/protobuf/proto" "google.golang.org/grpc" codectypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -24,7 +22,7 @@ func RegisterMsgServiceDesc(registry codectypes.InterfaceRegistry, sd *grpc.Serv // This approach is maybe a bit hacky, but less hacky than reflecting on the handler object itself. // We use a no-op interceptor to avoid actually calling into the handler itself. _, _ = methodHandler(nil, context.Background(), func(i interface{}) error { - msg, ok := i.(proto.Message) + msg, ok := i.(sdk.Msg) if !ok { // We panic here because there is no other alternative and the app cannot be initialized correctly // this should only happen if there is a problem with code generation in which case the app won't @@ -32,7 +30,8 @@ func RegisterMsgServiceDesc(registry codectypes.InterfaceRegistry, sd *grpc.Serv panic(fmt.Errorf("can't register request type %T for service method %s", i, fqMethod)) } - registry.RegisterCustomTypeURL((*sdk.MsgRequest)(nil), fqMethod, msg) + registry.RegisterImplementations((*sdk.Msg)(nil), msg) + return nil }, noopInterceptor) @@ -43,9 +42,3 @@ func RegisterMsgServiceDesc(registry codectypes.InterfaceRegistry, sd *grpc.Serv func noopInterceptor(_ context.Context, _ interface{}, _ *grpc.UnaryServerInfo, _ grpc.UnaryHandler) (interface{}, error) { return nil, nil } - -// IsServiceMsg checks if a type URL corresponds to a service method name, -// i.e. /cosmos.bank.Msg/Send vs /cosmos.bank.MsgSend -func IsServiceMsg(typeURL string) bool { - return strings.Count(typeURL, "/") >= 2 -} diff --git a/types/msgservice/service_msg_client.go b/types/msgservice/service_msg_client.go index de3be89094f8..b77997dba7fd 100644 --- a/types/msgservice/service_msg_client.go +++ b/types/msgservice/service_msg_client.go @@ -20,10 +20,10 @@ type ServiceMsgClientConn struct { } // Invoke implements the grpc ClientConn.Invoke method -func (t *ServiceMsgClientConn) Invoke(_ context.Context, method string, args, _ interface{}, _ ...grpc.CallOption) error { - req, ok := args.(sdk.MsgRequest) +func (t *ServiceMsgClientConn) Invoke(_ context.Context, _ string, args, _ interface{}, _ ...grpc.CallOption) error { + req, ok := args.(sdk.Msg) if !ok { - return fmt.Errorf("%T should implement %T", args, (*sdk.MsgRequest)(nil)) + return fmt.Errorf("%T should implement %T", args, (*sdk.Msg)(nil)) } err := req.ValidateBasic() @@ -31,10 +31,7 @@ func (t *ServiceMsgClientConn) Invoke(_ context.Context, method string, args, _ return err } - t.msgs = append(t.msgs, sdk.ServiceMsg{ - MethodName: method, - Request: req, - }) + t.msgs = append(t.msgs, req) return nil } diff --git a/types/service_msg.go b/types/service_msg.go deleted file mode 100644 index ee6cede2eb16..000000000000 --- a/types/service_msg.go +++ /dev/null @@ -1,70 +0,0 @@ -package types - -import ( - "fmt" - - "github.com/gogo/protobuf/proto" -) - -// MsgRequest is the interface a transaction message, defined as a proto -// service method, must fulfill. -type MsgRequest interface { - proto.Message - // ValidateBasic does a simple validation check that - // doesn't require access to any other information. - ValidateBasic() error - // Signers returns the addrs of signers that must sign. - // CONTRACT: All signatures must be present to be valid. - // CONTRACT: Returns addrs in some deterministic order. - GetSigners() []AccAddress -} - -// ServiceMsg is the struct into which an Any whose typeUrl matches a service -// method format (ex. `/cosmos.gov.v1beta1.Msg/SubmitProposal`) unpacks. -type ServiceMsg struct { - // MethodName is the fully-qualified service method name. - MethodName string - // Request is the request payload. - Request MsgRequest -} - -var _ Msg = ServiceMsg{} - -func (msg ServiceMsg) ProtoMessage() {} -func (msg ServiceMsg) Reset() {} -func (msg ServiceMsg) String() string { return "ServiceMsg" } - -// Route implements Msg.Route method. -func (msg ServiceMsg) Route() string { - return msg.MethodName -} - -// ValidateBasic implements Msg.ValidateBasic method. -func (msg ServiceMsg) ValidateBasic() error { - return msg.Request.ValidateBasic() -} - -// GetSignBytes implements Msg.GetSignBytes method. -func (msg ServiceMsg) GetSignBytes() []byte { - // Here, we're gracefully supporting Amino JSON for service - // Msgs. - // ref: https://github.com/cosmos/cosmos-sdk/issues/8346 - // If `msg` is a service Msg, then we cast its `Request` to a sdk.Msg - // and call GetSignBytes on the `Request`. - msgRequest, ok := msg.Request.(Msg) - if !ok { - panic(fmt.Errorf("cannot convert ServiceMsg request to sdk.Msg, got %T", msgRequest)) - } - - return msgRequest.GetSignBytes() -} - -// GetSigners implements Msg.GetSigners method. -func (msg ServiceMsg) GetSigners() []AccAddress { - return msg.Request.GetSigners() -} - -// Type implements Msg.Type method. -func (msg ServiceMsg) Type() string { - return msg.MethodName -} diff --git a/types/simulation/types.go b/types/simulation/types.go index e2a77c11baf3..89e5c813772a 100644 --- a/types/simulation/types.go +++ b/types/simulation/types.go @@ -2,14 +2,13 @@ package simulation import ( "encoding/json" - "fmt" "math/rand" - "reflect" "time" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" ) type WeightedProposalContent interface { @@ -78,16 +77,14 @@ func NewOperationMsgBasic(route, name, comment string, ok bool, msg []byte) Oper // NewOperationMsg - create a new operation message from sdk.Msg func NewOperationMsg(msg sdk.Msg, ok bool, comment string, cdc *codec.ProtoCodec) OperationMsg { - if reflect.TypeOf(msg) == reflect.TypeOf(sdk.ServiceMsg{}) { - srvMsg, ok := msg.(sdk.ServiceMsg) - if !ok { - panic(fmt.Sprintf("Expecting %T to implement sdk.ServiceMsg", msg)) - } - bz := cdc.MustMarshalJSON(srvMsg.Request) - - return NewOperationMsgBasic(srvMsg.MethodName, srvMsg.MethodName, comment, ok, bz) + if legacyMsg, okType := msg.(legacytx.LegacyMsg); okType { + return NewOperationMsgBasic(legacyMsg.Route(), legacyMsg.Type(), comment, ok, legacyMsg.GetSignBytes()) } - return NewOperationMsgBasic(msg.Route(), msg.Type(), comment, ok, msg.GetSignBytes()) + + bz := cdc.MustMarshalJSON(msg) + + return NewOperationMsgBasic(sdk.MsgTypeURL(msg), sdk.MsgTypeURL(msg), comment, ok, bz) + } // NoOpMsg - create a no-operation message diff --git a/types/tx/types.go b/types/tx/types.go index 66fbb193ad96..84ce81edcbf8 100644 --- a/types/tx/types.go +++ b/types/tx/types.go @@ -7,7 +7,6 @@ import ( cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/types/msgservice" ) // MaxGasWanted defines the max gas allowed. @@ -26,20 +25,11 @@ func (t *Tx) GetMsgs() []sdk.Msg { anys := t.Body.Messages res := make([]sdk.Msg, len(anys)) for i, any := range anys { - var msg sdk.Msg - if msgservice.IsServiceMsg(any.TypeUrl) { - req := any.GetCachedValue() - if req == nil { - panic("Any cached value is nil. Transaction messages must be correctly packed Any values.") - } - msg = sdk.ServiceMsg{ - MethodName: any.TypeUrl, - Request: any.GetCachedValue().(sdk.MsgRequest), - } - } else { - msg = any.GetCachedValue().(sdk.Msg) + cached := any.GetCachedValue() + if cached == nil { + panic("Any cached value is nil. Transaction messages must be correctly packed Any values.") } - res[i] = msg + res[i] = cached.(sdk.Msg) } return res } @@ -181,20 +171,10 @@ func (t *Tx) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { // UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method func (m *TxBody) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { for _, any := range m.Messages { - // If the any's typeUrl contains 2 slashes, then we unpack the any into - // a ServiceMsg struct as per ADR-031. - if msgservice.IsServiceMsg(any.TypeUrl) { - var req sdk.MsgRequest - err := unpacker.UnpackAny(any, &req) - if err != nil { - return err - } - } else { - var msg sdk.Msg - err := unpacker.UnpackAny(any, &msg) - if err != nil { - return err - } + var msg sdk.Msg + err := unpacker.UnpackAny(any, &msg) + if err != nil { + return err } } diff --git a/types/tx_msg.go b/types/tx_msg.go index b8a602d88c05..d7f15e83f078 100644 --- a/types/tx_msg.go +++ b/types/tx_msg.go @@ -1,6 +1,8 @@ package types import ( + fmt "fmt" + "github.com/gogo/protobuf/proto" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" @@ -11,21 +13,10 @@ type ( Msg interface { proto.Message - // Return the message type. - // Must be alphanumeric or empty. - Route() string - - // Returns a human-readable string for the message, intended for utilization - // within tags - Type() string - // ValidateBasic does a simple validation check that // doesn't require access to any other information. ValidateBasic() error - // Get the canonical byte representation of the Msg. - GetSignBytes() []byte - // Signers returns the addrs of signers that must sign. // CONTRACT: All signatures must be present to be valid. // CONTRACT: Returns addrs in some deterministic order. @@ -85,3 +76,8 @@ type TxDecoder func(txBytes []byte) (Tx, error) // TxEncoder marshals transaction to bytes type TxEncoder func(tx Tx) ([]byte, error) + +// MsgTypeURL returns the TypeURL of a `sdk.Msg`. +func MsgTypeURL(msg Msg) string { + return fmt.Sprintf("/%s", proto.MessageName(msg)) +} diff --git a/x/auth/client/testutil/suite.go b/x/auth/client/testutil/suite.go index 4c75d2b411e9..5e449e1f6aee 100644 --- a/x/auth/client/testutil/suite.go +++ b/x/auth/client/testutil/suite.go @@ -251,19 +251,7 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd() { sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) - // Send coins, try both with legacy Msg and with Msg service. - // Legacy proto Msg. - legacyTxRes, err := bankcli.LegacyGRPCProtoMsgSend( - val.ClientCtx, val.Moniker, - val.Address, - account2.GetAddress(), - sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))), - sdk.NewCoins(sendTokens), - ) - s.Require().NoError(err) - s.Require().NoError(s.network.WaitForNextBlock()) - - // Service Msg. + // Send coins. out, err := s.createBankMsg( val, account2.GetAddress(), sdk.NewCoins(sendTokens), @@ -292,16 +280,10 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd() { "", }, { - "happy case (legacy Msg)", - []string{legacyTxRes.TxResponse.TxHash, fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, - false, - "", - }, - { - "happy case (service Msg)", + "happy case", []string{txRes.TxHash, fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, - "/cosmos.bank.v1beta1.Msg/Send", + "/cosmos.bank.v1beta1.MsgSend", }, } diff --git a/x/auth/legacy/legacytx/stdsign.go b/x/auth/legacy/legacytx/stdsign.go index 0f352db26e16..d6b9212f12a3 100644 --- a/x/auth/legacy/legacytx/stdsign.go +++ b/x/auth/legacy/legacytx/stdsign.go @@ -16,6 +16,24 @@ import ( "github.com/cosmos/cosmos-sdk/types/tx/signing" ) +// LegacyMsg defines the old interface a message must fulfill, containing +// Amino signing method and legacy router info. +// Deprecated: Please use `Msg` instead. +type LegacyMsg interface { + sdk.Msg + + // Get the canonical byte representation of the Msg. + GetSignBytes() []byte + + // Return the message type. + // Must be alphanumeric or empty. + Route() string + + // Returns a human-readable string for the message, intended for utilization + // within tags + Type() string +} + // StdSignDoc is replay-prevention structure. // It includes the result of msg.GetSignBytes(), // as well as the ChainID (prevent cross chain replay) @@ -38,7 +56,7 @@ func StdSignBytes(chainID string, accnum, sequence, timeout uint64, fee StdFee, // If msg is a legacy Msg, then GetSignBytes is implemented. // If msg is a ServiceMsg, then GetSignBytes has graceful support of // calling GetSignBytes from its underlying Msg. - msgsBytes = append(msgsBytes, json.RawMessage(msg.GetSignBytes())) + msgsBytes = append(msgsBytes, json.RawMessage(msg.(LegacyMsg).GetSignBytes())) } bz, err := legacy.Cdc.MarshalJSON(StdSignDoc{ diff --git a/x/auth/legacy/legacytx/stdtx_builder.go b/x/auth/legacy/legacytx/stdtx_builder.go index 02ae449618ee..7e2e1c604438 100644 --- a/x/auth/legacy/legacytx/stdtx_builder.go +++ b/x/auth/legacy/legacytx/stdtx_builder.go @@ -29,25 +29,7 @@ func (s *StdTxBuilder) GetTx() authsigning.Tx { // SetMsgs implements TxBuilder.SetMsgs func (s *StdTxBuilder) SetMsgs(msgs ...sdk.Msg) error { - stdTxMsgs := make([]sdk.Msg, len(msgs)) - - for i, msg := range msgs { - switch msg := msg.(type) { - case sdk.ServiceMsg: - // Since ServiceMsg isn't registered with amino, we unpack msg.Request - // into a Msg so that it's handled gracefully for the legacy - // GET /txs/{hash} and /txs endpoints. - sdkMsg, ok := msg.Request.(sdk.Msg) - if !ok { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "Expecting %T at %d to implement sdk.Msg", msg.Request, i) - } - stdTxMsgs[i] = sdkMsg - default: - // legacy sdk.Msg - stdTxMsgs[i] = msg - } - } - s.Msgs = stdTxMsgs + s.Msgs = msgs return nil } diff --git a/x/auth/tx/builder.go b/x/auth/tx/builder.go index 31f4fdf8eca2..32eacd0137f7 100644 --- a/x/auth/tx/builder.go +++ b/x/auth/tx/builder.go @@ -204,12 +204,7 @@ func (w *wrapper) SetMsgs(msgs ...sdk.Msg) error { for i, msg := range msgs { var err error - switch msg := msg.(type) { - case sdk.ServiceMsg: - anys[i], err = codectypes.NewAnyWithCustomTypeURL(msg.Request, msg.MethodName) - default: - anys[i], err = codectypes.NewAnyWithValue(msg) - } + anys[i], err = codectypes.NewAnyWithValue(msg) if err != nil { return err } diff --git a/x/auth/tx/service_test.go b/x/auth/tx/service_test.go index 68c474facc36..98b2fbcbe677 100644 --- a/x/auth/tx/service_test.go +++ b/x/auth/tx/service_test.go @@ -32,6 +32,8 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) +var bankMsgSendEventAction = fmt.Sprintf("message.action='%s'", sdk.MsgTypeURL(&banktypes.MsgSend{})) + type IntegrationTestSuite struct { suite.Suite @@ -192,7 +194,7 @@ func (s IntegrationTestSuite) TestGetTxEvents_GRPC() { { "request with order-by", &tx.GetTxsEventRequest{ - Events: []string{"message.action='/cosmos.bank.v1beta1.Msg/Send'"}, + Events: []string{bankMsgSendEventAction}, OrderBy: tx.OrderBy_ORDER_BY_ASC, }, false, "", @@ -200,14 +202,14 @@ func (s IntegrationTestSuite) TestGetTxEvents_GRPC() { { "without pagination", &tx.GetTxsEventRequest{ - Events: []string{"message.action='/cosmos.bank.v1beta1.Msg/Send'"}, + Events: []string{bankMsgSendEventAction}, }, false, "", }, { "with pagination", &tx.GetTxsEventRequest{ - Events: []string{"message.action='/cosmos.bank.v1beta1.Msg/Send'"}, + Events: []string{bankMsgSendEventAction}, Pagination: &query.PageRequest{ CountTotal: false, Offset: 0, @@ -219,7 +221,7 @@ func (s IntegrationTestSuite) TestGetTxEvents_GRPC() { { "with multi events", &tx.GetTxsEventRequest{ - Events: []string{"message.action='/cosmos.bank.v1beta1.Msg/Send'", "message.module='bank'"}, + Events: []string{bankMsgSendEventAction, "message.module='bank'"}, }, false, "", }, @@ -262,43 +264,43 @@ func (s IntegrationTestSuite) TestGetTxEvents_GRPCGateway() { }, { "without pagination", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s", val.APIAddress, "message.action='/cosmos.bank.v1beta1.Msg/Send'"), + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s", val.APIAddress, bankMsgSendEventAction), false, "", }, { "with pagination", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&pagination.offset=%d&pagination.limit=%d", val.APIAddress, "message.action='/cosmos.bank.v1beta1.Msg/Send'", 0, 10), + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&pagination.offset=%d&pagination.limit=%d", val.APIAddress, bankMsgSendEventAction, 0, 10), false, "", }, { "valid request: order by asc", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s&order_by=ORDER_BY_ASC", val.APIAddress, "message.action='/cosmos.bank.v1beta1.Msg/Send'", "message.module='bank'"), + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s&order_by=ORDER_BY_ASC", val.APIAddress, bankMsgSendEventAction, "message.module='bank'"), false, "", }, { "valid request: order by desc", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s&order_by=ORDER_BY_DESC", val.APIAddress, "message.action='/cosmos.bank.v1beta1.Msg/Send'", "message.module='bank'"), + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s&order_by=ORDER_BY_DESC", val.APIAddress, bankMsgSendEventAction, "message.module='bank'"), false, "", }, { "invalid request: invalid order by", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s&order_by=invalid_order", val.APIAddress, "message.action='/cosmos.bank.v1beta1.Msg/Send'", "message.module='bank'"), + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s&order_by=invalid_order", val.APIAddress, bankMsgSendEventAction, "message.module='bank'"), true, "is not a valid tx.OrderBy", }, { "expect pass with multiple-events", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s", val.APIAddress, "message.action='/cosmos.bank.v1beta1.Msg/Send'", "message.module='bank'"), + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s&events=%s", val.APIAddress, bankMsgSendEventAction, "message.module='bank'"), false, "", }, { "expect pass with escape event", - fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s", val.APIAddress, "message.action%3D'%2Fcosmos.bank.v1beta1.Msg%2FSend'"), + fmt.Sprintf("%s/cosmos/tx/v1beta1/txs?events=%s", val.APIAddress, "message.action%3D'/cosmos.bank.v1beta1.MsgSend'"), false, "", }, @@ -584,6 +586,7 @@ func (s IntegrationTestSuite) mkTxBuilder() client.TxBuilder { ) txBuilder.SetFeeAmount(feeAmount) txBuilder.SetGasLimit(gasLimit) + txBuilder.SetMemo("foobar") // setup txFactory txFactory := clienttx.Factory{}. diff --git a/x/authz/client/cli/tx.go b/x/authz/client/cli/tx.go index 1b74e120f043..7af67ecda20a 100644 --- a/x/authz/client/cli/tx.go +++ b/x/authz/client/cli/tx.go @@ -60,7 +60,7 @@ func NewCmdGrantAuthorization() *cobra.Command { Examples: $ %s tx %s grant cosmos1skjw.. send %s --spend-limit=1000stake --from=cosmos1skl.. - $ %s tx %s grant cosmos1skjw.. generic --msg-type=/cosmos.gov.v1beta1.Msg/Vote --from=cosmos1sk.. + $ %s tx %s grant cosmos1skjw.. generic --msg-type=/cosmos.gov.v1beta1.MsgVote --from=cosmos1sk.. `, version.AppName, types.ModuleName, bank.SendAuthorization{}.MethodName(), version.AppName, types.ModuleName), ), Args: cobra.ExactArgs(2), @@ -254,17 +254,7 @@ Example: if err != nil { return err } - msgs := theTx.GetMsgs() - serviceMsgs := make([]sdk.ServiceMsg, len(msgs)) - for i, msg := range msgs { - srvMsg, ok := msg.(sdk.ServiceMsg) - if !ok { - return fmt.Errorf("tx contains %T which is not a sdk.ServiceMsg", msg) - } - serviceMsgs[i] = srvMsg - } - - msg := types.NewMsgExecAuthorized(grantee, serviceMsgs) + msg := types.NewMsgExecAuthorized(grantee, theTx.GetMsgs()) svcMsgClientConn := &msgservice.ServiceMsgClientConn{} msgClient := types.NewMsgClient(svcMsgClientConn) _, err = msgClient.ExecAuthorized(context.Background(), &msg) diff --git a/x/authz/client/rest/grpc_query_test.go b/x/authz/client/rest/grpc_query_test.go index 676af3915b01..a94c65d6366f 100644 --- a/x/authz/client/rest/grpc_query_test.go +++ b/x/authz/client/rest/grpc_query_test.go @@ -20,6 +20,7 @@ import ( types "github.com/cosmos/cosmos-sdk/x/authz/types" banktestutil "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" ) type IntegrationTestSuite struct { @@ -30,7 +31,7 @@ type IntegrationTestSuite struct { } var typeMsgSend = banktypes.SendAuthorization{}.MethodName() -var typeMsgVote = "/cosmos.gov.v1beta1.Msg/Vote" +var typeMsgVote = sdk.MsgTypeURL(&govtypes.MsgVote{}) func (s *IntegrationTestSuite) SetupSuite() { s.T().Log("setting up integration test suite") @@ -48,7 +49,7 @@ func (s *IntegrationTestSuite) SetupSuite() { newAddr := sdk.AccAddress(info.GetPubKey().Address()) // Send some funds to the new account. - _, err = banktestutil.MsgSendExec( + out, err := banktestutil.MsgSendExec( val.ClientCtx, val.Address, newAddr, @@ -57,9 +58,10 @@ func (s *IntegrationTestSuite) SetupSuite() { fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), ) s.Require().NoError(err) + s.Require().Contains(out.String(), `"code":0`) // grant authorization - _, err = authztestutil.ExecGrantAuthorization(val, []string{ + out, err = authztestutil.ExecGrantAuthorization(val, []string{ newAddr.String(), "send", fmt.Sprintf("--%s=100steak", cli.FlagSpendLimit), @@ -70,6 +72,7 @@ func (s *IntegrationTestSuite) SetupSuite() { fmt.Sprintf("--%s=%d", cli.FlagExpiration, time.Now().Add(time.Minute*time.Duration(120)).Unix()), }) s.Require().NoError(err) + s.Require().Contains(out.String(), `"code":0`) s.grantee = newAddr _, err = s.network.WaitForHeight(1) @@ -185,10 +188,9 @@ func (s *IntegrationTestSuite) TestQueryAuthorizationsGRPC() { fmt.Sprintf("%s/cosmos/authz/v1beta1/granters/%s/grantees/%s/grants", baseURL, val.Address.String(), s.grantee.String()), false, "", - func() { - }, + func() {}, func(authorizations *types.QueryAuthorizationsResponse) { - s.Require().Equal(len(authorizations.Authorizations), 1) + s.Require().Len(authorizations.Authorizations), 1) }, }, { diff --git a/x/authz/client/testutil/tx.go b/x/authz/client/testutil/tx.go index 08c3d706e59e..3b50074219b5 100644 --- a/x/authz/client/testutil/tx.go +++ b/x/authz/client/testutil/tx.go @@ -5,25 +5,22 @@ import ( "time" "github.com/gogo/protobuf/proto" - "github.com/stretchr/testify/suite" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/crypto/hd" "github.com/cosmos/cosmos-sdk/crypto/keyring" - - "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/testutil" clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" "github.com/cosmos/cosmos-sdk/testutil/network" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/authz/client/cli" + banktestutil "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" govcli "github.com/cosmos/cosmos-sdk/x/gov/client/cli" govtestutil "github.com/cosmos/cosmos-sdk/x/gov/client/testutil" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" stakingcli "github.com/cosmos/cosmos-sdk/x/staking/client/cli" - - banktestutil "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" - bank "github.com/cosmos/cosmos-sdk/x/bank/types" ) type IntegrationTestSuite struct { @@ -81,7 +78,7 @@ func (s *IntegrationTestSuite) TearDownSuite() { } var typeMsgSend = bank.SendAuthorization{}.MethodName() -var typeMsgVote = "/cosmos.gov.v1beta1.Msg/Vote" +var typeMsgVote = sdk.MsgTypeURL(&govtypes.MsgVote{}) func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { val := s.network.Validators[0] @@ -93,7 +90,6 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { testCases := []struct { name string args []string - respType proto.Message expectedCode uint32 expectErr bool }{ @@ -107,7 +103,6 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), }, - nil, 0, true, }, @@ -121,7 +116,7 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), }, - nil, 0, + 0, true, }, { @@ -134,7 +129,7 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), fmt.Sprintf("--%s=%d", cli.FlagExpiration, pastHour), }, - nil, 0, + 0, true, }, { @@ -149,8 +144,8 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), fmt.Sprintf("--%s=%d", cli.FlagExpiration, twoHours), }, - nil, 0, - true, + 0x1d, + false, }, { "failed with error both validators not allowed", @@ -166,7 +161,7 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { fmt.Sprintf("--%s=%s", cli.FlagDenyValidators, val.ValAddress.String()), fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), }, - nil, 0, + 0, true, }, { @@ -182,7 +177,7 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { fmt.Sprintf("--%s=%s", cli.FlagAllowedValidators, val.ValAddress.String()), fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), }, - &sdk.TxResponse{}, 0, + 0, false, }, { @@ -198,7 +193,7 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { fmt.Sprintf("--%s=%s", cli.FlagDenyValidators, val.ValAddress.String()), fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), }, - &sdk.TxResponse{}, 0, + 0, false, }, { @@ -214,7 +209,7 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { fmt.Sprintf("--%s=%s", cli.FlagAllowedValidators, val.ValAddress.String()), fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), }, - &sdk.TxResponse{}, 0, + 0, false, }, { @@ -230,7 +225,7 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { fmt.Sprintf("--%s=%s", cli.FlagAllowedValidators, val.ValAddress.String()), fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), }, - &sdk.TxResponse{}, 0, + 0, false, }, { @@ -245,7 +240,7 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), }, - &sdk.TxResponse{}, 0, + 0, false, }, { @@ -260,7 +255,7 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), }, - &sdk.TxResponse{}, 0, + 0, false, }, } @@ -276,9 +271,9 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { if tc.expectErr { s.Require().Error(err) } else { + var txResp sdk.TxResponse s.Require().NoError(err) - s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), tc.respType), out.String()) - txResp := tc.respType.(*sdk.TxResponse) + s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(out.Bytes(), &txResp), out.String()) s.Require().Equal(tc.expectedCode, txResp.Code, out.String()) } }) @@ -427,7 +422,7 @@ func (s *IntegrationTestSuite) TestExecAuthorizationWithExpiration() { ) s.Require().NoError(err) // msg vote - voteTx := fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.gov.v1beta1.Msg/Vote","proposal_id":"1","voter":"%s","option":"VOTE_OPTION_YES"}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val.Address.String()) + voteTx := fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.gov.v1beta1.MsgVote","proposal_id":"1","voter":"%s","option":"VOTE_OPTION_YES"}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val.Address.String()) execMsg := testutil.WriteToNewTempFile(s.T(), voteTx) // waiting for authorization to expires @@ -468,7 +463,7 @@ func (s *IntegrationTestSuite) TestNewExecGenericAuthorized() { s.Require().NoError(err) // msg vote - voteTx := fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.gov.v1beta1.Msg/Vote","proposal_id":"1","voter":"%s","option":"VOTE_OPTION_YES"}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val.Address.String()) + voteTx := fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.gov.v1beta1.MsgVote","proposal_id":"1","voter":"%s","option":"VOTE_OPTION_YES"}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val.Address.String()) execMsg := testutil.WriteToNewTempFile(s.T(), voteTx) testCases := []struct { @@ -646,7 +641,7 @@ func (s *IntegrationTestSuite) TestExecDelegateAuthorization() { sdk.NewCoin("stake", sdk.NewInt(50)), ) - delegateTx := fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.staking.v1beta1.Msg/Delegate","delegator_address":"%s","validator_address":"%s","amount":{"denom":"%s","amount":"%s"}}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val.Address.String(), val.ValAddress.String(), + delegateTx := fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.staking.v1beta1.MsgDelegate","delegator_address":"%s","validator_address":"%s","amount":{"denom":"%s","amount":"%s"}}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val.Address.String(), val.ValAddress.String(), tokens.GetDenomByIndex(0), tokens[0].Amount) execMsg := testutil.WriteToNewTempFile(s.T(), delegateTx) @@ -736,7 +731,7 @@ func (s *IntegrationTestSuite) TestExecDelegateAuthorization() { sdk.NewCoin("stake", sdk.NewInt(50)), ) - delegateTx = fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.staking.v1beta1.Msg/Delegate","delegator_address":"%s","validator_address":"%s","amount":{"denom":"%s","amount":"%s"}}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val.Address.String(), val.ValAddress.String(), + delegateTx = fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.staking.v1beta1.MsgDelegate","delegator_address":"%s","validator_address":"%s","amount":{"denom":"%s","amount":"%s"}}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val.Address.String(), val.ValAddress.String(), tokens.GetDenomByIndex(0), tokens[0].Amount) execMsg = testutil.WriteToNewTempFile(s.T(), delegateTx) @@ -822,7 +817,6 @@ func (s *IntegrationTestSuite) TestExecDelegateAuthorization() { out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, args) s.Require().NoError(err) s.Contains(out.String(), fmt.Sprintf("cannot delegate/undelegate to %s validator", val.ValAddress.String())) - } func (s *IntegrationTestSuite) TestExecUndelegateAuthorization() { @@ -865,7 +859,7 @@ func (s *IntegrationTestSuite) TestExecUndelegateAuthorization() { sdk.NewCoin("stake", sdk.NewInt(50)), ) - undelegateTx := fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.staking.v1beta1.Msg/Undelegate","delegator_address":"%s","validator_address":"%s","amount":{"denom":"%s","amount":"%s"}}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val.Address.String(), val.ValAddress.String(), + undelegateTx := fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.staking.v1beta1.MsgUndelegate","delegator_address":"%s","validator_address":"%s","amount":{"denom":"%s","amount":"%s"}}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val.Address.String(), val.ValAddress.String(), tokens.GetDenomByIndex(0), tokens[0].Amount) execMsg := testutil.WriteToNewTempFile(s.T(), undelegateTx) @@ -955,7 +949,7 @@ func (s *IntegrationTestSuite) TestExecUndelegateAuthorization() { sdk.NewCoin("stake", sdk.NewInt(50)), ) - undelegateTx = fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.staking.v1beta1.Msg/Undelegate","delegator_address":"%s","validator_address":"%s","amount":{"denom":"%s","amount":"%s"}}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val.Address.String(), val.ValAddress.String(), + undelegateTx = fmt.Sprintf(`{"body":{"messages":[{"@type":"/cosmos.staking.v1beta1.MsgUndelegate","delegator_address":"%s","validator_address":"%s","amount":{"denom":"%s","amount":"%s"}}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]}`, val.Address.String(), val.ValAddress.String(), tokens.GetDenomByIndex(0), tokens[0].Amount) execMsg = testutil.WriteToNewTempFile(s.T(), undelegateTx) diff --git a/x/authz/exported/authorizations.go b/x/authz/exported/authorizations.go index 32fea0b0b3a8..c065c9f65072 100644 --- a/x/authz/exported/authorizations.go +++ b/x/authz/exported/authorizations.go @@ -15,7 +15,7 @@ type Authorization interface { // Accept determines whether this grant permits the provided sdk.ServiceMsg to be performed, and if // so provides an upgraded authorization instance. - Accept(ctx sdk.Context, msg sdk.ServiceMsg) (updated Authorization, delete bool, err error) + Accept(ctx sdk.Context, msg sdk.Msg) (updated Authorization, delete bool, err error) // ValidateBasic does a simple validation check that // doesn't require access to any other information. diff --git a/x/authz/keeper/keeper.go b/x/authz/keeper/keeper.go index 42acd0d77405..88416b88710f 100644 --- a/x/authz/keeper/keeper.go +++ b/x/authz/keeper/keeper.go @@ -73,26 +73,29 @@ func (k Keeper) update(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccA // DispatchActions attempts to execute the provided messages via authorization // grants from the message signer to the grantee. -func (k Keeper) DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, serviceMsgs []sdk.ServiceMsg) (*sdk.Result, error) { +func (k Keeper) DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, msgs []sdk.Msg) (*sdk.Result, error) { var msgResult *sdk.Result var err error - for _, serviceMsg := range serviceMsgs { - signers := serviceMsg.GetSigners() + for _, msg := range msgs { + signers := msg.GetSigners() if len(signers) != 1 { return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "authorization can be given to msg with only one signer") } granter := signers[0] if !granter.Equals(grantee) { - authorization, _ := k.GetOrRevokeAuthorization(ctx, grantee, granter, serviceMsg.MethodName) + authorization, _ := k.GetOrRevokeAuthorization(ctx, grantee, granter, sdk.MsgTypeURL(msg)) if authorization == nil { return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "authorization not found") } - updated, del, err := authorization.Accept(ctx, serviceMsg) + updated, del, err := authorization.Accept(ctx, msg) if err != nil { return nil, err } if del { - k.Revoke(ctx, grantee, granter, serviceMsg.Type()) + err = k.Revoke(ctx, grantee, granter, sdk.MsgTypeURL(msg)) + if err != nil { + return nil, err + } } else if updated != nil { err = k.update(ctx, grantee, granter, updated) if err != nil { @@ -100,15 +103,15 @@ func (k Keeper) DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, service } } } - handler := k.router.Handler(serviceMsg.Route()) + handler := k.router.Handler(msg) if handler == nil { - return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized message route: %s", serviceMsg.Route()) + return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized message route: %s", sdk.MsgTypeURL(msg)) } - msgResult, err = handler(ctx, serviceMsg.Request) + msgResult, err = handler(ctx, msg) if err != nil { - return nil, sdkerrors.Wrapf(err, "failed to execute message; message %s", serviceMsg.MethodName) + return nil, sdkerrors.Wrapf(err, "failed to execute message; message %v", msg) } } diff --git a/x/authz/keeper/keeper_test.go b/x/authz/keeper/keeper_test.go index 161fed6e98bd..91ea9b5fcbd1 100644 --- a/x/authz/keeper/keeper_test.go +++ b/x/authz/keeper/keeper_test.go @@ -4,15 +4,11 @@ import ( "testing" "time" - proto "github.com/gogo/protobuf/proto" - - "github.com/cosmos/cosmos-sdk/baseapp" - + "github.com/stretchr/testify/suite" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" - "github.com/stretchr/testify/suite" - + "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/authz/types" @@ -42,7 +38,6 @@ func (s *TestSuite) SetupTest() { s.ctx = ctx s.queryClient = queryClient s.addrs = simapp.AddTestAddrsIncremental(app, ctx, 3, sdk.NewInt(30000000)) - } func (s *TestSuite) TestKeeper() { @@ -76,7 +71,7 @@ func (s *TestSuite) TestKeeper() { s.Require().Equal(authorization.MethodName(), banktypes.SendAuthorization{}.MethodName()) s.T().Log("verify fetching authorization with wrong msg type fails") - authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, proto.MessageName(&banktypes.MsgMultiSend{})) + authorization, _ = app.AuthzKeeper.GetOrRevokeAuthorization(ctx, granteeAddr, granterAddr, sdk.MsgTypeURL(&banktypes.MsgMultiSend{})) s.Require().Nil(authorization) s.T().Log("verify fetching authorization with wrong grantee fails") @@ -139,21 +134,18 @@ func (s *TestSuite) TestKeeperFees() { smallCoin := sdk.NewCoins(sdk.NewInt64Coin("steak", 20)) someCoin := sdk.NewCoins(sdk.NewInt64Coin("steak", 123)) - msgs := types.NewMsgExecAuthorized(granteeAddr, []sdk.ServiceMsg{ - { - MethodName: banktypes.SendAuthorization{}.MethodName(), - Request: &banktypes.MsgSend{ - Amount: sdk.NewCoins(sdk.NewInt64Coin("steak", 2)), - FromAddress: granterAddr.String(), - ToAddress: recipientAddr.String(), - }, + msgs := types.NewMsgExecAuthorized(granteeAddr, []sdk.Msg{ + &banktypes.MsgSend{ + Amount: sdk.NewCoins(sdk.NewInt64Coin("steak", 2)), + FromAddress: granterAddr.String(), + ToAddress: recipientAddr.String(), }, }) s.Require().NoError(msgs.UnpackInterfaces(app.AppCodec())) s.T().Log("verify dispatch fails with invalid authorization") - executeMsgs, err := msgs.GetServiceMsgs() + executeMsgs, err := msgs.GetMessages() s.Require().NoError(err) result, err := app.AuthzKeeper.DispatchActions(s.ctx, granteeAddr, executeMsgs) @@ -169,7 +161,7 @@ func (s *TestSuite) TestKeeperFees() { s.Require().Equal(authorization.MethodName(), banktypes.SendAuthorization{}.MethodName()) - executeMsgs, err = msgs.GetServiceMsgs() + executeMsgs, err = msgs.GetMessages() s.Require().NoError(err) result, err = app.AuthzKeeper.DispatchActions(s.ctx, granteeAddr, executeMsgs) @@ -182,19 +174,16 @@ func (s *TestSuite) TestKeeperFees() { s.T().Log("verify dispatch fails with overlimit") // grant authorization - msgs = types.NewMsgExecAuthorized(granteeAddr, []sdk.ServiceMsg{ - { - MethodName: banktypes.SendAuthorization{}.MethodName(), - Request: &banktypes.MsgSend{ - Amount: someCoin, - FromAddress: granterAddr.String(), - ToAddress: recipientAddr.String(), - }, + msgs = types.NewMsgExecAuthorized(granteeAddr, []sdk.Msg{ + &banktypes.MsgSend{ + Amount: someCoin, + FromAddress: granterAddr.String(), + ToAddress: recipientAddr.String(), }, }) s.Require().NoError(msgs.UnpackInterfaces(app.AppCodec())) - executeMsgs, err = msgs.GetServiceMsgs() + executeMsgs, err = msgs.GetMessages() s.Require().NoError(err) result, err = app.AuthzKeeper.DispatchActions(s.ctx, granteeAddr, executeMsgs) diff --git a/x/authz/keeper/msg_server.go b/x/authz/keeper/msg_server.go index 53beb129dfaa..85c5ff262b58 100644 --- a/x/authz/keeper/msg_server.go +++ b/x/authz/keeper/msg_server.go @@ -24,7 +24,7 @@ func (k Keeper) GrantAuthorization(goCtx context.Context, msg *types.MsgGrantAut authorization := msg.GetGrantAuthorization() // If the granted service Msg doesn't exist, we throw an error. - if k.router.Handler(authorization.MethodName()) == nil { + if k.router.HandlerbyTypeURL(authorization.MethodName()) == nil { return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "%s doesn't exist.", authorization.MethodName()) } @@ -63,7 +63,7 @@ func (k Keeper) ExecAuthorized(goCtx context.Context, msg *types.MsgExecAuthoriz if err != nil { return nil, err } - msgs, err := msg.GetServiceMsgs() + msgs, err := msg.GetMessages() if err != nil { return nil, err } diff --git a/x/authz/simulation/operations.go b/x/authz/simulation/operations.go index b01296d8bc11..6b3c0f6ab10b 100644 --- a/x/authz/simulation/operations.go +++ b/x/authz/simulation/operations.go @@ -20,10 +20,12 @@ import ( ) // authz message types -const ( - TypeMsgGrantAuthorization = "/cosmos.authz.v1beta1.Msg/GrantAuthorization" - TypeMsgRevokeAuthorization = "/cosmos.authz.v1beta1.Msg/RevokeAuthorization" - TypeMsgExecDelegated = "/cosmos.authz.v1beta1.Msg/ExecAuthorized" +var ( + // TODO Remove `Request` suffix + // https://github.com/cosmos/cosmos-sdk/issues/9114 + TypeMsgGrantAuthorization = sdk.MsgTypeURL(&types.MsgGrantAuthorizationRequest{}) + TypeMsgRevokeAuthorization = sdk.MsgTypeURL(&types.MsgRevokeAuthorizationRequest{}) + TypeMsgExecDelegated = sdk.MsgTypeURL(&types.MsgExecAuthorizedRequest{}) ) // Simulation operation weights constants @@ -130,7 +132,7 @@ func SimulateMsgGrantAuthorization(ak types.AccountKeeper, bk types.BankKeeper, _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { - return simtypes.NoOpMsg(types.ModuleName, svcMsgClientConn.GetMsgs()[0].Type(), "unable to deliver tx"), nil, err + return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(svcMsgClientConn.GetMsgs()[0]), "unable to deliver tx"), nil, err } return simtypes.NewOperationMsg(svcMsgClientConn.GetMsgs()[0], true, "", protoCdc), nil, err } @@ -242,16 +244,13 @@ func SimulateMsgExecuteAuthorized(ak types.AccountKeeper, bk types.BankKeeper, k } sendCoins := sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(10))) - execMsg := sdk.ServiceMsg{ - MethodName: banktype.SendAuthorization{}.MethodName(), - Request: banktype.NewMsgSend( - granterAddr, - granteeAddr, - sendCoins, - ), - } + execMsg := banktype.NewMsgSend( + granterAddr, + granteeAddr, + sendCoins, + ) - msg := types.NewMsgExecAuthorized(grantee.Address, []sdk.ServiceMsg{execMsg}) + msg := types.NewMsgExecAuthorized(grantee.Address, []sdk.Msg{execMsg}) sendGrant := targetGrant.Authorization.GetCachedValue().(*banktype.SendAuthorization) _, _, err = sendGrant.Accept(ctx, execMsg) if err != nil { diff --git a/x/authz/types/codec.go b/x/authz/types/codec.go index c6119f2a0bbb..dd68cf0bc126 100644 --- a/x/authz/types/codec.go +++ b/x/authz/types/codec.go @@ -11,7 +11,7 @@ import ( // RegisterInterfaces registers the interfaces types with the interface registry func RegisterInterfaces(registry types.InterfaceRegistry) { - registry.RegisterImplementations((*sdk.MsgRequest)(nil), + registry.RegisterImplementations((*sdk.Msg)(nil), &MsgGrantAuthorizationRequest{}, &MsgRevokeAuthorizationRequest{}, &MsgExecAuthorizedRequest{}, diff --git a/x/authz/types/generic_authorization.go b/x/authz/types/generic_authorization.go index 101f3acf1664..67af64c99dcf 100644 --- a/x/authz/types/generic_authorization.go +++ b/x/authz/types/generic_authorization.go @@ -2,8 +2,6 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/x/authz/exported" ) @@ -24,14 +22,11 @@ func (authorization GenericAuthorization) MethodName() string { } // Accept implements Authorization.Accept. -func (authorization GenericAuthorization) Accept(ctx sdk.Context, msg sdk.ServiceMsg) (updated exported.Authorization, delete bool, err error) { +func (authorization GenericAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (updated exported.Authorization, delete bool, err error) { return &authorization, false, nil } // ValidateBasic implements Authorization.ValidateBasic. func (authorization GenericAuthorization) ValidateBasic() error { - if !msgservice.IsServiceMsg(authorization.MessageName) { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, " %s is not a valid service msg", authorization.MessageName) - } return nil } diff --git a/x/authz/types/generic_authorization_test.go b/x/authz/types/generic_authorization_test.go index 2dda401c693f..a5ffe0803a81 100644 --- a/x/authz/types/generic_authorization_test.go +++ b/x/authz/types/generic_authorization_test.go @@ -10,12 +10,8 @@ import ( ) func TestGenericAuthorization(t *testing.T) { - t.Log("verify ValidateBasic returns error for non-service msg") - authorization := types.NewGenericAuthorization(banktypes.TypeMsgSend) - require.Error(t, authorization.ValidateBasic()) - t.Log("verify ValidateBasic returns nil for service msg") - authorization = types.NewGenericAuthorization(banktypes.SendAuthorization{}.MethodName()) + authorization := types.NewGenericAuthorization(banktypes.SendAuthorization{}.MethodName()) require.NoError(t, authorization.ValidateBasic()) require.Equal(t, banktypes.SendAuthorization{}.MethodName(), authorization.MessageName) } diff --git a/x/authz/types/msgs.go b/x/authz/types/msgs.go index 0b72d1a7e869..bdb38ccc915f 100644 --- a/x/authz/types/msgs.go +++ b/x/authz/types/msgs.go @@ -12,9 +12,9 @@ import ( ) var ( - _ sdk.MsgRequest = &MsgGrantAuthorizationRequest{} - _ sdk.MsgRequest = &MsgRevokeAuthorizationRequest{} - _ sdk.MsgRequest = &MsgExecAuthorizedRequest{} + _ sdk.Msg = &MsgGrantAuthorizationRequest{} + _ sdk.Msg = &MsgRevokeAuthorizationRequest{} + _ sdk.Msg = &MsgExecAuthorizedRequest{} _ types.UnpackInterfacesMessage = &MsgGrantAuthorizationRequest{} _ types.UnpackInterfacesMessage = &MsgExecAuthorizedRequest{} @@ -96,7 +96,7 @@ func (msg *MsgGrantAuthorizationRequest) SetAuthorization(authorization exported // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces func (msg MsgExecAuthorizedRequest) UnpackInterfaces(unpacker types.AnyUnpacker) error { for _, x := range msg.Msgs { - var msgExecAuthorized sdk.MsgRequest + var msgExecAuthorized sdk.Msg err := unpacker.UnpackAny(x, &msgExecAuthorized) if err != nil { return err @@ -155,20 +155,15 @@ func (msg MsgRevokeAuthorizationRequest) ValidateBasic() error { // NewMsgExecAuthorized creates a new MsgExecAuthorized //nolint:interfacer -func NewMsgExecAuthorized(grantee sdk.AccAddress, msgs []sdk.ServiceMsg) MsgExecAuthorizedRequest { +func NewMsgExecAuthorized(grantee sdk.AccAddress, msgs []sdk.Msg) MsgExecAuthorizedRequest { msgsAny := make([]*types.Any, len(msgs)) for i, msg := range msgs { - bz, err := proto.Marshal(msg.Request) + any, err := types.NewAnyWithValue(msg) if err != nil { panic(err) } - anyMsg := &types.Any{ - TypeUrl: msg.MethodName, - Value: bz, - } - - msgsAny[i] = anyMsg + msgsAny[i] = any } return MsgExecAuthorizedRequest{ @@ -177,20 +172,15 @@ func NewMsgExecAuthorized(grantee sdk.AccAddress, msgs []sdk.ServiceMsg) MsgExec } } -// GetServiceMsgs returns the cache values from the MsgExecAuthorized.Msgs if present. -func (msg MsgExecAuthorizedRequest) GetServiceMsgs() ([]sdk.ServiceMsg, error) { - msgs := make([]sdk.ServiceMsg, len(msg.Msgs)) +// GetMessages returns the cache values from the MsgExecAuthorized.Msgs if present. +func (msg MsgExecAuthorizedRequest) GetMessages() ([]sdk.Msg, error) { + msgs := make([]sdk.Msg, len(msg.Msgs)) for i, msgAny := range msg.Msgs { - msgReq, ok := msgAny.GetCachedValue().(sdk.MsgRequest) + msg, ok := msgAny.GetCachedValue().(sdk.Msg) if !ok { return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "messages contains %T which is not a sdk.MsgRequest", msgAny) } - srvMsg := sdk.ServiceMsg{ - MethodName: msgAny.TypeUrl, - Request: msgReq, - } - - msgs[i] = srvMsg + msgs[i] = msg } return msgs, nil diff --git a/x/authz/types/msgs_test.go b/x/authz/types/msgs_test.go index c27c750b0d93..9062cc9b0967 100644 --- a/x/authz/types/msgs_test.go +++ b/x/authz/types/msgs_test.go @@ -23,19 +23,16 @@ func TestMsgExecAuthorized(t *testing.T) { tests := []struct { title string grantee sdk.AccAddress - msgs []sdk.ServiceMsg + msgs []sdk.Msg expectPass bool }{ - {"nil grantee address", nil, []sdk.ServiceMsg{}, false}, - {"zero-messages test: should fail", grantee, []sdk.ServiceMsg{}, false}, - {"valid test: msg type", grantee, []sdk.ServiceMsg{ - { - MethodName: banktypes.SendAuthorization{}.MethodName(), - Request: &banktypes.MsgSend{ - Amount: sdk.NewCoins(sdk.NewInt64Coin("steak", 2)), - FromAddress: granter.String(), - ToAddress: grantee.String(), - }, + {"nil grantee address", nil, []sdk.Msg{}, false}, + {"zero-messages test: should fail", grantee, []sdk.Msg{}, false}, + {"valid test: msg type", grantee, []sdk.Msg{ + &banktypes.MsgSend{ + Amount: sdk.NewCoins(sdk.NewInt64Coin("steak", 2)), + FromAddress: granter.String(), + ToAddress: grantee.String(), }, }, true}, } diff --git a/x/bank/client/testutil/cli_helpers.go b/x/bank/client/testutil/cli_helpers.go index 57a5a5798edc..a1a33c4f67bc 100644 --- a/x/bank/client/testutil/cli_helpers.go +++ b/x/bank/client/testutil/cli_helpers.go @@ -1,22 +1,14 @@ package testutil import ( - "context" "fmt" "github.com/tendermint/tendermint/libs/cli" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/testutil" clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "github.com/cosmos/cosmos-sdk/testutil/testdata" - sdk "github.com/cosmos/cosmos-sdk/types" - txtypes "github.com/cosmos/cosmos-sdk/types/tx" - "github.com/cosmos/cosmos-sdk/types/tx/signing" - authclient "github.com/cosmos/cosmos-sdk/x/auth/client" bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/cli" - "github.com/cosmos/cosmos-sdk/x/bank/types" ) func MsgSendExec(clientCtx client.Context, from, to, amount fmt.Stringer, extraArgs ...string) (testutil.BufferWriter, error) { @@ -32,56 +24,3 @@ func QueryBalancesExec(clientCtx client.Context, address fmt.Stringer, extraArgs return clitestutil.ExecTestCLICmd(clientCtx, bankcli.GetBalancesCmd(), args) } - -// LegacyGRPCProtoMsgSend is a legacy method to broadcast a legacy proto MsgSend. -// -// Deprecated. -//nolint:interfacer -func LegacyGRPCProtoMsgSend(clientCtx client.Context, keyName string, from, to sdk.Address, fee, amount []sdk.Coin, extraArgs ...string) (*txtypes.BroadcastTxResponse, error) { - // prepare txBuilder with msg - txBuilder := clientCtx.TxConfig.NewTxBuilder() - feeAmount := fee - gasLimit := testdata.NewTestGasLimit() - - // This sets a legacy Proto MsgSend. - err := txBuilder.SetMsgs(&types.MsgSend{ - FromAddress: from.String(), - ToAddress: to.String(), - Amount: amount, - }) - if err != nil { - return nil, err - } - - txBuilder.SetFeeAmount(feeAmount) - txBuilder.SetGasLimit(gasLimit) - - // setup txFactory - txFactory := tx.Factory{}. - WithChainID(clientCtx.ChainID). - WithKeybase(clientCtx.Keyring). - WithTxConfig(clientCtx.TxConfig). - WithSignMode(signing.SignMode_SIGN_MODE_DIRECT) - - // Sign Tx. - err = authclient.SignTx(txFactory, clientCtx, keyName, txBuilder, false, true) - if err != nil { - return nil, err - } - - txBytes, err := clientCtx.TxConfig.TxEncoder()(txBuilder.GetTx()) - if err != nil { - return nil, err - } - - // Broadcast the tx via gRPC. - queryClient := txtypes.NewServiceClient(clientCtx) - - return queryClient.BroadcastTx( - context.Background(), - &txtypes.BroadcastTxRequest{ - Mode: txtypes.BroadcastMode_BROADCAST_MODE_SYNC, - TxBytes: txBytes, - }, - ) -} diff --git a/x/bank/client/testutil/suite.go b/x/bank/client/testutil/suite.go index d56835f9b68f..342c780253e0 100644 --- a/x/bank/client/testutil/suite.go +++ b/x/bank/client/testutil/suite.go @@ -383,10 +383,7 @@ func (s *IntegrationTestSuite) TestNewSendTxCmdGenOnly() { s.Require().NoError(err) tx, err := s.cfg.TxConfig.TxJSONDecoder()(bz.Bytes()) s.Require().NoError(err) - s.Require().Equal([]sdk.Msg{sdk.ServiceMsg{ - MethodName: "/cosmos.bank.v1beta1.Msg/Send", - Request: types.NewMsgSend(from, to, amount), - }}, tx.GetMsgs()) + s.Require().Equal([]sdk.Msg{types.NewMsgSend(from, to, amount)}, tx.GetMsgs()) } func (s *IntegrationTestSuite) TestNewSendTxCmd() { @@ -473,62 +470,6 @@ func (s *IntegrationTestSuite) TestNewSendTxCmd() { } } -// TestBankMsgService does a basic test of whether or not service Msg's as defined -// in ADR 031 work in the most basic end-to-end case. -func (s *IntegrationTestSuite) TestBankMsgService() { - val := s.network.Validators[0] - - testCases := []struct { - name string - from, to sdk.AccAddress - amount sdk.Coins - args []string - expectErr bool - expectedCode uint32 - respType proto.Message - rawLogContains string - }{ - { - "valid transaction", - val.Address, - val.Address, - sdk.NewCoins( - sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), sdk.NewInt(10)), - sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)), - ), - []string{ - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - }, - false, - 0, - &sdk.TxResponse{}, - "/cosmos.bank.v1beta1.Msg/Send", // indicates we are using ServiceMsg and not a regular Msg - }, - } - - for _, tc := range testCases { - tc := tc - - s.Run(tc.name, func() { - clientCtx := val.ClientCtx - - bz, err := MsgSendExec(clientCtx, tc.from, tc.to, tc.amount, tc.args...) - if tc.expectErr { - s.Require().Error(err) - } else { - s.Require().NoError(err) - - s.Require().NoError(clientCtx.JSONMarshaler.UnmarshalJSON(bz.Bytes(), tc.respType), bz.String()) - txResp := tc.respType.(*sdk.TxResponse) - s.Require().Equal(tc.expectedCode, txResp.Code) - s.Require().Contains(txResp.RawLog, tc.rawLogContains) - } - }) - } -} - func NewCoin(denom string, amount sdk.Int) *sdk.Coin { coin := sdk.NewCoin(denom, amount) return &coin diff --git a/x/bank/types/send_authorization.go b/x/bank/types/send_authorization.go index 82d1bf1e630b..758526f64fa1 100644 --- a/x/bank/types/send_authorization.go +++ b/x/bank/types/send_authorization.go @@ -1,8 +1,6 @@ package types import ( - "reflect" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authz "github.com/cosmos/cosmos-sdk/x/authz/exported" @@ -21,26 +19,25 @@ func NewSendAuthorization(spendLimit sdk.Coins) *SendAuthorization { // MethodName implements Authorization.MethodName. func (authorization SendAuthorization) MethodName() string { - return "/cosmos.bank.v1beta1.Msg/Send" + return sdk.MsgTypeURL(&MsgSend{}) } // Accept implements Authorization.Accept. -func (authorization SendAuthorization) Accept(ctx sdk.Context, msg sdk.ServiceMsg) (updated authz.Authorization, delete bool, err error) { - if reflect.TypeOf(msg.Request) == reflect.TypeOf(&MsgSend{}) { - msg, ok := msg.Request.(*MsgSend) - if ok { - limitLeft, isNegative := authorization.SpendLimit.SafeSub(msg.Amount) - if isNegative { - return nil, false, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "requested amount is more than spend limit") - } - if limitLeft.IsZero() { - return nil, true, nil - } - - return &SendAuthorization{SpendLimit: limitLeft}, false, nil - } +func (authorization SendAuthorization) Accept(_ sdk.Context, msg sdk.Msg) (updated authz.Authorization, delete bool, err error) { + msgSend, ok := msg.(*MsgSend) + if !ok { + return nil, false, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "type mismatch") + } + + limitLeft, isNegative := authorization.SpendLimit.SafeSub(msgSend.Amount) + if isNegative { + return nil, false, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "requested amount is more than spend limit") } - return nil, false, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "type mismatch") + if limitLeft.IsZero() { + return nil, true, nil + } + + return &SendAuthorization{SpendLimit: limitLeft}, false, nil } // ValidateBasic implements Authorization.ValidateBasic. diff --git a/x/bank/types/send_authorization_test.go b/x/bank/types/send_authorization_test.go index 45bb718cd847..8ff979763612 100644 --- a/x/bank/types/send_authorization_test.go +++ b/x/bank/types/send_authorization_test.go @@ -24,31 +24,24 @@ func TestSendAuthorization(t *testing.T) { authorization := types.NewSendAuthorization(coins1000) t.Log("verify authorization returns valid method name") - require.Equal(t, authorization.MethodName(), "/cosmos.bank.v1beta1.Msg/Send") + require.Equal(t, authorization.MethodName(), "/cosmos.bank.v1beta1.MsgSend") require.NoError(t, authorization.ValidateBasic()) send := types.NewMsgSend(fromAddr, toAddr, coins1000) - srvMsg := sdk.ServiceMsg{ - MethodName: "/cosmos.bank.v1beta1.Msg/Send", - Request: send, - } + require.NoError(t, authorization.ValidateBasic()) t.Log("verify updated authorization returns nil") - updated, del, err := authorization.Accept(ctx, srvMsg) + updated, del, err := authorization.Accept(ctx, send) require.NoError(t, err) require.True(t, del) require.Nil(t, updated) authorization = types.NewSendAuthorization(coins1000) - require.Equal(t, authorization.MethodName(), "/cosmos.bank.v1beta1.Msg/Send") + require.Equal(t, authorization.MethodName(), "/cosmos.bank.v1beta1.MsgSend") require.NoError(t, authorization.ValidateBasic()) send = types.NewMsgSend(fromAddr, toAddr, coins500) - srvMsg = sdk.ServiceMsg{ - MethodName: "/cosmos.bank.v1beta1.Msg/Send", - Request: send, - } require.NoError(t, authorization.ValidateBasic()) - updated, del, err = authorization.Accept(ctx, srvMsg) + updated, del, err = authorization.Accept(ctx, send) t.Log("verify updated authorization returns remaining spent limit") require.NoError(t, err) @@ -58,7 +51,7 @@ func TestSendAuthorization(t *testing.T) { require.Equal(t, sendAuth.String(), updated.String()) t.Log("expect updated authorization nil after spending remaining amount") - updated, del, err = updated.Accept(ctx, srvMsg) + updated, del, err = updated.Accept(ctx, send) require.NoError(t, err) require.True(t, del) require.Nil(t, updated) diff --git a/x/evidence/types/msgs_test.go b/x/evidence/types/msgs_test.go index 4248b0dfba94..ee1ed509bca5 100644 --- a/x/evidence/types/msgs_test.go +++ b/x/evidence/types/msgs_test.go @@ -8,6 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" "github.com/cosmos/cosmos-sdk/x/evidence/exported" "github.com/cosmos/cosmos-sdk/x/evidence/types" ) @@ -50,8 +51,8 @@ func TestMsgSubmitEvidence(t *testing.T) { } for i, tc := range testCases { - require.Equal(t, tc.msg.Route(), types.RouterKey, "unexpected result for tc #%d", i) - require.Equal(t, tc.msg.Type(), types.TypeMsgSubmitEvidence, "unexpected result for tc #%d", i) + require.Equal(t, tc.msg.(legacytx.LegacyMsg).Route(), types.RouterKey, "unexpected result for tc #%d", i) + require.Equal(t, tc.msg.(legacytx.LegacyMsg).Type(), types.TypeMsgSubmitEvidence, "unexpected result for tc #%d", i) require.Equal(t, tc.expectErr, tc.msg.ValidateBasic() != nil, "unexpected result for tc #%d", i) if !tc.expectErr { diff --git a/x/feegrant/client/cli/tx.go b/x/feegrant/client/cli/tx.go index 2d38cd053082..e909a46999e8 100644 --- a/x/feegrant/client/cli/tx.go +++ b/x/feegrant/client/cli/tx.go @@ -58,7 +58,7 @@ Examples: %s tx %s grant cosmos1skjw... cosmos1skjw... --spend-limit 100stake --expiration 2022-01-30T15:04:05Z or %s tx %s grant cosmos1skjw... cosmos1skjw... --spend-limit 100stake --period 3600 --period-limit 10stake --expiration 36000 or %s tx %s grant cosmos1skjw... cosmos1skjw... --spend-limit 100stake --expiration 2022-01-30T15:04:05Z - --allowed-messages "/cosmos.gov.v1beta1.Msg/SubmitProposal,/cosmos.gov.v1beta1.Msg/Vote" + --allowed-messages "/cosmos.gov.v1beta1.MsgSubmitProposal,/cosmos.gov.v1beta1.MsgVote" `, version.AppName, types.ModuleName, version.AppName, types.ModuleName, version.AppName, types.ModuleName, ), ), diff --git a/x/feegrant/client/testutil/suite.go b/x/feegrant/client/testutil/suite.go index f1d1a70d4a79..7792bd8acf80 100644 --- a/x/feegrant/client/testutil/suite.go +++ b/x/feegrant/client/testutil/suite.go @@ -2,6 +2,7 @@ package testutil import ( "fmt" + "strings" "testing" "time" @@ -649,7 +650,7 @@ func (s *IntegrationTestSuite) TestFilteredFeeAllowance() { } spendLimit := sdk.NewCoin("stake", sdk.NewInt(1000)) - allowMsgs := "/cosmos.gov.v1beta1.Msg/SubmitProposal,weighted_vote" + allowMsgs := strings.Join([]string{sdk.MsgTypeURL(&govtypes.MsgSubmitProposal{}), sdk.MsgTypeURL(&govtypes.MsgVoteWeighted{})}, ",") testCases := []struct { name string diff --git a/x/feegrant/simulation/genesis.go b/x/feegrant/simulation/genesis.go index dee9b3b0fe5c..85fadcda4126 100644 --- a/x/feegrant/simulation/genesis.go +++ b/x/feegrant/simulation/genesis.go @@ -52,7 +52,7 @@ func generateRandomAllowances(granter, grantee sdk.AccAddress, r *rand.Rand) typ filteredAllowance, err := types.NewFeeAllowanceGrant(granter, grantee, &types.AllowedMsgFeeAllowance{ Allowance: basicAllowance.GetAllowance(), - AllowedMessages: []string{"/cosmos.gov.v1beta1.Msg/SubmitProposal"}, + AllowedMessages: []string{"/cosmos.gov.v1beta1.MsgSubmitProposal"}, }) if err != nil { panic(err) diff --git a/x/feegrant/simulation/operations.go b/x/feegrant/simulation/operations.go index f8c53a7092c9..b48ea9feb4d3 100644 --- a/x/feegrant/simulation/operations.go +++ b/x/feegrant/simulation/operations.go @@ -21,8 +21,11 @@ import ( const ( OpWeightMsgGrantFeeAllowance = "op_weight_msg_grant_fee_allowance" OpWeightMsgRevokeFeeAllowance = "op_weight_msg_grant_revoke_allowance" - TypeMsgGrantFeeAllowance = "/cosmos.feegrant.v1beta1.Msg/GrantFeeAllowance" - TypeMsgRevokeFeeAllowance = "/cosmos.feegrant.v1beta1.Msg/RevokeFeeAllowance" +) + +var ( + TypeMsgGrantFeeAllowance = sdk.MsgTypeURL(&types.MsgGrantFeeAllowance{}) + TypeMsgRevokeFeeAllowance = sdk.MsgTypeURL(&types.MsgRevokeFeeAllowance{}) ) func WeightedOperations( @@ -121,7 +124,7 @@ func SimulateMsgGrantFeeAllowance(ak types.AccountKeeper, bk types.BankKeeper, k _, _, err = app.Deliver(txGen.TxEncoder(), tx) if err != nil { - return simtypes.NoOpMsg(types.ModuleName, svcMsgClientConn.GetMsgs()[0].Type(), "unable to deliver tx"), nil, err + return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(svcMsgClientConn.GetMsgs()[0]), "unable to deliver tx"), nil, err } return simtypes.NewOperationMsg(svcMsgClientConn.GetMsgs()[0], true, "", protoCdc), nil, err } diff --git a/x/feegrant/types/codec.go b/x/feegrant/types/codec.go index 5557d96ac3d2..a8a0adb69508 100644 --- a/x/feegrant/types/codec.go +++ b/x/feegrant/types/codec.go @@ -8,7 +8,7 @@ import ( // RegisterInterfaces registers the interfaces types with the interface registry func RegisterInterfaces(registry types.InterfaceRegistry) { - registry.RegisterImplementations((*sdk.MsgRequest)(nil), + registry.RegisterImplementations((*sdk.Msg)(nil), &MsgGrantFeeAllowance{}, &MsgRevokeFeeAllowance{}, ) diff --git a/x/feegrant/types/filtered_fee.go b/x/feegrant/types/filtered_fee.go index 7fdbd0f21c5d..3cef6d2408b1 100644 --- a/x/feegrant/types/filtered_fee.go +++ b/x/feegrant/types/filtered_fee.go @@ -80,7 +80,7 @@ func (a *AllowedMsgFeeAllowance) allMsgTypesAllowed(ctx sdk.Context, msgs []sdk. for _, msg := range msgs { ctx.GasMeter().ConsumeGas(gasCostPerIteration, "check msg") - if !msgsMap[msg.Type()] { + if !msgsMap[sdk.MsgTypeURL(msg)] { return false } } diff --git a/x/feegrant/types/msgs.go b/x/feegrant/types/msgs.go index e346763f9200..9fb80cff8539 100644 --- a/x/feegrant/types/msgs.go +++ b/x/feegrant/types/msgs.go @@ -9,7 +9,7 @@ import ( ) var ( - _, _ sdk.MsgRequest = &MsgGrantFeeAllowance{}, &MsgRevokeFeeAllowance{} + _, _ sdk.Msg = &MsgGrantFeeAllowance{}, &MsgRevokeFeeAllowance{} _ types.UnpackInterfacesMessage = &MsgGrantFeeAllowance{} ) diff --git a/x/genutil/client/testutil/suite.go b/x/genutil/client/testutil/suite.go index a0bc047b4b4d..a1b077ab3022 100644 --- a/x/genutil/client/testutil/suite.go +++ b/x/genutil/client/testutil/suite.go @@ -15,6 +15,7 @@ import ( "github.com/cosmos/cosmos-sdk/testutil" "github.com/cosmos/cosmos-sdk/testutil/network" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -84,7 +85,7 @@ func (s *IntegrationTestSuite) TestGenTxCmd() { msgs := tx.GetMsgs() s.Require().Len(msgs, 1) - s.Require().Equal(types.TypeMsgCreateValidator, msgs[0].Type()) + s.Require().Equal(types.TypeMsgCreateValidator, msgs[0].(legacytx.LegacyMsg).Type()) s.Require().Equal([]sdk.AccAddress{val.Address}, msgs[0].GetSigners()) s.Require().Equal(amount, msgs[0].(*types.MsgCreateValidator).Value) err = tx.ValidateBasic() diff --git a/x/gov/client/utils/query.go b/x/gov/client/utils/query.go index 9b103e3468c9..c0734c0ff1dd 100644 --- a/x/gov/client/utils/query.go +++ b/x/gov/client/utils/query.go @@ -39,14 +39,14 @@ func (p Proposer) String() string { func QueryDepositsByTxQuery(clientCtx client.Context, params types.QueryProposalParams) ([]byte, error) { searchResult, err := combineEvents( clientCtx, defaultPage, - // Query old Msgs + // Query legacy Msgs event action []string{ fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgDeposit), fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalDeposit, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), }, - // Query service Msgs + // Query proto Msgs event action []string{ - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeSvcMsgDeposit), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgDeposit{})), fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalDeposit, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), }, ) @@ -58,14 +58,7 @@ func QueryDepositsByTxQuery(clientCtx client.Context, params types.QueryProposal for _, info := range searchResult.Txs { for _, msg := range info.GetTx().GetMsgs() { - var depMsg *types.MsgDeposit - if msg.Type() == types.TypeSvcMsgDeposit { - depMsg = msg.(sdk.ServiceMsg).Request.(*types.MsgDeposit) - } else if protoDepMsg, ok := msg.(*types.MsgDeposit); ok { - depMsg = protoDepMsg - } - - if depMsg != nil { + if depMsg, ok := msg.(*types.MsgDeposit); ok { deposits = append(deposits, types.Deposit{ Depositor: depMsg.Depositor, ProposalId: params.ProposalID, @@ -95,27 +88,27 @@ func QueryVotesByTxQuery(clientCtx client.Context, params types.QueryProposalVot // query interrupted either if we collected enough votes or tx indexer run out of relevant txs for len(votes) < totalLimit { - // Search for both (old) votes and weighted votes. + // Search for both (legacy) votes and weighted votes. searchResult, err := combineEvents( clientCtx, nextTxPage, - // Query old Vote Msgs + // Query legacy Vote Msgs []string{ fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgVote), fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), }, - // Query Vote service Msgs + // Query Vote proto Msgs []string{ - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeSvcMsgVote), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgVote{})), fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), }, - // Query old VoteWeighted Msgs + // Query legacy VoteWeighted Msgs []string{ fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgVoteWeighted), fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), }, - // Query VoteWeighted service Msgs + // Query VoteWeighted proto Msgs []string{ - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeSvcMsgVoteWeighted), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgVoteWeighted{})), fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), }, ) @@ -125,14 +118,7 @@ func QueryVotesByTxQuery(clientCtx client.Context, params types.QueryProposalVot for _, info := range searchResult.Txs { for _, msg := range info.GetTx().GetMsgs() { - var voteMsg *types.MsgVote - if msg.Type() == types.TypeSvcMsgVote { - voteMsg = msg.(sdk.ServiceMsg).Request.(*types.MsgVote) - } else if protoVoteMsg, ok := msg.(*types.MsgVote); ok { - voteMsg = protoVoteMsg - } - - if voteMsg != nil { + if voteMsg, ok := msg.(*types.MsgVote); ok { votes = append(votes, types.Vote{ Voter: voteMsg.Voter, ProposalId: params.ProposalID, @@ -140,14 +126,7 @@ func QueryVotesByTxQuery(clientCtx client.Context, params types.QueryProposalVot }) } - var voteWeightedMsg *types.MsgVoteWeighted - if msg.Type() == types.TypeSvcMsgVoteWeighted { - voteWeightedMsg = msg.(sdk.ServiceMsg).Request.(*types.MsgVoteWeighted) - } else if protoVoteWeightedMsg, ok := msg.(*types.MsgVoteWeighted); ok { - voteWeightedMsg = protoVoteWeightedMsg - } - - if voteWeightedMsg != nil { + if voteWeightedMsg, ok := msg.(*types.MsgVoteWeighted); ok { votes = append(votes, types.Vote{ Voter: voteWeightedMsg.Voter, ProposalId: params.ProposalID, @@ -181,27 +160,27 @@ func QueryVotesByTxQuery(clientCtx client.Context, params types.QueryProposalVot func QueryVoteByTxQuery(clientCtx client.Context, params types.QueryVoteParams) ([]byte, error) { searchResult, err := combineEvents( clientCtx, defaultPage, - // Query old Vote Msgs + // Query legacy Vote Msgs []string{ fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgVote), fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, []byte(params.Voter.String())), }, - // Query Vote service Msgs + // Query Vote proto Msgs []string{ - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeSvcMsgVote), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgVote{})), fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, []byte(params.Voter.String())), }, - // Query old VoteWeighted Msgs + // Query legacy VoteWeighted Msgs []string{ fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgVoteWeighted), fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, []byte(params.Voter.String())), }, - // Query VoteWeighted service Msgs + // Query VoteWeighted proto Msgs []string{ - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeSvcMsgVoteWeighted), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgVoteWeighted{})), fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalVote, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, []byte(params.Voter.String())), }, @@ -212,37 +191,26 @@ func QueryVoteByTxQuery(clientCtx client.Context, params types.QueryVoteParams) for _, info := range searchResult.Txs { for _, msg := range info.GetTx().GetMsgs() { - var voteMsg *types.MsgVote // there should only be a single vote under the given conditions - if msg.Type() == types.TypeSvcMsgVote { - voteMsg = msg.(sdk.ServiceMsg).Request.(*types.MsgVote) - } else if protoVoteMsg, ok := msg.(*types.MsgVote); ok { - voteMsg = protoVoteMsg - } - - if voteMsg != nil { - vote := types.Vote{ + var vote *types.Vote + if voteMsg, ok := msg.(*types.MsgVote); ok { + vote = &types.Vote{ Voter: voteMsg.Voter, ProposalId: params.ProposalID, Options: types.NewNonSplitVoteOption(voteMsg.Option), } + } - bz, err := clientCtx.JSONMarshaler.MarshalJSON(&vote) - if err != nil { - return nil, err - } - - return bz, nil - } else if msg.Type() == types.TypeMsgVoteWeighted { - voteMsg := msg.(*types.MsgVoteWeighted) - - vote := types.Vote{ - Voter: voteMsg.Voter, + if voteWeightedMsg, ok := msg.(*types.MsgVoteWeighted); ok { + vote = &types.Vote{ + Voter: voteWeightedMsg.Voter, ProposalId: params.ProposalID, - Options: voteMsg.Options, + Options: voteWeightedMsg.Options, } + } - bz, err := clientCtx.JSONMarshaler.MarshalJSON(&vote) + if vote != nil { + bz, err := clientCtx.JSONMarshaler.MarshalJSON(vote) if err != nil { return nil, err } @@ -260,15 +228,15 @@ func QueryVoteByTxQuery(clientCtx client.Context, params types.QueryVoteParams) func QueryDepositByTxQuery(clientCtx client.Context, params types.QueryDepositParams) ([]byte, error) { searchResult, err := combineEvents( clientCtx, defaultPage, - // Query old Msgs + // Query legacy Msgs event action []string{ fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgDeposit), fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalDeposit, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, []byte(params.Depositor.String())), }, - // Query service Msgs + // Query proto Msgs event action []string{ - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeSvcMsgDeposit), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgDeposit{})), fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalDeposit, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", params.ProposalID))), fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, []byte(params.Depositor.String())), }, @@ -279,15 +247,8 @@ func QueryDepositByTxQuery(clientCtx client.Context, params types.QueryDepositPa for _, info := range searchResult.Txs { for _, msg := range info.GetTx().GetMsgs() { - var depMsg *types.MsgDeposit // there should only be a single deposit under the given conditions - if msg.Type() == types.TypeSvcMsgDeposit { - depMsg = msg.(sdk.ServiceMsg).Request.(*types.MsgDeposit) - } else if protoDepMsg, ok := msg.(*types.MsgDeposit); ok { - depMsg = protoDepMsg - } - - if depMsg != nil { + if depMsg, ok := msg.(*types.MsgDeposit); ok { deposit := types.Deposit{ Depositor: depMsg.Depositor, ProposalId: params.ProposalID, @@ -313,14 +274,14 @@ func QueryProposerByTxQuery(clientCtx client.Context, proposalID uint64) (Propos searchResult, err := combineEvents( clientCtx, defaultPage, - // Query old Msgs + // Query legacy Msgs event action []string{ fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgSubmitProposal), fmt.Sprintf("%s.%s='%s'", types.EventTypeSubmitProposal, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", proposalID))), }, - // Query service Msgs + // Query proto Msgs event action []string{ - fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeSvcMsgSubmitProposal), + fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, sdk.MsgTypeURL(&types.MsgSubmitProposal{})), fmt.Sprintf("%s.%s='%s'", types.EventTypeSubmitProposal, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", proposalID))), }, ) @@ -331,13 +292,7 @@ func QueryProposerByTxQuery(clientCtx client.Context, proposalID uint64) (Propos for _, info := range searchResult.Txs { for _, msg := range info.GetTx().GetMsgs() { // there should only be a single proposal under the given conditions - if msg.Type() == types.TypeSvcMsgSubmitProposal { - subMsg := msg.(sdk.ServiceMsg).Request.(*types.MsgSubmitProposal) - - return NewProposer(proposalID, subMsg.Proposer), nil - } else if protoSubMsg, ok := msg.(*types.MsgSubmitProposal); ok { - subMsg := protoSubMsg - + if subMsg, ok := msg.(*types.MsgSubmitProposal); ok { return NewProposer(proposalID, subMsg.Proposer), nil } } @@ -367,7 +322,7 @@ func QueryProposalByID(proposalID uint64, clientCtx client.Context, queryRoute s // // Tx are indexed in tendermint via their Msgs `Type()`, which can be: // - via legacy Msgs (amino or proto), their `Type()` is a custom string, -// - via ADR-031 service msgs, their `Type()` is the protobuf FQ method name. +// - via ADR-031 proto msgs, their `Type()` is the protobuf FQ method name. // In searching for events, we search for both `Type()`s, and we use the // `combineEvents` function here to merge events. func combineEvents(clientCtx client.Context, page int, eventGroups ...[]string) (*sdk.SearchTxsResult, error) { diff --git a/x/gov/client/utils/query_test.go b/x/gov/client/utils/query_test.go index fb7d002c8583..30822c0d671c 100644 --- a/x/gov/client/utils/query_test.go +++ b/x/gov/client/utils/query_test.go @@ -13,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" "github.com/cosmos/cosmos-sdk/x/gov/client/utils" "github.com/cosmos/cosmos-sdk/x/gov/types" ) @@ -44,7 +45,7 @@ func (mock TxSearchMock) TxSearch(ctx context.Context, query string, prove bool, return nil, err } for _, msg := range sdkTx.GetMsgs() { - if msg.Type() == msgType { + if msg.(legacytx.LegacyMsg).Type() == msgType { matchingTxs = append(matchingTxs, tx) break } diff --git a/x/gov/types/msgs.go b/x/gov/types/msgs.go index 098aad437857..f1c351e6ddca 100644 --- a/x/gov/types/msgs.go +++ b/x/gov/types/msgs.go @@ -18,12 +18,6 @@ const ( TypeMsgVote = "vote" TypeMsgVoteWeighted = "weighted_vote" TypeMsgSubmitProposal = "submit_proposal" - - // These are used for querying events by action. - TypeSvcMsgDeposit = "/cosmos.gov.v1beta1.Msg/Deposit" - TypeSvcMsgVote = "/cosmos.gov.v1beta1.Msg/Vote" - TypeSvcMsgVoteWeighted = "/cosmos.gov.v1beta1.Msg/VoteWeighted" - TypeSvcMsgSubmitProposal = "/cosmos.gov.v1beta1.Msg/SubmitProposal" ) var ( diff --git a/x/staking/client/rest/query.go b/x/staking/client/rest/query.go index 172ab477b7a7..b833766c2f91 100644 --- a/x/staking/client/rest/query.go +++ b/x/staking/client/rest/query.go @@ -153,28 +153,22 @@ func delegatorTxsHandlerFn(clientCtx client.Context) http.HandlerFunc { // For each case, we search txs for both: // - legacy messages: their Type() is a custom string, e.g. "delegate" - // - service Msgs: their Type() is their FQ method name, e.g. "/cosmos.staking.v1beta1.Msg/Delegate" + // - service Msgs: their Type() is their FQ method name, e.g. "/cosmos.staking.v1beta1.MsgDelegate" // and we combine the results. switch { case isBondTx: actions = append(actions, types.TypeMsgDelegate) - actions = append(actions, types.TypeSvcMsgDelegate) case isUnbondTx: actions = append(actions, types.TypeMsgUndelegate) - actions = append(actions, types.TypeSvcMsgUndelegate) case isRedTx: actions = append(actions, types.TypeMsgBeginRedelegate) - actions = append(actions, types.TypeSvcMsgBeginRedelegate) case noQuery: actions = append(actions, types.TypeMsgDelegate) - actions = append(actions, types.TypeSvcMsgDelegate) actions = append(actions, types.TypeMsgUndelegate) - actions = append(actions, types.TypeSvcMsgUndelegate) actions = append(actions, types.TypeMsgBeginRedelegate) - actions = append(actions, types.TypeSvcMsgBeginRedelegate) default: w.WriteHeader(http.StatusNoContent) diff --git a/x/staking/types/authz.go b/x/staking/types/authz.go index 32e8b719d1f2..45e0e4088d11 100644 --- a/x/staking/types/authz.go +++ b/x/staking/types/authz.go @@ -12,10 +12,6 @@ const gasCostPerIteration = uint64(10) var ( _ authz.Authorization = &StakeAuthorization{} - - TypeDelegate = "/cosmos.staking.v1beta1.Msg/Delegate" - TypeUndelegate = "/cosmos.staking.v1beta1.Msg/Undelegate" - TypeBeginRedelegate = "/cosmos.staking.v1beta1.Msg/BeginRedelegate" ) // NewStakeAuthorization creates a new StakeAuthorization object. @@ -61,11 +57,11 @@ func (authorization StakeAuthorization) ValidateBasic() error { } // Accept implements Authorization.Accept. -func (authorization StakeAuthorization) Accept(ctx sdk.Context, msg sdk.ServiceMsg) (updated authz.Authorization, delete bool, err error) { +func (authorization StakeAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (updated authz.Authorization, delete bool, err error) { var validatorAddress string var amount sdk.Coin - switch msg := msg.Request.(type) { + switch msg := msg.(type) { case *MsgDelegate: validatorAddress = msg.ValidatorAddress amount = msg.Amount @@ -142,11 +138,11 @@ func validateAndBech32fy(allowed []sdk.ValAddress, denied []sdk.ValAddress) ([]s func normalizeAuthzType(authzType AuthorizationType) (string, error) { switch authzType { case AuthorizationType_AUTHORIZATION_TYPE_DELEGATE: - return TypeDelegate, nil + return sdk.MsgTypeURL(&MsgDelegate{}), nil case AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE: - return TypeUndelegate, nil + return sdk.MsgTypeURL(&MsgUndelegate{}), nil case AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE: - return TypeBeginRedelegate, nil + return sdk.MsgTypeURL(&MsgBeginRedelegate{}), nil default: return "", sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "unknown authorization type %T", authzType) } diff --git a/x/staking/types/authz_test.go b/x/staking/types/authz_test.go index c43ef9e1fb48..31b95b702283 100644 --- a/x/staking/types/authz_test.go +++ b/x/staking/types/authz_test.go @@ -3,9 +3,8 @@ package types_test import ( "testing" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" @@ -33,7 +32,7 @@ func TestAuthzAuthorizations(t *testing.T) { // verify MethodName delAuth, err = stakingtypes.NewStakeAuthorization([]sdk.ValAddress{val1, val2}, []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, &coin100) require.NoError(t, err) - require.Equal(t, delAuth.MethodName(), stakingtypes.TypeDelegate) + require.Equal(t, delAuth.MethodName(), sdk.MsgTypeURL(&stakingtypes.MsgDelegate{})) // error both allow & deny list _, err = stakingtypes.NewStakeAuthorization([]sdk.ValAddress{val1, val2}, []sdk.ValAddress{val1}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, &coin100) @@ -41,11 +40,11 @@ func TestAuthzAuthorizations(t *testing.T) { // verify MethodName undelAuth, _ := stakingtypes.NewStakeAuthorization([]sdk.ValAddress{val1, val2}, []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE, &coin100) - require.Equal(t, undelAuth.MethodName(), stakingtypes.TypeUndelegate) + require.Equal(t, undelAuth.MethodName(), sdk.MsgTypeURL(&stakingtypes.MsgUndelegate{})) // verify MethodName beginRedelAuth, _ := stakingtypes.NewStakeAuthorization([]sdk.ValAddress{val1, val2}, []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE, &coin100) - require.Equal(t, beginRedelAuth.MethodName(), stakingtypes.TypeBeginRedelegate) + require.Equal(t, beginRedelAuth.MethodName(), sdk.MsgTypeURL(&stakingtypes.MsgBeginRedelegate{})) validators1_2 := []string{val1.String(), val2.String()} @@ -55,7 +54,7 @@ func TestAuthzAuthorizations(t *testing.T) { denied []sdk.ValAddress msgType stakingtypes.AuthorizationType limit *sdk.Coin - srvMsg sdk.ServiceMsg + srvMsg sdk.Msg expectErr bool isDelete bool updatedAuthorization *stakingtypes.StakeAuthorization @@ -66,7 +65,7 @@ func TestAuthzAuthorizations(t *testing.T) { []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, &coin100, - createSrvMsgDelegate(delAuth.MethodName(), delAddr, val1, coin100), + stakingtypes.NewMsgDelegate(delAddr, val1, coin100), false, true, nil, @@ -77,7 +76,7 @@ func TestAuthzAuthorizations(t *testing.T) { []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, &coin100, - createSrvMsgDelegate(delAuth.MethodName(), delAddr, val1, coin50), + stakingtypes.NewMsgDelegate(delAddr, val1, coin50), false, false, &stakingtypes.StakeAuthorization{ @@ -91,7 +90,7 @@ func TestAuthzAuthorizations(t *testing.T) { []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, &coin100, - createSrvMsgDelegate(delAuth.MethodName(), delAddr, val3, coin100), + stakingtypes.NewMsgDelegate(delAddr, val3, coin100), true, false, nil, @@ -102,7 +101,7 @@ func TestAuthzAuthorizations(t *testing.T) { []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, nil, - createSrvMsgDelegate(delAuth.MethodName(), delAddr, val2, coin100), + stakingtypes.NewMsgDelegate(delAddr, val2, coin100), false, false, &stakingtypes.StakeAuthorization{ @@ -116,7 +115,7 @@ func TestAuthzAuthorizations(t *testing.T) { []sdk.ValAddress{val1}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, nil, - createSrvMsgDelegate(delAuth.MethodName(), delAddr, val1, coin100), + stakingtypes.NewMsgDelegate(delAddr, val1, coin100), true, false, nil, @@ -128,7 +127,7 @@ func TestAuthzAuthorizations(t *testing.T) { []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE, &coin100, - createSrvMsgUndelegate(undelAuth.MethodName(), delAddr, val1, coin100), + stakingtypes.NewMsgUndelegate(delAddr, val1, coin100), false, true, nil, @@ -139,7 +138,7 @@ func TestAuthzAuthorizations(t *testing.T) { []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE, &coin100, - createSrvMsgUndelegate(undelAuth.MethodName(), delAddr, val1, coin50), + stakingtypes.NewMsgUndelegate(delAddr, val1, coin50), false, false, &stakingtypes.StakeAuthorization{ @@ -153,7 +152,7 @@ func TestAuthzAuthorizations(t *testing.T) { []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE, &coin100, - createSrvMsgUndelegate(undelAuth.MethodName(), delAddr, val3, coin100), + stakingtypes.NewMsgUndelegate(delAddr, val3, coin100), true, false, nil, @@ -164,7 +163,7 @@ func TestAuthzAuthorizations(t *testing.T) { []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE, nil, - createSrvMsgUndelegate(undelAuth.MethodName(), delAddr, val2, coin100), + stakingtypes.NewMsgUndelegate(delAddr, val2, coin100), false, false, &stakingtypes.StakeAuthorization{ @@ -178,7 +177,7 @@ func TestAuthzAuthorizations(t *testing.T) { []sdk.ValAddress{val1}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE, &coin100, - createSrvMsgUndelegate(undelAuth.MethodName(), delAddr, val1, coin100), + stakingtypes.NewMsgUndelegate(delAddr, val1, coin100), true, false, nil, @@ -190,7 +189,7 @@ func TestAuthzAuthorizations(t *testing.T) { []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE, &coin100, - createSrvMsgUndelegate(undelAuth.MethodName(), delAddr, val1, coin100), + stakingtypes.NewMsgUndelegate(delAddr, val1, coin100), false, true, nil, @@ -201,7 +200,7 @@ func TestAuthzAuthorizations(t *testing.T) { []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE, &coin100, - createSrvMsgReDelegate(undelAuth.MethodName(), delAddr, val1, coin50), + stakingtypes.NewMsgBeginRedelegate(delAddr, val1, val1, coin50), false, false, &stakingtypes.StakeAuthorization{ @@ -215,7 +214,7 @@ func TestAuthzAuthorizations(t *testing.T) { []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE, &coin100, - createSrvMsgReDelegate(undelAuth.MethodName(), delAddr, val3, coin100), + stakingtypes.NewMsgBeginRedelegate(delAddr, val3, val3, coin100), true, false, nil, @@ -226,7 +225,7 @@ func TestAuthzAuthorizations(t *testing.T) { []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE, nil, - createSrvMsgReDelegate(undelAuth.MethodName(), delAddr, val2, coin100), + stakingtypes.NewMsgBeginRedelegate(delAddr, val2, val2, coin100), false, false, &stakingtypes.StakeAuthorization{ @@ -240,7 +239,7 @@ func TestAuthzAuthorizations(t *testing.T) { []sdk.ValAddress{val1}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE, &coin100, - createSrvMsgReDelegate(undelAuth.MethodName(), delAddr, val1, coin100), + stakingtypes.NewMsgBeginRedelegate(delAddr, val1, val1, coin100), true, false, nil, @@ -266,27 +265,3 @@ func TestAuthzAuthorizations(t *testing.T) { }) } } - -func createSrvMsgUndelegate(methodName string, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) sdk.ServiceMsg { - msg := stakingtypes.NewMsgUndelegate(delAddr, valAddr, amount) - return sdk.ServiceMsg{ - MethodName: methodName, - Request: msg, - } -} - -func createSrvMsgReDelegate(methodName string, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) sdk.ServiceMsg { - msg := stakingtypes.NewMsgBeginRedelegate(delAddr, valAddr, valAddr, amount) - return sdk.ServiceMsg{ - MethodName: methodName, - Request: msg, - } -} - -func createSrvMsgDelegate(methodName string, delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) sdk.ServiceMsg { - msg := stakingtypes.NewMsgDelegate(delAddr, valAddr, amount) - return sdk.ServiceMsg{ - MethodName: methodName, - Request: msg, - } -} diff --git a/x/staking/types/msg.go b/x/staking/types/msg.go index e397046e5779..2525aa66468f 100644 --- a/x/staking/types/msg.go +++ b/x/staking/types/msg.go @@ -16,13 +16,6 @@ const ( TypeMsgCreateValidator = "create_validator" TypeMsgDelegate = "delegate" TypeMsgBeginRedelegate = "begin_redelegate" - - // These are used for querying events by action. - TypeSvcMsgUndelegate = "/cosmos.staking.v1beta1.Msg/Undelegate" - TypeSvcMsgEditValidator = "/cosmos.staking.v1beta1.Msg/EditValidator" - TypeSvcMsgCreateValidator = "/cosmos.staking.v1beta1.Msg/CreateValidator" - TypeSvcMsgDelegate = "/cosmos.staking.v1beta1.Msg/Delegate" - TypeSvcMsgBeginRedelegate = "/cosmos.staking.v1beta1.Msg/BeginRedelegate" ) var (