Skip to content
This repository was archived by the owner on Jan 20, 2026. It is now read-only.

Commit 571d289

Browse files
authored
Add legacy UnpackAny (#587)
## Describe your changes and provide context Add back interfaceRegistry implementation which was removed in https://github.com/sei-protocol/sei-cosmos/pull/574/files for historical queries. ## Testing performed to validate your change integration with sei-chain
1 parent 7d1e09b commit 571d289

1 file changed

Lines changed: 196 additions & 0 deletions

File tree

codec/types/legacy.go

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
package types
2+
3+
import (
4+
fmt "fmt"
5+
"reflect"
6+
7+
"github.com/gogo/protobuf/proto"
8+
)
9+
10+
type legacyInterfaceRegistry struct {
11+
interfaceNames map[string]reflect.Type
12+
interfaceImpls map[reflect.Type]interfaceMap
13+
typeURLMap map[string]reflect.Type
14+
}
15+
16+
// NewInterfaceRegistry returns a new InterfaceRegistry
17+
func NewLegacyInterfaceRegistry() InterfaceRegistry {
18+
return &legacyInterfaceRegistry{
19+
interfaceNames: map[string]reflect.Type{},
20+
interfaceImpls: map[reflect.Type]interfaceMap{},
21+
typeURLMap: map[string]reflect.Type{},
22+
}
23+
}
24+
25+
func (registry *legacyInterfaceRegistry) RegisterInterface(protoName string, iface interface{}, impls ...proto.Message) {
26+
typ := reflect.TypeOf(iface)
27+
if typ.Elem().Kind() != reflect.Interface {
28+
panic(fmt.Errorf("%T is not an interface type", iface))
29+
}
30+
registry.interfaceNames[protoName] = typ
31+
registry.RegisterImplementations(iface, impls...)
32+
}
33+
34+
// RegisterImplementations registers a concrete proto Message which implements
35+
// the given interface.
36+
//
37+
// This function PANICs if different concrete types are registered under the
38+
// same typeURL.
39+
func (registry *legacyInterfaceRegistry) RegisterImplementations(iface interface{}, impls ...proto.Message) {
40+
for _, impl := range impls {
41+
typeURL := "/" + proto.MessageName(impl)
42+
registry.registerImpl(iface, typeURL, impl)
43+
}
44+
}
45+
46+
// RegisterCustomTypeURL registers a concrete type which implements the given
47+
// interface under `typeURL`.
48+
//
49+
// This function PANICs if different concrete types are registered under the
50+
// same typeURL.
51+
func (registry *legacyInterfaceRegistry) RegisterCustomTypeURL(iface interface{}, typeURL string, impl proto.Message) {
52+
registry.registerImpl(iface, typeURL, impl)
53+
}
54+
55+
// registerImpl registers a concrete type which implements the given
56+
// interface under `typeURL`.
57+
//
58+
// This function PANICs if different concrete types are registered under the
59+
// same typeURL.
60+
func (registry *legacyInterfaceRegistry) registerImpl(iface interface{}, typeURL string, impl proto.Message) {
61+
ityp := reflect.TypeOf(iface).Elem()
62+
imap, found := registry.interfaceImpls[ityp]
63+
if !found {
64+
imap = map[string]reflect.Type{}
65+
}
66+
67+
implType := reflect.TypeOf(impl)
68+
if !implType.AssignableTo(ityp) {
69+
panic(fmt.Errorf("type %T doesn't actually implement interface %+v", impl, ityp))
70+
}
71+
72+
// Check if we already registered something under the given typeURL. It's
73+
// okay to register the same concrete type again, but if we are registering
74+
// a new concrete type under the same typeURL, then we throw an error (here,
75+
// we panic).
76+
foundImplType, found := imap[typeURL]
77+
if found && foundImplType != implType {
78+
panic(
79+
fmt.Errorf(
80+
"concrete type %s has already been registered under typeURL %s, cannot register %s under same typeURL. "+
81+
"This usually means that there are conflicting modules registering different concrete types "+
82+
"for a same interface implementation",
83+
foundImplType,
84+
typeURL,
85+
implType,
86+
),
87+
)
88+
}
89+
90+
imap[typeURL] = implType
91+
registry.typeURLMap[typeURL] = implType
92+
93+
registry.interfaceImpls[ityp] = imap
94+
}
95+
96+
func (registry *legacyInterfaceRegistry) ListAllInterfaces() []string {
97+
interfaceNames := registry.interfaceNames
98+
keys := make([]string, 0, len(interfaceNames))
99+
for key := range interfaceNames {
100+
keys = append(keys, key)
101+
}
102+
return keys
103+
}
104+
105+
func (registry *legacyInterfaceRegistry) ListImplementations(ifaceName string) []string {
106+
typ, ok := registry.interfaceNames[ifaceName]
107+
if !ok {
108+
return []string{}
109+
}
110+
111+
impls, ok := registry.interfaceImpls[typ.Elem()]
112+
if !ok {
113+
return []string{}
114+
}
115+
116+
keys := make([]string, 0, len(impls))
117+
for key := range impls {
118+
keys = append(keys, key)
119+
}
120+
return keys
121+
}
122+
123+
func (registry *legacyInterfaceRegistry) UnpackAny(any *Any, iface interface{}) error {
124+
// here we gracefully handle the case in which `any` itself is `nil`, which may occur in message decoding
125+
if any == nil {
126+
return nil
127+
}
128+
129+
if any.TypeUrl == "" {
130+
// if TypeUrl is empty return nil because without it we can't actually unpack anything
131+
return nil
132+
}
133+
134+
rv := reflect.ValueOf(iface)
135+
if rv.Kind() != reflect.Ptr {
136+
return fmt.Errorf("UnpackAny expects a pointer")
137+
}
138+
139+
rt := rv.Elem().Type()
140+
141+
cachedValue := any.cachedValue
142+
if cachedValue != nil {
143+
if reflect.TypeOf(cachedValue).AssignableTo(rt) {
144+
rv.Elem().Set(reflect.ValueOf(cachedValue))
145+
return nil
146+
}
147+
}
148+
149+
imap, found := registry.interfaceImpls[rt]
150+
if !found {
151+
return fmt.Errorf("no registered implementations of type %+v", rt)
152+
}
153+
154+
typ, found := imap[any.TypeUrl]
155+
if !found {
156+
return fmt.Errorf("no concrete type registered for type URL %s against interface %T", any.TypeUrl, iface)
157+
}
158+
159+
msg, ok := reflect.New(typ.Elem()).Interface().(proto.Message)
160+
if !ok {
161+
return fmt.Errorf("can't proto unmarshal %T", msg)
162+
}
163+
164+
err := proto.Unmarshal(any.Value, msg)
165+
if err != nil {
166+
return err
167+
}
168+
169+
err = UnpackInterfaces(msg, registry)
170+
if err != nil {
171+
return err
172+
}
173+
174+
rv.Elem().Set(reflect.ValueOf(msg))
175+
176+
any.cachedValue = msg
177+
178+
return nil
179+
}
180+
181+
// Resolve returns the proto message given its typeURL. It works with types
182+
// registered with RegisterInterface/RegisterImplementations, as well as those
183+
// registered with RegisterWithCustomTypeURL.
184+
func (registry *legacyInterfaceRegistry) Resolve(typeURL string) (proto.Message, error) {
185+
typ, found := registry.typeURLMap[typeURL]
186+
if !found {
187+
return nil, fmt.Errorf("unable to resolve type URL %s", typeURL)
188+
}
189+
190+
msg, ok := reflect.New(typ.Elem()).Interface().(proto.Message)
191+
if !ok {
192+
return nil, fmt.Errorf("can't resolve type URL %s", typeURL)
193+
}
194+
195+
return msg, nil
196+
}

0 commit comments

Comments
 (0)