Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ADR 031 BaseApp and codec infrastructure #7519

Merged
merged 59 commits into from
Oct 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
64c76d2
Refactor RegisterQueryServices -> RegisterServices
aaronc Oct 12, 2020
d9b8a1d
Cleaner proto files
amaury1093 Oct 12, 2020
889025b
Fix tests
aaronc Oct 12, 2020
b3c301d
Merge branch 'aaronc/7500-register-services' of ssh://github.com/cosm…
amaury1093 Oct 12, 2020
e1482f2
Add MsgServer
amaury1093 Oct 12, 2020
6c77f06
Fix lint
amaury1093 Oct 12, 2020
c746a2b
Remove MsgServer from configurator for now
amaury1093 Oct 12, 2020
ac713d2
Merge branch 'master' of ssh://github.com/cosmos/cosmos-sdk into am-7…
amaury1093 Oct 12, 2020
59ebc3b
Remove useless file
amaury1093 Oct 12, 2020
f18f4e3
Fix build
amaury1093 Oct 12, 2020
1ed29df
typo
amaury1093 Oct 12, 2020
0d9efc4
Add router
amaury1093 Oct 13, 2020
be1a26b
Fix test
amaury1093 Oct 13, 2020
b35ce5d
Merge branch 'master' of ssh://github.com/cosmos/cosmos-sdk into am-7…
amaury1093 Oct 13, 2020
fef78cf
WIP
amaury1093 Oct 13, 2020
ff05343
Add router
amaury1093 Oct 13, 2020
c323889
Merge branch 'master' of ssh://github.com/cosmos/cosmos-sdk into am-7…
amaury1093 Oct 13, 2020
13b8dbd
Remove test helper
amaury1093 Oct 13, 2020
ab00ebf
Add beginning of test
amaury1093 Oct 13, 2020
797ddd3
Move test to simapp?
amaury1093 Oct 13, 2020
a65ab55
ServiceMsg implement sdk.Msg
amaury1093 Oct 13, 2020
759b2a5
Add handler by MsgServiceRouter
amaury1093 Oct 13, 2020
931ca66
Correct signature
amaury1093 Oct 13, 2020
f21b332
Add full test
amaury1093 Oct 13, 2020
24fa6f0
use TxEncoder
amaury1093 Oct 13, 2020
12cbcec
Update baseapp/msg_service_router.go
amaury1093 Oct 13, 2020
b599b65
Push changes
amaury1093 Oct 13, 2020
0c5ce17
WIP on ServiceMsg unpacking
aaronc Oct 13, 2020
14619c4
Make TestMsgService test pass
aaronc Oct 13, 2020
687fc09
Fix tests
aaronc Oct 13, 2020
b6bec3a
Tidying up
aaronc Oct 13, 2020
d89f2f6
Tidying up
aaronc Oct 13, 2020
82e2521
Tidying up
aaronc Oct 13, 2020
42f59ff
Add JSON test
aaronc Oct 13, 2020
9a038a5
Add comments
aaronc Oct 13, 2020
8c56444
Tidying
aaronc Oct 13, 2020
a6a8132
Merge branch 'master' into am-7500-servicemsg
aaronc Oct 13, 2020
aa3192f
Lint
aaronc Oct 13, 2020
55577f6
Register MsgRequest interface
aaronc Oct 13, 2020
701f879
Rename
aaronc Oct 13, 2020
84033e4
Fix tests
aaronc Oct 13, 2020
3762e1d
Merge branch 'master' of ssh://github.com/cosmos/cosmos-sdk into am-7…
amaury1093 Oct 14, 2020
b30a5eb
RegisterCustomTypeURL
amaury1093 Oct 14, 2020
715d002
Add changelog entries
amaury1093 Oct 14, 2020
791097a
Put in features
amaury1093 Oct 14, 2020
62a887f
Update baseapp/msg_service_router.go
amaury1093 Oct 14, 2020
98fa5d2
Update baseapp/msg_service_router.go
amaury1093 Oct 14, 2020
c63105d
Update baseapp/msg_service_router.go
amaury1093 Oct 14, 2020
2be1a61
Update baseapp/msg_service_router.go
amaury1093 Oct 14, 2020
105b04a
Update baseapp/msg_service_router.go
amaury1093 Oct 14, 2020
c306dff
Update baseapp/msg_service_router.go
amaury1093 Oct 14, 2020
eaebd7e
Update baseapp/msg_service_router.go
amaury1093 Oct 14, 2020
7cbc9f9
Address review comments
amaury1093 Oct 14, 2020
dfb6cf6
Merge branch 'master' into am-7500-servicemsg
amaury1093 Oct 14, 2020
8624ad7
Address nit
amaury1093 Oct 14, 2020
880c7cb
Fix lint
amaury1093 Oct 14, 2020
304e937
Update codec/types/interface_registry.go
amaury1093 Oct 15, 2020
ca9258c
godoc
amaury1093 Oct 15, 2020
d6120af
Merge branch 'master' into am-7500-servicemsg
alexanderbez Oct 15, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ Ref: https://keepachangelog.com/en/1.0.0/

