github.com/Finschia/finschia-sdk@v0.48.1/codec/types/interface_registry.go (about)

     1  package types
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  
     7  	"github.com/gogo/protobuf/jsonpb"
     8  
     9  	"github.com/gogo/protobuf/proto"
    10  )
    11  
    12  // AnyUnpacker is an interface which allows safely unpacking types packed
    13  // in Any's against a whitelist of registered types
    14  type AnyUnpacker interface {
    15  	// UnpackAny unpacks the value in any to the interface pointer passed in as
    16  	// iface. Note that the type in any must have been registered in the
    17  	// underlying whitelist registry as a concrete type for that interface
    18  	// Ex:
    19  	//    var msg sdk.Msg
    20  	//    err := cdc.UnpackAny(any, &msg)
    21  	//    ...
    22  	UnpackAny(any *Any, iface interface{}) error
    23  }
    24  
    25  // InterfaceRegistry provides a mechanism for registering interfaces and
    26  // implementations that can be safely unpacked from Any
    27  type InterfaceRegistry interface {
    28  	AnyUnpacker
    29  	jsonpb.AnyResolver
    30  
    31  	// RegisterInterface associates protoName as the public name for the
    32  	// interface passed in as iface. This is to be used primarily to create
    33  	// a public facing registry of interface implementations for clients.
    34  	// protoName should be a well-chosen public facing name that remains stable.
    35  	// RegisterInterface takes an optional list of impls to be registered
    36  	// as implementations of iface.
    37  	//
    38  	// Ex:
    39  	//   registry.RegisterInterface("cosmos.base.v1beta1.Msg", (*sdk.Msg)(nil))
    40  	RegisterInterface(protoName string, iface interface{}, impls ...proto.Message)
    41  
    42  	// RegisterImplementations registers impls as concrete implementations of
    43  	// the interface iface.
    44  	//
    45  	// Ex:
    46  	//  registry.RegisterImplementations((*sdk.Msg)(nil), &MsgSend{}, &MsgMultiSend{})
    47  	RegisterImplementations(iface interface{}, impls ...proto.Message)
    48  
    49  	// ListAllInterfaces list the type URLs of all registered interfaces.
    50  	ListAllInterfaces() []string
    51  
    52  	// ListImplementations lists the valid type URLs for the given interface name that can be used
    53  	// for the provided interface type URL.
    54  	ListImplementations(ifaceTypeURL string) []string
    55  }
    56  
    57  // UnpackInterfacesMessage is meant to extend protobuf types (which implement
    58  // proto.Message) to support a post-deserialization phase which unpacks
    59  // types packed within Any's using the whitelist provided by AnyUnpacker
    60  type UnpackInterfacesMessage interface {
    61  	// UnpackInterfaces is implemented in order to unpack values packed within
    62  	// Any's using the AnyUnpacker. It should generally be implemented as
    63  	// follows:
    64  	//   func (s *MyStruct) UnpackInterfaces(unpacker AnyUnpacker) error {
    65  	//		var x AnyInterface
    66  	//		// where X is an Any field on MyStruct
    67  	//		err := unpacker.UnpackAny(s.X, &x)
    68  	//		if err != nil {
    69  	//			return nil
    70  	//		}
    71  	//		// where Y is a field on MyStruct that implements UnpackInterfacesMessage itself
    72  	//		err = s.Y.UnpackInterfaces(unpacker)
    73  	//		if err != nil {
    74  	//			return nil
    75  	//		}
    76  	//		return nil
    77  	//	 }
    78  	UnpackInterfaces(unpacker AnyUnpacker) error
    79  }
    80  
    81  type interfaceRegistry struct {
    82  	interfaceNames map[string]reflect.Type
    83  	interfaceImpls map[reflect.Type]interfaceMap
    84  	typeURLMap     map[string]reflect.Type
    85  }
    86  
    87  type interfaceMap = map[string]reflect.Type
    88  
    89  // NewInterfaceRegistry returns a new InterfaceRegistry
    90  func NewInterfaceRegistry() InterfaceRegistry {
    91  	return &interfaceRegistry{
    92  		interfaceNames: map[string]reflect.Type{},
    93  		interfaceImpls: map[reflect.Type]interfaceMap{},
    94  		typeURLMap:     map[string]reflect.Type{},
    95  	}
    96  }
    97  
    98  func (registry *interfaceRegistry) RegisterInterface(protoName string, iface interface{}, impls ...proto.Message) {
    99  	typ := reflect.TypeOf(iface)
   100  	if typ.Elem().Kind() != reflect.Interface {
   101  		panic(fmt.Errorf("%T is not an interface type", iface))
   102  	}
   103  	registry.interfaceNames[protoName] = typ
   104  	registry.RegisterImplementations(iface, impls...)
   105  }
   106  
   107  // RegisterImplementations registers a concrete proto Message which implements
   108  // the given interface.
   109  //
   110  // This function PANICs if different concrete types are registered under the
   111  // same typeURL.
   112  func (registry *interfaceRegistry) RegisterImplementations(iface interface{}, impls ...proto.Message) {
   113  	for _, impl := range impls {
   114  		typeURL := "/" + proto.MessageName(impl)
   115  		registry.registerImpl(iface, typeURL, impl)
   116  	}
   117  }
   118  
   119  // RegisterCustomTypeURL registers a concrete type which implements the given
   120  // interface under `typeURL`.
   121  //
   122  // This function PANICs if different concrete types are registered under the
   123  // same typeURL.
   124  func (registry *interfaceRegistry) RegisterCustomTypeURL(iface interface{}, typeURL string, impl proto.Message) {
   125  	registry.registerImpl(iface, typeURL, impl)
   126  }
   127  
   128  // registerImpl registers a concrete type which implements the given
   129  // interface under `typeURL`.
   130  //
   131  // This function PANICs if different concrete types are registered under the
   132  // same typeURL.
   133  func (registry *interfaceRegistry) registerImpl(iface interface{}, typeURL string, impl proto.Message) {
   134  	ityp := reflect.TypeOf(iface).Elem()
   135  	imap, found := registry.interfaceImpls[ityp]
   136  	if !found {
   137  		imap = map[string]reflect.Type{}
   138  	}
   139  
   140  	implType := reflect.TypeOf(impl)
   141  	if !implType.AssignableTo(ityp) {
   142  		panic(fmt.Errorf("type %T doesn't actually implement interface %+v", impl, ityp))
   143  	}
   144  
   145  	// Check if we already registered something under the given typeURL. It's
   146  	// okay to register the same concrete type again, but if we are registering
   147  	// a new concrete type under the same typeURL, then we throw an error (here,
   148  	// we panic).
   149  	foundImplType, found := imap[typeURL]
   150  	if found && foundImplType != implType {
   151  		panic(
   152  			fmt.Errorf(
   153  				"concrete type %s has already been registered under typeURL %s, cannot register %s under same typeURL. "+
   154  					"This usually means that there are conflicting modules registering different concrete types "+
   155  					"for a same interface implementation",
   156  				foundImplType,
   157  				typeURL,
   158  				implType,
   159  			),
   160  		)
   161  	}
   162  
   163  	imap[typeURL] = implType
   164  	registry.typeURLMap[typeURL] = implType
   165  
   166  	registry.interfaceImpls[ityp] = imap
   167  }
   168  
   169  func (registry *interfaceRegistry) ListAllInterfaces() []string {
   170  	interfaceNames := registry.interfaceNames
   171  	keys := make([]string, 0, len(interfaceNames))
   172  	for key := range interfaceNames {
   173  		keys = append(keys, key)
   174  	}
   175  	return keys
   176  }
   177  
   178  func (registry *interfaceRegistry) ListImplementations(ifaceName string) []string {
   179  	typ, ok := registry.interfaceNames[ifaceName]
   180  	if !ok {
   181  		return []string{}
   182  	}
   183  
   184  	impls, ok := registry.interfaceImpls[typ.Elem()]
   185  	if !ok {
   186  		return []string{}
   187  	}
   188  
   189  	keys := make([]string, 0, len(impls))
   190  	for key := range impls {
   191  		keys = append(keys, key)
   192  	}
   193  	return keys
   194  }
   195  
   196  func (registry *interfaceRegistry) UnpackAny(any *Any, iface interface{}) error {
   197  	// here we gracefully handle the case in which `any` itself is `nil`, which may occur in message decoding
   198  	if any == nil {
   199  		return nil
   200  	}
   201  
   202  	if any.TypeUrl == "" {
   203  		// if TypeUrl is empty return nil because without it we can't actually unpack anything
   204  		return nil
   205  	}
   206  
   207  	rv := reflect.ValueOf(iface)
   208  	if rv.Kind() != reflect.Ptr {
   209  		return fmt.Errorf("UnpackAny expects a pointer")
   210  	}
   211  
   212  	rt := rv.Elem().Type()
   213  
   214  	cachedValue := any.cachedValue
   215  	if cachedValue != nil {
   216  		if reflect.TypeOf(cachedValue).AssignableTo(rt) {
   217  			rv.Elem().Set(reflect.ValueOf(cachedValue))
   218  			return nil
   219  		}
   220  	}
   221  
   222  	imap, found := registry.interfaceImpls[rt]
   223  	if !found {
   224  		return fmt.Errorf("no registered implementations of type %+v", rt)
   225  	}
   226  
   227  	typ, found := imap[any.TypeUrl]
   228  	if !found {
   229  		return fmt.Errorf("no concrete type registered for type URL %s against interface %T", any.TypeUrl, iface)
   230  	}
   231  
   232  	msg, ok := reflect.New(typ.Elem()).Interface().(proto.Message)
   233  	if !ok {
   234  		return fmt.Errorf("can't proto unmarshal %T", msg)
   235  	}
   236  
   237  	err := proto.Unmarshal(any.Value, msg)
   238  	if err != nil {
   239  		return err
   240  	}
   241  
   242  	err = UnpackInterfaces(msg, registry)
   243  	if err != nil {
   244  		return err
   245  	}
   246  
   247  	rv.Elem().Set(reflect.ValueOf(msg))
   248  
   249  	any.cachedValue = msg
   250  
   251  	return nil
   252  }
   253  
   254  // Resolve returns the proto message given its typeURL. It works with types
   255  // registered with RegisterInterface/RegisterImplementations, as well as those
   256  // registered with RegisterWithCustomTypeURL.
   257  func (registry *interfaceRegistry) Resolve(typeURL string) (proto.Message, error) {
   258  	typ, found := registry.typeURLMap[typeURL]
   259  	if !found {
   260  		return nil, fmt.Errorf("unable to resolve type URL %s", typeURL)
   261  	}
   262  
   263  	msg, ok := reflect.New(typ.Elem()).Interface().(proto.Message)
   264  	if !ok {
   265  		return nil, fmt.Errorf("can't resolve type URL %s", typeURL)
   266  	}
   267  
   268  	return msg, nil
   269  }
   270  
   271  // UnpackInterfaces is a convenience function that calls UnpackInterfaces
   272  // on x if x implements UnpackInterfacesMessage
   273  func UnpackInterfaces(x interface{}, unpacker AnyUnpacker) error {
   274  	if msg, ok := x.(UnpackInterfacesMessage); ok {
   275  		return msg.UnpackInterfaces(unpacker)
   276  	}
   277  	return nil
   278  }