-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ADR 031 BaseApp and codec infrastructure (#7519)
* Refactor RegisterQueryServices -> RegisterServices * Cleaner proto files * Fix tests * Add MsgServer * Fix lint * Remove MsgServer from configurator for now * Remove useless file * Fix build * typo * Add router * Fix test * WIP * Add router * Remove test helper * Add beginning of test * Move test to simapp? * ServiceMsg implement sdk.Msg * Add handler by MsgServiceRouter * Correct signature * Add full test * use TxEncoder * Update baseapp/msg_service_router.go Co-authored-by: Aaron Craelius <aaron@regen.network> * Push changes * WIP on ServiceMsg unpacking * Make TestMsgService test pass * Fix tests * Tidying up * Tidying up * Tidying up * Add JSON test * Add comments * Tidying * Lint * Register MsgRequest interface * Rename * Fix tests * RegisterCustomTypeURL * Add changelog entries * Put in features * Update baseapp/msg_service_router.go Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com> * Update baseapp/msg_service_router.go Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com> * Update baseapp/msg_service_router.go Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com> * Update baseapp/msg_service_router.go Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com> * Update baseapp/msg_service_router.go Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com> * Update baseapp/msg_service_router.go Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com> * Update baseapp/msg_service_router.go Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com> * Address review comments * Address nit * Fix lint * Update codec/types/interface_registry.go Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com> * godoc Co-authored-by: Aaron Craelius <aaronc@users.noreply.github.com> Co-authored-by: Aaron Craelius <aaron@regen.network> Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com> Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com>
- Loading branch information
Showing
48 changed files
with
8,971 additions
and
7,552 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | ||
_, _ = 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) | ||
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) | ||
} | ||
} | ||
} | ||
|
||
// SetInterfaceRegistry sets the interface registry for the router. | ||
func (msr *MsgServiceRouter) SetInterfaceRegistry(interfaceRegistry codectypes.InterfaceRegistry) { | ||
msr.interfaceRegistry = interfaceRegistry | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
Oops, something went wrong.