* (x/staking) [\#7499](https://github.com/cosmos/cosmos-sdk/pull/7499) `BondStatus` is now a protobuf `enum` instead of an `int32`, and JSON serialized using its protobuf name, so expect names like `BOND_STATUS_UNBONDING` as opposed to `Unbonding`.

### API Breaking

* (AppModule) [\#7518](https://github.com/cosmos/cosmos-sdk/pull/7518) Rename `AppModule.RegisterQueryServices` to `AppModule.RegisterServices`, as this method now registers multiple services (the gRPC query service and the protobuf Msg service). A `Configurator` struct is used to hold the different services.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not introduced in this PR, but in #7518 (comment)


### Features

* (codec) [\#7519](https://github.com/cosmos/cosmos-sdk/pull/7519) `InterfaceRegistry` now inherits `jsonpb.AnyResolver`, and has a `RegisterCustomTypeURL` method to support ADR 031 packing of `Any`s. `AnyResolver` is now a required parameter to `RejectUnknownFields`.
* (baseapp) [\#7519](https://github.com/cosmos/cosmos-sdk/pull/7519) Add `ServiceMsgRouter` to BaseApp to handle routing of protobuf service `Msg`s. The two new types defined in ADR 031, `sdk.ServiceMsg` and `sdk.MsgRequest` are introduced with this router.
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved

### Bug Fixes

* (kvstore) [\#7415](https://github.com/cosmos/cosmos-sdk/pull/7415) Allow new stores to be registered during on-chain upgrades.
Expand Down
2 changes: 1 addition & 1 deletion baseapp/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.Res
Result: res,
}

bz, err := codec.ProtoMarshalJSON(simRes)
bz, err := codec.ProtoMarshalJSON(simRes, app.interfaceRegistry)
if err != nil {
return sdkerrors.QueryResult(sdkerrors.Wrap(err, "failed to JSON encode simulation response"))
}
Expand Down
77 changes: 51 additions & 26 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
dbm "github.com/tendermint/tm-db"

"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/snapshots"
"github.com/cosmos/cosmos-sdk/store"
"github.com/cosmos/cosmos-sdk/store/rootmulti"
Expand Down Expand Up @@ -45,15 +46,17 @@ type (
// BaseApp reflects the ABCI application implementation.
type BaseApp struct { // nolint: maligned
// initialized on creation
logger log.Logger
name string // application name from abci.Info
db dbm.DB // common DB backend
cms sdk.CommitMultiStore // Main (uncached) state
storeLoader StoreLoader // function to handle store loading, may be overridden with SetStoreLoader()
router sdk.Router // handle any kind of message
queryRouter sdk.QueryRouter // router for redirecting query calls
grpcQueryRouter *GRPCQueryRouter // router for redirecting gRPC query calls
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx
logger log.Logger
name string // application name from abci.Info
db dbm.DB // common DB backend
cms sdk.CommitMultiStore // Main (uncached) state
storeLoader StoreLoader // function to handle store loading, may be overridden with SetStoreLoader()
router sdk.Router // handle any kind of message
queryRouter sdk.QueryRouter // router for redirecting query calls
grpcQueryRouter *GRPCQueryRouter // router for redirecting gRPC query calls
msgServiceRouter *MsgServiceRouter // router for redirecting Msg service messages
interfaceRegistry types.InterfaceRegistry
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx

anteHandler sdk.AnteHandler // ante handler for fee and auth
initChainer sdk.InitChainer // initialize state with validators and state blob
Expand Down Expand Up @@ -136,16 +139,17 @@ func NewBaseApp(
name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, options ...func(*BaseApp),
) *BaseApp {
app := &BaseApp{
logger: logger,
name: name,
db: db,
cms: store.NewCommitMultiStore(db),
storeLoader: DefaultStoreLoader,
router: NewRouter(),
queryRouter: NewQueryRouter(),
grpcQueryRouter: NewGRPCQueryRouter(),
txDecoder: txDecoder,
fauxMerkleMode: false,
logger: logger,
name: name,
db: db,
cms: store.NewCommitMultiStore(db),
storeLoader: DefaultStoreLoader,
router: NewRouter(),
queryRouter: NewQueryRouter(),
grpcQueryRouter: NewGRPCQueryRouter(),
msgServiceRouter: NewMsgServiceRouter(),
txDecoder: txDecoder,
fauxMerkleMode: false,
}

for _, option := range options {
Expand Down Expand Up @@ -176,6 +180,9 @@ func (app *BaseApp) Logger() log.Logger {
return app.logger
}

// MsgServiceRouter returns the MsgServiceRouter of a BaseApp.
func (app *BaseApp) MsgServiceRouter() *MsgServiceRouter { return app.msgServiceRouter }

// MountStores mounts all IAVL or DB stores to the provided keys in the BaseApp
// multistore.
func (app *BaseApp) MountStores(keys ...sdk.StoreKey) {
Expand Down Expand Up @@ -682,20 +689,38 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (*s
break
}

msgRoute := msg.Route()
handler := app.router.Route(ctx, msgRoute)
var (
msgEvents sdk.Events
msgResult *sdk.Result
msgFqName string
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 {
// legacy sdk.Msg routing
msgRoute := msg.Route()
msgFqName = msg.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)
}

if handler == nil {
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized message route: %s; message index: %d", msgRoute, i)
msgResult, err = handler(ctx, msg)
}

msgResult, err := handler(ctx, 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, msg.Type())),
msgEvents = sdk.Events{
sdk.NewEvent(sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyAction, msgFqName)),
}
msgEvents = msgEvents.AppendEvents(msgResult.GetEvents())

Expand Down
6 changes: 3 additions & 3 deletions baseapp/baseapp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1692,9 +1692,9 @@ func TestQuery(t *testing.T) {

func TestGRPCQuery(t *testing.T) {
grpcQueryOpt := func(bapp *BaseApp) {
testdata.RegisterTestServiceServer(
testdata.RegisterQueryServer(
bapp.GRPCQueryRouter(),
testdata.TestServiceImpl{},
testdata.QueryImpl{},
)
}

Expand All @@ -1711,7 +1711,7 @@ func TestGRPCQuery(t *testing.T) {

reqQuery := abci.RequestQuery{
Data: reqBz,
Path: "/testdata.TestService/SayHello",
Path: "/testdata.Query/SayHello",
}

resQuery := app.Query(reqQuery)
Expand Down
11 changes: 6 additions & 5 deletions baseapp/grpcrouter_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ import (
gocontext "context"
"fmt"

"github.com/cosmos/cosmos-sdk/codec/types"

gogogrpc "github.com/gogo/protobuf/grpc"
abci "github.com/tendermint/tendermint/abci/types"
"google.golang.org/grpc"

"github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)

Expand All @@ -22,6 +21,11 @@ type QueryServiceTestHelper struct {
ctx sdk.Context
}

var (
_ gogogrpc.Server = &QueryServiceTestHelper{}
_ gogogrpc.ClientConn = &QueryServiceTestHelper{}
)

// NewQueryServerTestHelper creates a new QueryServiceTestHelper that wraps
// the provided sdk.Context
func NewQueryServerTestHelper(ctx sdk.Context, interfaceRegistry types.InterfaceRegistry) *QueryServiceTestHelper {
Expand Down Expand Up @@ -62,6 +66,3 @@ func (q *QueryServiceTestHelper) Invoke(_ gocontext.Context, method string, args
func (q *QueryServiceTestHelper) NewStream(gocontext.Context, *grpc.StreamDesc, string, ...grpc.CallOption) (grpc.ClientStream, error) {
return nil, fmt.Errorf("not supported")
}

var _ gogogrpc.Server = &QueryServiceTestHelper{}
var _ gogogrpc.ClientConn = &QueryServiceTestHelper{}
4 changes: 2 additions & 2 deletions baseapp/grpcrouter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ func TestGRPCRouter(t *testing.T) {
qr := NewGRPCQueryRouter()
interfaceRegistry := testdata.NewTestInterfaceRegistry()
qr.SetInterfaceRegistry(interfaceRegistry)
testdata.RegisterTestServiceServer(qr, testdata.TestServiceImpl{})
testdata.RegisterQueryServer(qr, testdata.QueryImpl{})
helper := &QueryServiceTestHelper{
GRPCQueryRouter: qr,
ctx: sdk.Context{}.WithContext(context.Background()),
}
client := testdata.NewTestServiceClient(helper)
client := testdata.NewQueryClient(helper)

res, err := client.Echo(context.Background(), &testdata.EchoRequest{Message: "hello"})
require.Nil(t, err)
Expand Down
94 changes: 94 additions & 0 deletions baseapp/msg_service_router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package baseapp

import (
"context"
"fmt"

"github.com/gogo/protobuf/proto"

gogogrpc "github.com/gogo/protobuf/grpc"
"google.golang.org/grpc"

codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)

// MsgServiceRouter routes fully-qualified Msg service methods to their handler.
type MsgServiceRouter struct {
interfaceRegistry codectypes.InterfaceRegistry
routes map[string]MsgServiceHandler
}

var _ gogogrpc.Server = &MsgServiceRouter{}

// NewMsgServiceRouter creates a new MsgServiceRouter.
func NewMsgServiceRouter() *MsgServiceRouter {
return &MsgServiceRouter{
routes: map[string]MsgServiceHandler{},
}
}

// MsgServiceHandler defines a function type which handles Msg service message.
type MsgServiceHandler = func(ctx sdk.Context, req sdk.MsgRequest) (*sdk.Result, error)

// Handler 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]
}

// RegisterService implements the gRPC Server.RegisterService method. sd is a gRPC
// service description, handler is an object which implements that gRPC service.
func (msr *MsgServiceRouter) RegisterService(sd *grpc.ServiceDesc, handler interface{}) {
// Adds a top-level query handler based on the gRPC service name.
for _, method := range sd.Methods {
fqMethod := fmt.Sprintf("/%s/%s", sd.ServiceName, method.MethodName)
methodHandler := method.Handler

// 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.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

_, _ = methodHandler(nil, context.Background(), func(i interface{}) error {
msg, ok := i.(proto.Message)
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))
}

msr.interfaceRegistry.RegisterCustomTypeURL((*sdk.MsgRequest)(nil), fqMethod, msg)
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
return nil
}, func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
return nil, nil
})

msr.routes[fqMethod] = func(ctx sdk.Context, req sdk.MsgRequest) (*sdk.Result, error) {
ctx = ctx.WithEventManager(sdk.NewEventManager())

// Call the method handler from the service description with the handler object.
res, err := methodHandler(handler, sdk.WrapSDKContext(ctx), func(_ interface{}) error {
// We don't do any decoding here because the decoding was already done.
return nil
}, func(goCtx context.Context, _ interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
goCtx = context.WithValue(goCtx, sdk.SdkContextKey, ctx)
return handler(goCtx, req)
})
if err != nil {
return nil, err
}

resMsg, ok := res.(proto.Message)
if !ok {
return nil, fmt.Errorf("can't proto encode %T", resMsg)
}

return sdk.WrapServiceResult(ctx, resMsg, err)
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

// SetInterfaceRegistry sets the interface registry for the router.
func (msr *MsgServiceRouter) SetInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) {
msr.interfaceRegistry = interfaceRegistry
}
74 changes: 74 additions & 0 deletions baseapp/msg_service_router_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package baseapp_test

import (
"os"
"testing"

"github.com/cosmos/cosmos-sdk/baseapp"

tmproto "github.com/tendermint/tendermint/proto/tendermint/types"

"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
dbm "github.com/tendermint/tm-db"

"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
)

func TestMsgService(t *testing.T) {
priv, _, _ := testdata.KeyTestPubAddr()
encCfg := simapp.MakeEncodingConfig()
db := dbm.NewMemDB()
app := baseapp.NewBaseApp("test", log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, encCfg.TxConfig.TxDecoder())
app.SetInterfaceRegistry(encCfg.InterfaceRegistry)
testdata.RegisterMsgServer(
app.MsgServiceRouter(),
testdata.MsgServerImpl{},
)
_ = app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: 1}})

msg := testdata.NewServiceMsgCreateDog(&testdata.MsgCreateDog{Dog: &testdata.Dog{Name: "Spot"}})
txBuilder := encCfg.TxConfig.NewTxBuilder()
txBuilder.SetFeeAmount(testdata.NewTestFeeAmount())
txBuilder.SetGasLimit(testdata.NewTestGasLimit())
err := txBuilder.SetMsgs(msg)
require.NoError(t, err)

// First round: we gather all the signer infos. We use the "set empty
// signature" hack to do that.
sigV2 := signing.SignatureV2{
PubKey: priv.PubKey(),
Data: &signing.SingleSignatureData{
SignMode: encCfg.TxConfig.SignModeHandler().DefaultMode(),
Signature: nil,
},
Sequence: 0,
}

err = txBuilder.SetSignatures(sigV2)
require.NoError(t, err)

// Second round: all signer infos are set, so each signer can sign.
signerData := authsigning.SignerData{
ChainID: "test",
AccountNumber: 0,
Sequence: 0,
}
sigV2, err = tx.SignWithPrivKey(
encCfg.TxConfig.SignModeHandler().DefaultMode(), signerData,
txBuilder, priv, encCfg.TxConfig, 0)
require.NoError(t, err)
err = txBuilder.SetSignatures(sigV2)
require.NoError(t, err)

// Send the tx to the app
txBytes, err := encCfg.TxConfig.TxEncoder()(txBuilder.GetTx())
require.NoError(t, err)
res := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
require.Equal(t, abci.CodeTypeOK, res.Code, "res=%+v", res)
}
Loading