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 }