cosmos-sdk/server/grpc/reflection/v2alpha1/reflection.go

202 lines
7.2 KiB
Go

package v2alpha1
import (
"context"
"fmt"
"github.com/gogo/protobuf/proto"
"google.golang.org/grpc"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx"
)
type Config struct {
SigningModes map[string]int32
ChainID string
SdkConfig *sdk.Config
InterfaceRegistry codectypes.InterfaceRegistry
}
// Register registers the cosmos sdk reflection service
// to the provided *grpc.Server given a Config
func Register(srv *grpc.Server, conf Config) error {
reflectionServer, err := newReflectionServiceServer(srv, conf)
if err != nil {
return err
}
RegisterReflectionServiceServer(srv, reflectionServer)
return nil
}
type reflectionServiceServer struct {
desc *AppDescriptor
}
func (r reflectionServiceServer) GetAuthnDescriptor(_ context.Context, _ *GetAuthnDescriptorRequest) (*GetAuthnDescriptorResponse, error) {
return &GetAuthnDescriptorResponse{Authn: r.desc.Authn}, nil
}
func (r reflectionServiceServer) GetChainDescriptor(_ context.Context, _ *GetChainDescriptorRequest) (*GetChainDescriptorResponse, error) {
return &GetChainDescriptorResponse{Chain: r.desc.Chain}, nil
}
func (r reflectionServiceServer) GetCodecDescriptor(_ context.Context, _ *GetCodecDescriptorRequest) (*GetCodecDescriptorResponse, error) {
return &GetCodecDescriptorResponse{Codec: r.desc.Codec}, nil
}
func (r reflectionServiceServer) GetConfigurationDescriptor(_ context.Context, _ *GetConfigurationDescriptorRequest) (*GetConfigurationDescriptorResponse, error) {
return &GetConfigurationDescriptorResponse{Config: r.desc.Configuration}, nil
}
func (r reflectionServiceServer) GetQueryServicesDescriptor(_ context.Context, _ *GetQueryServicesDescriptorRequest) (*GetQueryServicesDescriptorResponse, error) {
return &GetQueryServicesDescriptorResponse{Queries: r.desc.QueryServices}, nil
}
func (r reflectionServiceServer) GetTxDescriptor(_ context.Context, _ *GetTxDescriptorRequest) (*GetTxDescriptorResponse, error) {
return &GetTxDescriptorResponse{Tx: r.desc.Tx}, nil
}
func newReflectionServiceServer(grpcSrv *grpc.Server, conf Config) (reflectionServiceServer, error) {
// set chain descriptor
chainDescriptor := &ChainDescriptor{Id: conf.ChainID}
// set configuration descriptor
configurationDescriptor := &ConfigurationDescriptor{
Bech32AccountAddressPrefix: conf.SdkConfig.GetBech32AccountAddrPrefix(),
}
// set codec descriptor
codecDescriptor, err := newCodecDescriptor(conf.InterfaceRegistry)
if err != nil {
return reflectionServiceServer{}, fmt.Errorf("unable to create codec descriptor: %w", err)
}
// set query service descriptor
queryServiceDescriptor := newQueryServiceDescriptor(grpcSrv)
// set deliver descriptor
txDescriptor, err := newTxDescriptor(conf.InterfaceRegistry)
if err != nil {
return reflectionServiceServer{}, fmt.Errorf("unable to create deliver descriptor: %w", err)
}
authnDescriptor := newAuthnDescriptor(conf.SigningModes)
desc := &AppDescriptor{
Authn: authnDescriptor,
Chain: chainDescriptor,
Codec: codecDescriptor,
Configuration: configurationDescriptor,
QueryServices: queryServiceDescriptor,
Tx: txDescriptor,
}
ifaceList := make([]string, len(desc.Codec.Interfaces))
ifaceImplementers := make(map[string][]string, len(desc.Codec.Interfaces))
for i, iface := range desc.Codec.Interfaces {
ifaceList[i] = iface.Fullname
impls := make([]string, len(iface.InterfaceImplementers))
for j, impl := range iface.InterfaceImplementers {
impls[j] = impl.TypeUrl
}
ifaceImplementers[iface.Fullname] = impls
}
return reflectionServiceServer{
desc: desc,
}, nil
}
// newCodecDescriptor describes the codec given the codectypes.InterfaceRegistry
func newCodecDescriptor(ir codectypes.InterfaceRegistry) (*CodecDescriptor, error) {
registeredInterfaces := ir.ListAllInterfaces()
interfaceDescriptors := make([]*InterfaceDescriptor, len(registeredInterfaces))
for i, iface := range registeredInterfaces {
implementers := ir.ListImplementations(iface)
interfaceImplementers := make([]*InterfaceImplementerDescriptor, len(implementers))
for j, implementer := range implementers {
pb, err := ir.Resolve(implementer)
if err != nil {
return nil, fmt.Errorf("unable to resolve implementing type %s for interface %s", implementer, iface)
}
pbName := proto.MessageName(pb)
if pbName == "" {
return nil, fmt.Errorf("unable to get proto name for implementing type %s for interface %s", implementer, iface)
}
interfaceImplementers[j] = &InterfaceImplementerDescriptor{
Fullname: pbName,
TypeUrl: implementer,
}
}
interfaceDescriptors[i] = &InterfaceDescriptor{
Fullname: iface,
// NOTE(fdymylja): this could be filled, but it won't be filled as of now
// doing this would require us to fully rebuild in a (dependency) transitive way the proto
// registry of the supported proto.Messages for the application, this could be easily
// done if we weren't relying on gogoproto which does not allow us to iterate over the
// registry. Achieving this right now would mean to start slowly building descriptors
// getting their files dependencies, building those dependencies then rebuilding the
// descriptor builder. It's too much work as of now.
InterfaceAcceptingMessages: nil,
InterfaceImplementers: interfaceImplementers,
}
}
return &CodecDescriptor{
Interfaces: interfaceDescriptors,
}, nil
}
func newQueryServiceDescriptor(srv *grpc.Server) *QueryServicesDescriptor {
svcInfo := srv.GetServiceInfo()
queryServices := make([]*QueryServiceDescriptor, 0, len(svcInfo))
for name, info := range svcInfo {
methods := make([]*QueryMethodDescriptor, len(info.Methods))
for i, svcMethod := range info.Methods {
methods[i] = &QueryMethodDescriptor{
Name: svcMethod.Name,
FullQueryPath: fmt.Sprintf("/%s/%s", name, svcMethod.Name),
}
}
queryServices = append(queryServices, &QueryServiceDescriptor{
Fullname: name,
Methods: methods,
})
}
return &QueryServicesDescriptor{QueryServices: queryServices}
}
func newTxDescriptor(ir codectypes.InterfaceRegistry) (*TxDescriptor, error) {
// get base tx type name
txPbName := proto.MessageName(&tx.Tx{})
if txPbName == "" {
return nil, fmt.Errorf("unable to get *tx.Tx protobuf name")
}
// get msgs
sdkMsgImplementers := ir.ListImplementations(sdk.MsgInterfaceProtoName)
msgsDesc := make([]*MsgDescriptor, 0, len(sdkMsgImplementers))
// process sdk.Msg
for _, msgTypeURL := range sdkMsgImplementers {
msgsDesc = append(msgsDesc, &MsgDescriptor{
MsgTypeUrl: msgTypeURL,
})
}
return &TxDescriptor{
Fullname: txPbName,
Msgs: msgsDesc,
}, nil
}
func newAuthnDescriptor(signingModes map[string]int32) *AuthnDescriptor {
signModesDesc := make([]*SigningModeDescriptor, 0, len(signingModes))
for i, m := range signingModes {
signModesDesc = append(signModesDesc, &SigningModeDescriptor{
Name: i,
Number: m,
// NOTE(fdymylja): this cannot be filled as of now, auth and the sdk itself don't support as of now
// a service which allows to get authentication metadata for the provided sign mode.
AuthnInfoProviderMethodFullname: "",
})
}
return &AuthnDescriptor{SignModes: signModesDesc}
}