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 }