github.com/cosmos/cosmos-sdk@v0.50.10/codec/types/interface_registry.go (about)

     1  package types
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  
     7  	"github.com/cosmos/gogoproto/jsonpb"
     8  	"github.com/cosmos/gogoproto/proto"
     9  	"google.golang.org/protobuf/reflect/protodesc"
    10  	"google.golang.org/protobuf/reflect/protoreflect"
    11  
    12  	"cosmossdk.io/x/tx/signing"
    13  )
    14  
    15  // AnyUnpacker is an interface which allows safely unpacking types packed
    16  // in Any's against a whitelist of registered types
    17  type AnyUnpacker interface {
    18  	// UnpackAny unpacks the value in any to the interface pointer passed in as
    19  	// iface. Note that the type in any must have been registered in the
    20  	// underlying whitelist registry as a concrete type for that interface
    21  	// Ex:
    22  	//    var msg sdk.Msg
    23  	//    err := cdc.UnpackAny(any, &msg)
    24  	//    ...
    25  	UnpackAny(any *Any, iface interface{}) error
    26  }
    27  
    28  // InterfaceRegistry provides a mechanism for registering interfaces and
    29  // implementations that can be safely unpacked from Any
    30  type InterfaceRegistry interface {
    31  	AnyUnpacker
    32  	jsonpb.AnyResolver
    33  
    34  	// RegisterInterface associates protoName as the public name for the
    35  	// interface passed in as iface. This is to be used primarily to create
    36  	// a public facing registry of interface implementations for clients.
    37  	// protoName should be a well-chosen public facing name that remains stable.
    38  	// RegisterInterface takes an optional list of impls to be registered
    39  	// as implementations of iface.
    40  	//
    41  	// Ex:
    42  	//   registry.RegisterInterface("cosmos.base.v1beta1.Msg", (*sdk.Msg)(nil))
    43  	RegisterInterface(protoName string, iface interface{}, impls ...proto.Message)
    44  
    45  	// RegisterImplementations registers impls as concrete implementations of
    46  	// the interface iface.
    47  	//
    48  	// Ex:
    49  	//  registry.RegisterImplementations((*sdk.Msg)(nil), &MsgSend{}, &MsgMultiSend{})
    50  	RegisterImplementations(iface interface{}, impls ...proto.Message)
    51  
    52  	// ListAllInterfaces list the type URLs of all registered interfaces.
    53  	ListAllInterfaces() []string
    54  
    55  	// ListImplementations lists the valid type URLs for the given interface name that can be used
    56  	// for the provided interface type URL.
    57  	ListImplementations(ifaceTypeURL string) []string
    58  
    59  	// EnsureRegistered ensures there is a registered interface for the given concrete type.
    60  	EnsureRegistered(iface interface{}) error
    61  
    62  	protodesc.Resolver
    63  
    64  	// RangeFiles iterates over all registered files and calls f on each one. This
    65  	// implements the part of protoregistry.Files that is needed for reflecting over
    66  	// the entire FileDescriptorSet.
    67  	RangeFiles(f func(protoreflect.FileDescriptor) bool)
    68  
    69  	SigningContext() *signing.Context
    70  
    71  	// mustEmbedInterfaceRegistry requires that all implementations of InterfaceRegistry embed an official implementation
    72  	// from this package. This allows new methods to be added to the InterfaceRegistry interface without breaking
    73  	// backwards compatibility.
    74  	mustEmbedInterfaceRegistry()
    75  }
    76  
    77  // UnpackInterfacesMessage is meant to extend protobuf types (which implement
    78  // proto.Message) to support a post-deserialization phase which unpacks
    79  // types packed within Any's using the whitelist provided by AnyUnpacker
    80  type UnpackInterfacesMessage interface {
    81  	// UnpackInterfaces is implemented in order to unpack values packed within
    82  	// Any's using the AnyUnpacker. It should generally be implemented as
    83  	// follows:
    84  	//   func (s *MyStruct) UnpackInterfaces(unpacker AnyUnpacker) error {
    85  	//		var x AnyInterface
    86  	//		// where X is an Any field on MyStruct
    87  	//		err := unpacker.UnpackAny(s.X, &x)
    88  	//		if err != nil {
    89  	//			return nil
    90  	//		}
    91  	//		// where Y is a field on MyStruct that implements UnpackInterfacesMessage itself
    92  	//		err = s.Y.UnpackInterfaces(unpacker)
    93  	//		if err != nil {
    94  	//			return nil
    95  	//		}
    96  	//		return nil
    97  	//	 }
    98  	UnpackInterfaces(unpacker AnyUnpacker) error
    99  }
   100  
   101  type interfaceRegistry struct {
   102  	signing.ProtoFileResolver
   103  	interfaceNames map[string]reflect.Type
   104  	interfaceImpls map[reflect.Type]interfaceMap
   105  	implInterfaces map[reflect.Type]reflect.Type
   106  	typeURLMap     map[string]reflect.Type
   107  	signingCtx     *signing.Context
   108  }
   109  
   110  type interfaceMap = map[string]reflect.Type
   111  
   112  // NewInterfaceRegistry returns a new InterfaceRegistry
   113  func NewInterfaceRegistry() InterfaceRegistry {
   114  	registry, err := NewInterfaceRegistryWithOptions(InterfaceRegistryOptions{
   115  		ProtoFiles: proto.HybridResolver,
   116  		SigningOptions: signing.Options{
   117  			AddressCodec:          failingAddressCodec{},
   118  			ValidatorAddressCodec: failingAddressCodec{},
   119  		},
   120  	})
   121  	if err != nil {
   122  		panic(err)
   123  	}
   124  	return registry
   125  }
   126  
   127  // InterfaceRegistryOptions are options for creating a new InterfaceRegistry.
   128  type InterfaceRegistryOptions struct {
   129  	// ProtoFiles is the set of files to use for the registry. It is required.
   130  	ProtoFiles signing.ProtoFileResolver
   131  
   132  	// SigningOptions are the signing options to use for the registry.
   133  	SigningOptions signing.Options
   134  }
   135  
   136  // NewInterfaceRegistryWithOptions returns a new InterfaceRegistry with the given options.
   137  func NewInterfaceRegistryWithOptions(options InterfaceRegistryOptions) (InterfaceRegistry, error) {
   138  	if options.ProtoFiles == nil {
   139  		return nil, fmt.Errorf("proto files must be provided")
   140  	}
   141  
   142  	options.SigningOptions.FileResolver = options.ProtoFiles
   143  	signingCtx, err := signing.NewContext(options.SigningOptions)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	return &interfaceRegistry{
   149  		interfaceNames:    map[string]reflect.Type{},
   150  		interfaceImpls:    map[reflect.Type]interfaceMap{},
   151  		implInterfaces:    map[reflect.Type]reflect.Type{},
   152  		typeURLMap:        map[string]reflect.Type{},
   153  		ProtoFileResolver: options.ProtoFiles,
   154  		signingCtx:        signingCtx,
   155  	}, nil
   156  }
   157  
   158  func (registry *interfaceRegistry) RegisterInterface(protoName string, iface interface{}, impls ...proto.Message) {
   159  	typ := reflect.TypeOf(iface)
   160  	if typ.Elem().Kind() != reflect.Interface {
   161  		panic(fmt.Errorf("%T is not an interface type", iface))
   162  	}
   163  
   164  	registry.interfaceNames[protoName] = typ
   165  	registry.RegisterImplementations(iface, impls...)
   166  }
   167  
   168  // EnsureRegistered ensures there is a registered interface for the given concrete type.
   169  //
   170  // Returns an error if not, and nil if so.
   171  func (registry *interfaceRegistry) EnsureRegistered(impl interface{}) error {
   172  	if reflect.ValueOf(impl).Kind() != reflect.Ptr {
   173  		return fmt.Errorf("%T is not a pointer", impl)
   174  	}
   175  
   176  	if _, found := registry.implInterfaces[reflect.TypeOf(impl)]; !found {
   177  		return fmt.Errorf("%T does not have a registered interface", impl)
   178  	}
   179  
   180  	return nil
   181  }
   182  
   183  // RegisterImplementations registers a concrete proto Message which implements
   184  // the given interface.
   185  //
   186  // This function PANICs if different concrete types are registered under the
   187  // same typeURL.
   188  func (registry *interfaceRegistry) RegisterImplementations(iface interface{}, impls ...proto.Message) {
   189  	for _, impl := range impls {
   190  		typeURL := MsgTypeURL(impl)
   191  		registry.registerImpl(iface, typeURL, impl)
   192  	}
   193  }
   194  
   195  // RegisterCustomTypeURL registers a concrete type which implements the given
   196  // interface under `typeURL`.
   197  //
   198  // This function PANICs if different concrete types are registered under the
   199  // same typeURL.
   200  func (registry *interfaceRegistry) RegisterCustomTypeURL(iface interface{}, typeURL string, impl proto.Message) {
   201  	registry.registerImpl(iface, typeURL, impl)
   202  }
   203  
   204  // registerImpl registers a concrete type which implements the given
   205  // interface under `typeURL`.
   206  //
   207  // This function PANICs if different concrete types are registered under the
   208  // same typeURL.
   209  func (registry *interfaceRegistry) registerImpl(iface interface{}, typeURL string, impl proto.Message) {
   210  	ityp := reflect.TypeOf(iface).Elem()
   211  	imap, found := registry.interfaceImpls[ityp]
   212  	if !found {
   213  		imap = map[string]reflect.Type{}
   214  	}
   215  
   216  	implType := reflect.TypeOf(impl)
   217  	if !implType.AssignableTo(ityp) {
   218  		panic(fmt.Errorf("type %T doesn't actually implement interface %+v", impl, ityp))
   219  	}
   220  
   221  	// Check if we already registered something under the given typeURL. It's
   222  	// okay to register the same concrete type again, but if we are registering
   223  	// a new concrete type under the same typeURL, then we throw an error (here,
   224  	// we panic).
   225  	foundImplType, found := imap[typeURL]
   226  	if found && foundImplType != implType {
   227  		panic(
   228  			fmt.Errorf(
   229  				"concrete type %s has already been registered under typeURL %s, cannot register %s under same typeURL. "+
   230  					"This usually means that there are conflicting modules registering different concrete types "+
   231  					"for a same interface implementation",
   232  				foundImplType,
   233  				typeURL,
   234  				implType,
   235  			),
   236  		)
   237  	}
   238  
   239  	imap[typeURL] = implType
   240  	registry.typeURLMap[typeURL] = implType
   241  	registry.implInterfaces[implType] = ityp
   242  	registry.interfaceImpls[ityp] = imap
   243  }
   244  
   245  func (registry *interfaceRegistry) ListAllInterfaces() []string {
   246  	interfaceNames := registry.interfaceNames
   247  	keys := make([]string, 0, len(interfaceNames))
   248  	for key := range interfaceNames {
   249  		keys = append(keys, key)
   250  	}
   251  	return keys
   252  }
   253  
   254  func (registry *interfaceRegistry) ListImplementations(ifaceName string) []string {
   255  	typ, ok := registry.interfaceNames[ifaceName]
   256  	if !ok {
   257  		return []string{}
   258  	}
   259  
   260  	impls, ok := registry.interfaceImpls[typ.Elem()]
   261  	if !ok {
   262  		return []string{}
   263  	}
   264  
   265  	keys := make([]string, 0, len(impls))
   266  	for key := range impls {
   267  		keys = append(keys, key)
   268  	}
   269  	return keys
   270  }
   271  
   272  func (registry *interfaceRegistry) UnpackAny(any *Any, iface interface{}) error {
   273  	// here we gracefully handle the case in which `any` itself is `nil`, which may occur in message decoding
   274  	if any == nil {
   275  		return nil
   276  	}
   277  
   278  	if any.TypeUrl == "" {
   279  		// if TypeUrl is empty return nil because without it we can't actually unpack anything
   280  		return nil
   281  	}
   282  
   283  	rv := reflect.ValueOf(iface)
   284  	if rv.Kind() != reflect.Ptr {
   285  		return fmt.Errorf("UnpackAny expects a pointer")
   286  	}
   287  
   288  	rt := rv.Elem().Type()
   289  
   290  	cachedValue := any.cachedValue
   291  	if cachedValue != nil {
   292  		if reflect.TypeOf(cachedValue).AssignableTo(rt) {
   293  			rv.Elem().Set(reflect.ValueOf(cachedValue))
   294  			return nil
   295  		}
   296  	}
   297  
   298  	imap, found := registry.interfaceImpls[rt]
   299  	if !found {
   300  		return fmt.Errorf("no registered implementations of type %+v", rt)
   301  	}
   302  
   303  	typ, found := imap[any.TypeUrl]
   304  	if !found {
   305  		return fmt.Errorf("no concrete type registered for type URL %s against interface %T", any.TypeUrl, iface)
   306  	}
   307  
   308  	msg, ok := reflect.New(typ.Elem()).Interface().(proto.Message)
   309  	if !ok {
   310  		return fmt.Errorf("can't proto unmarshal %T", msg)
   311  	}
   312  
   313  	err := proto.Unmarshal(any.Value, msg)
   314  	if err != nil {
   315  		return err
   316  	}
   317  
   318  	err = UnpackInterfaces(msg, registry)
   319  	if err != nil {
   320  		return err
   321  	}
   322  
   323  	rv.Elem().Set(reflect.ValueOf(msg))
   324  
   325  	any.cachedValue = msg
   326  
   327  	return nil
   328  }
   329  
   330  // Resolve returns the proto message given its typeURL. It works with types
   331  // registered with RegisterInterface/RegisterImplementations, as well as those
   332  // registered with RegisterWithCustomTypeURL.
   333  func (registry *interfaceRegistry) Resolve(typeURL string) (proto.Message, error) {
   334  	typ, found := registry.typeURLMap[typeURL]
   335  	if !found {
   336  		return nil, fmt.Errorf("unable to resolve type URL %s", typeURL)
   337  	}
   338  
   339  	msg, ok := reflect.New(typ.Elem()).Interface().(proto.Message)
   340  	if !ok {
   341  		return nil, fmt.Errorf("can't resolve type URL %s", typeURL)
   342  	}
   343  
   344  	return msg, nil
   345  }
   346  
   347  func (registry *interfaceRegistry) SigningContext() *signing.Context {
   348  	return registry.signingCtx
   349  }
   350  
   351  func (registry *interfaceRegistry) mustEmbedInterfaceRegistry() {}
   352  
   353  // UnpackInterfaces is a convenience function that calls UnpackInterfaces
   354  // on x if x implements UnpackInterfacesMessage
   355  func UnpackInterfaces(x interface{}, unpacker AnyUnpacker) error {
   356  	if msg, ok := x.(UnpackInterfacesMessage); ok {
   357  		return msg.UnpackInterfaces(unpacker)
   358  	}
   359  	return nil
   360  }
   361  
   362  type failingAddressCodec struct{}
   363  
   364  func (f failingAddressCodec) StringToBytes(string) ([]byte, error) {
   365  	return nil, fmt.Errorf("InterfaceRegistry requires a proper address codec implementation to do address conversion")
   366  }
   367  
   368  func (f failingAddressCodec) BytesToString([]byte) (string, error) {
   369  	return "", fmt.Errorf("InterfaceRegistry requires a proper address codec implementation to do address conversion")
   370  }