refactor: migrate codec/types to type aliases, remove depinject dep on sdk (#24336)

Co-authored-by: Alex | Interchain Labs <alex@interchainlabs.io>
Co-authored-by: Tyler <48813565+technicallyty@users.noreply.github.com>
This commit is contained in:
Aaron Craelius
2025-04-02 23:37:19 +00:00
committed by GitHub
co-authored by Alex | Interchain Labs Tyler
parent 41e3e9d004
commit 167f3f12e2
46 changed files with 300 additions and 1775 deletions
+7 -147
View File
@@ -1,154 +1,14 @@
package types
import (
fmt "fmt"
"github.com/cosmos/gogoproto/proto"
protov2 "google.golang.org/protobuf/proto"
errorsmod "cosmossdk.io/errors"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
gogoany "github.com/cosmos/gogoproto/types/any"
)
type Any struct {
// A URL/resource name that uniquely identifies the type of the serialized
// protocol buffer message. This string must contain at least
// one "/" character. The last segment of the URL's path must represent
// the fully qualified name of the type (as in
// `path/google.protobuf.Duration`). The name should be in a canonical form
// (e.g., leading "." is not accepted).
//
// In practice, teams usually precompile into the binary all types that they
// expect it to use in the context of Any. However, for URLs which use the
// scheme `http`, `https`, or no scheme, one can optionally set up a type
// server that maps type URLs to message definitions as follows:
//
// * If no scheme is provided, `https` is assumed.
// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
// value in binary format, or produce an error.
// * Applications are allowed to cache lookup results based on the
// URL, or have them precompiled into a binary to avoid any
// lookup. Therefore, binary compatibility needs to be preserved
// on changes to types. (Use versioned type names to manage
// breaking changes.)
//
// Note: this functionality is not currently available in the official
// protobuf release, and it is not used for type URLs beginning with
// type.googleapis.com.
//
// Schemes other than `http`, `https` (or the empty scheme) might be
// used with implementation specific semantics.
// Any is an alias for github.com/cosmos/gogoproto/types/any.Any.
type Any = gogoany.Any
TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"`
// NewAnyWithValue is an alias for github.com/cosmos/gogoproto/types/any.NewAnyWithCacheWithValue.
var NewAnyWithValue = gogoany.NewAnyWithCacheWithValue
// Must be a valid serialized protocol buffer of the above specified type.
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
cachedValue interface{}
compat *anyCompat
}
// NewAnyWithValue constructs a new Any packed with the value provided or
// returns an error if that value couldn't be packed. This also caches
// the packed value so that it can be retrieved from GetCachedValue without
// unmarshaling
func NewAnyWithValue(v proto.Message) (*Any, error) {
if v == nil {
return nil, errorsmod.Wrap(sdkerrors.ErrPackAny, "Expecting non nil value to create a new Any")
}
var (
bz []byte
err error
)
if msg, ok := v.(protov2.Message); ok {
protov2MarshalOpts := protov2.MarshalOptions{Deterministic: true}
bz, err = protov2MarshalOpts.Marshal(msg)
} else {
bz, err = proto.Marshal(v)
}
if err != nil {
return nil, err
}
return &Any{
TypeUrl: MsgTypeURL(v),
Value: bz,
cachedValue: v,
}, nil
}
// UnsafePackAny packs the value x in the Any and instead of returning the error
// in the case of a packing failure, keeps the cached value. This should only
// be used in situations where compatibility is needed with amino. Amino-only
// values can safely be packed using this method when they will only be
// marshaled with amino and not protobuf.
func UnsafePackAny(x interface{}) *Any {
if msg, ok := x.(proto.Message); ok {
any, err := NewAnyWithValue(msg)
if err == nil {
return any
}
}
return &Any{cachedValue: x}
}
// pack packs the value x in the Any or returns an error. This also caches
// the packed value so that it can be retrieved from GetCachedValue without
// unmarshaling
func (any *Any) pack(x proto.Message) error {
any.TypeUrl = MsgTypeURL(x)
var (
bz []byte
err error
)
if msg, ok := x.(protov2.Message); ok {
protov2MarshalOpts := protov2.MarshalOptions{Deterministic: true}
bz, err = protov2MarshalOpts.Marshal(msg)
} else {
bz, err = proto.Marshal(x)
}
if err != nil {
return err
}
any.Value = bz
any.cachedValue = x
return nil
}
// GetCachedValue returns the cached value from the Any if present
func (any *Any) GetCachedValue() interface{} {
return any.cachedValue
}
// GoString returns a string representing valid go code to reproduce the current state of
// the struct.
func (any *Any) GoString() string {
if any == nil {
return "nil"
}
extra := ""
if any.XXX_unrecognized != nil {
extra = fmt.Sprintf(",\n XXX_unrecognized: %#v,\n", any.XXX_unrecognized)
}
return fmt.Sprintf("&Any{TypeUrl: %#v,\n Value: %#v%s\n}",
any.TypeUrl, any.Value, extra)
}
// String implements the stringer interface
func (any *Any) String() string {
if any == nil {
return "nil"
}
return fmt.Sprintf("&Any{TypeUrl:%v,Value:%v,XXX_unrecognized:%v}",
any.TypeUrl, any.Value, any.XXX_unrecognized)
}
// UnsafePackAny is an alias for github.com/cosmos/gogoproto/types/any.UnsafePackAnyWithCache.
var UnsafePackAny = gogoany.UnsafePackAnyWithCache
-535
View File
@@ -1,535 +0,0 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: google/protobuf/any.proto
package types
import (
bytes "bytes"
fmt "fmt"
_ "github.com/cosmos/gogoproto/gogoproto"
proto "github.com/cosmos/gogoproto/proto"
io "io"
math "math"
math_bits "math/bits"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
func (m *Any) Reset() { *m = Any{} }
func (*Any) ProtoMessage() {}
func (*Any) Descriptor() ([]byte, []int) {
return fileDescriptor_b53526c13ae22eb4, []int{0}
}
func (*Any) XXX_WellKnownType() string { return "Any" }
func (m *Any) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *Any) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_Any.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *Any) XXX_Merge(src proto.Message) {
xxx_messageInfo_Any.Merge(m, src)
}
func (m *Any) XXX_Size() int {
return m.Size()
}
func (m *Any) XXX_DiscardUnknown() {
xxx_messageInfo_Any.DiscardUnknown(m)
}
var xxx_messageInfo_Any proto.InternalMessageInfo
func (m *Any) GetTypeUrl() string {
if m != nil {
return m.TypeUrl
}
return ""
}
func (m *Any) GetValue() []byte {
if m != nil {
return m.Value
}
return nil
}
func (*Any) XXX_MessageName() string {
return "google.protobuf.Any"
}
func init() {
}
func init() { proto.RegisterFile("google/protobuf/any.proto", fileDescriptor_b53526c13ae22eb4) }
var fileDescriptor_b53526c13ae22eb4 = []byte{
// 248 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4c, 0xcf, 0xcf, 0x4f,
0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcc, 0xab, 0xd4,
0x03, 0x73, 0x84, 0xf8, 0x21, 0x52, 0x7a, 0x30, 0x29, 0x29, 0x91, 0xf4, 0xfc, 0xf4, 0x7c, 0x30,
0x4f, 0x1f, 0xc4, 0x82, 0x48, 0x28, 0x79, 0x70, 0x31, 0x3b, 0xe6, 0x55, 0x0a, 0x49, 0x72, 0x71,
0x94, 0x54, 0x16, 0xa4, 0xc6, 0x97, 0x16, 0xe5, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0xb1,
0x83, 0xf8, 0xa1, 0x45, 0x39, 0x42, 0x22, 0x5c, 0xac, 0x65, 0x89, 0x39, 0xa5, 0xa9, 0x12, 0x4c,
0x0a, 0x8c, 0x1a, 0x3c, 0x41, 0x10, 0x8e, 0x95, 0xc0, 0x8c, 0x05, 0xf2, 0x0c, 0x1b, 0x16, 0xc8,
0x33, 0x7c, 0x58, 0x28, 0xcf, 0xd0, 0x70, 0x47, 0x81, 0xc1, 0xa9, 0x99, 0xf1, 0xc6, 0x43, 0x39,
0x86, 0x0f, 0x0f, 0xe5, 0x18, 0x7f, 0x3c, 0x94, 0x63, 0x6c, 0x78, 0x24, 0xc7, 0xb8, 0xe2, 0x91,
0x1c, 0xe3, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0xf8, 0xe2,
0x91, 0x1c, 0xc3, 0x07, 0x90, 0xf8, 0x63, 0x39, 0xc6, 0x03, 0x8f, 0xe5, 0x18, 0x4e, 0x3c, 0x96,
0x63, 0xe4, 0x12, 0x4e, 0xce, 0xcf, 0xd5, 0x43, 0x73, 0xab, 0x13, 0x87, 0x63, 0x5e, 0x65, 0x00,
0x88, 0x13, 0xc0, 0x18, 0xc5, 0x0a, 0x72, 0x48, 0xf1, 0x22, 0x26, 0x66, 0xf7, 0x00, 0xa7, 0x55,
0x4c, 0x72, 0xee, 0x10, 0xa5, 0x01, 0x50, 0xa5, 0x7a, 0xe1, 0xa9, 0x39, 0x39, 0xde, 0x79, 0xf9,
0xe5, 0x79, 0x21, 0x20, 0x65, 0x49, 0x6c, 0x60, 0x33, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff,
0x4d, 0x91, 0x00, 0xa0, 0x1a, 0x01, 0x00, 0x00,
}
func (this *Any) Compare(that interface{}) int {
if that == nil {
if this == nil {
return 0
}
return 1
}
that1, ok := that.(*Any)
if !ok {
that2, ok := that.(Any)
if ok {
that1 = &that2
} else {
return 1
}
}
if that1 == nil {
if this == nil {
return 0
}
return 1
} else if this == nil {
return -1
}
if this.TypeUrl != that1.TypeUrl {
if this.TypeUrl < that1.TypeUrl {
return -1
}
return 1
}
if c := bytes.Compare(this.Value, that1.Value); c != 0 {
return c
}
if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 {
return c
}
return 0
}
func (this *Any) Equal(that interface{}) bool {
if that == nil {
return this == nil
}
that1, ok := that.(*Any)
if !ok {
that2, ok := that.(Any)
if ok {
that1 = &that2
} else {
return false
}
}
if that1 == nil {
return this == nil
} else if this == nil {
return false
}
if this.TypeUrl != that1.TypeUrl {
return false
}
if !bytes.Equal(this.Value, that1.Value) {
return false
}
if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) {
return false
}
return true
}
func (m *Any) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Any) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Any) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.XXX_unrecognized != nil {
i -= len(m.XXX_unrecognized)
copy(dAtA[i:], m.XXX_unrecognized)
}
if len(m.Value) > 0 {
i -= len(m.Value)
copy(dAtA[i:], m.Value)
i = encodeVarintAny(dAtA, i, uint64(len(m.Value)))
i--
dAtA[i] = 0x12
}
if len(m.TypeUrl) > 0 {
i -= len(m.TypeUrl)
copy(dAtA[i:], m.TypeUrl)
i = encodeVarintAny(dAtA, i, uint64(len(m.TypeUrl)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintAny(dAtA []byte, offset int, v uint64) int {
offset -= sovAny(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func NewPopulatedAny(r randyAny, easy bool) *Any {
this := &Any{}
this.TypeUrl = string(randStringAny(r))
v1 := r.Intn(100)
this.Value = make([]byte, v1)
for i := 0; i < v1; i++ {
this.Value[i] = byte(r.Intn(256))
}
if !easy && r.Intn(10) != 0 {
this.XXX_unrecognized = randUnrecognizedAny(r, 3)
}
return this
}
type randyAny interface {
Float32() float32
Float64() float64
Int63() int64
Int31() int32
Uint32() uint32
Intn(n int) int
}
func randUTF8RuneAny(r randyAny) rune {
ru := r.Intn(62)
if ru < 10 {
return rune(ru + 48)
} else if ru < 36 {
return rune(ru + 55)
}
return rune(ru + 61)
}
func randStringAny(r randyAny) string {
v2 := r.Intn(100)
tmps := make([]rune, v2)
for i := 0; i < v2; i++ {
tmps[i] = randUTF8RuneAny(r)
}
return string(tmps)
}
func randUnrecognizedAny(r randyAny, maxFieldNumber int) (dAtA []byte) {
l := r.Intn(5)
for i := 0; i < l; i++ {
wire := r.Intn(4)
if wire == 3 {
wire = 5
}
fieldNumber := maxFieldNumber + r.Intn(100)
dAtA = randFieldAny(dAtA, r, fieldNumber, wire)
}
return dAtA
}
func randFieldAny(dAtA []byte, r randyAny, fieldNumber int, wire int) []byte {
key := uint32(fieldNumber)<<3 | uint32(wire)
switch wire {
case 0:
dAtA = encodeVarintPopulateAny(dAtA, uint64(key))
v3 := r.Int63()
if r.Intn(2) == 0 {
v3 *= -1
}
dAtA = encodeVarintPopulateAny(dAtA, uint64(v3))
case 1:
dAtA = encodeVarintPopulateAny(dAtA, uint64(key))
dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)))
case 2:
dAtA = encodeVarintPopulateAny(dAtA, uint64(key))
ll := r.Intn(100)
dAtA = encodeVarintPopulateAny(dAtA, uint64(ll))
for j := 0; j < ll; j++ {
dAtA = append(dAtA, byte(r.Intn(256)))
}
default:
dAtA = encodeVarintPopulateAny(dAtA, uint64(key))
dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)))
}
return dAtA
}
func encodeVarintPopulateAny(dAtA []byte, v uint64) []byte {
for v >= 1<<7 {
dAtA = append(dAtA, uint8(uint64(v)&0x7f|0x80))
v >>= 7
}
dAtA = append(dAtA, uint8(v))
return dAtA
}
func (m *Any) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.TypeUrl)
if l > 0 {
n += 1 + l + sovAny(uint64(l))
}
l = len(m.Value)
if l > 0 {
n += 1 + l + sovAny(uint64(l))
}
if m.XXX_unrecognized != nil {
n += len(m.XXX_unrecognized)
}
return n
}
func sovAny(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozAny(x uint64) (n int) {
return sovAny(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *Any) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAny
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: Any: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: Any: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field TypeUrl", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAny
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthAny
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthAny
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.TypeUrl = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowAny
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthAny
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthAny
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...)
if m.Value == nil {
m.Value = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipAny(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthAny
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipAny(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowAny
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowAny
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowAny
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthAny
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupAny
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthAny
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthAny = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowAny = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupAny = fmt.Errorf("proto: unexpected end of group")
)
-67
View File
@@ -1,67 +0,0 @@
package types
import (
"testing"
"github.com/cosmos/gogoproto/proto"
"github.com/stretchr/testify/require"
)
type Dog struct {
Name string `protobuf:"bytes,1,opt,name=size,proto3" json:"size,omitempty"`
}
func (d Dog) Greet() string { return d.Name }
// We implement a minimal proto.Message interface
func (d *Dog) Reset() { d.Name = "" }
func (d *Dog) String() string { return d.Name }
func (d *Dog) ProtoMessage() {}
func (d *Dog) XXX_MessageName() string { return "tests/dog" }
type Animal interface {
Greet() string
}
var (
_ Animal = (*Dog)(nil)
_ proto.Message = (*Dog)(nil)
)
func TestAnyPackUnpack(t *testing.T) {
registry := NewInterfaceRegistry()
registry.RegisterInterface("Animal", (*Animal)(nil))
registry.RegisterImplementations(
(*Animal)(nil),
&Dog{},
)
spot := &Dog{Name: "Spot"}
var animal Animal
// with cache
any, err := NewAnyWithValue(spot)
require.NoError(t, err)
require.Equal(t, spot, any.GetCachedValue())
err = registry.UnpackAny(any, &animal)
require.NoError(t, err)
require.Equal(t, spot, animal)
// without cache
any.cachedValue = nil
err = registry.UnpackAny(any, &animal)
require.NoError(t, err)
require.Equal(t, spot, animal)
}
func TestString(t *testing.T) {
require := require.New(t)
spot := &Dog{Name: "Spot"}
any, err := NewAnyWithValue(spot)
require.NoError(err)
require.Equal("&Any{TypeUrl:/tests/dog,Value:[10 4 83 112 111 116],XXX_unrecognized:[]}", any.String())
require.Equal(`&Any{TypeUrl: "/tests/dog",
Value: []byte{0xa, 0x4, 0x53, 0x70, 0x6f, 0x74}
}`, any.GoString())
}
-75
View File
@@ -1,75 +0,0 @@
package types_test
import (
"fmt"
"runtime"
"testing"
"github.com/cosmos/gogoproto/proto"
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
)
type errOnMarshal struct {
testdata.Dog
}
var _ proto.Message = (*errOnMarshal)(nil)
var errAlways = fmt.Errorf("always erroring")
func (eom *errOnMarshal) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return nil, errAlways
}
var eom = &errOnMarshal{}
// Ensure that returning an error doesn't suddenly allocate and waste bytes.
// See https://github.com/cosmos/cosmos-sdk/issues/8537
func TestNewAnyWithCustomTypeURLWithErrorNoAllocation(t *testing.T) {
// This tests continues to fail inconsistently.
//
// Example: https://github.com/cosmos/cosmos-sdk/pull/9246/checks?check_run_id=2643313958#step:6:118
// Ref: https://github.com/cosmos/cosmos-sdk/issues/9010
t.SkipNow()
// make sure we're not in the middle of a GC.
runtime.GC()
var ms1, ms2 runtime.MemStats
runtime.ReadMemStats(&ms1)
any, err := types.NewAnyWithValue(eom)
runtime.ReadMemStats(&ms2)
// Ensure that no fresh allocation was made.
if diff := ms2.HeapAlloc - ms1.HeapAlloc; diff > 0 {
t.Errorf("Unexpected allocation of %d bytes", diff)
}
if err == nil {
t.Fatal("err wasn't returned")
}
if any != nil {
t.Fatalf("Unexpectedly got a non-nil Any value: %v", any)
}
}
var sink interface{}
func BenchmarkNewAnyWithCustomTypeURLWithErrorReturned(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
any, err := types.NewAnyWithValue(eom)
if err == nil {
b.Fatal("err wasn't returned")
}
if any != nil {
b.Fatalf("Unexpectedly got a non-nil Any value: %v", any)
}
sink = any
}
if sink == nil {
b.Fatal("benchmark didn't run")
}
sink = (interface{})(nil)
}
+12 -199
View File
@@ -1,210 +1,23 @@
package types
import (
"fmt"
"reflect"
"runtime/debug"
"github.com/cosmos/gogoproto/jsonpb"
"github.com/cosmos/gogoproto/proto"
amino "github.com/tendermint/go-amino"
gogoany "github.com/cosmos/gogoproto/types/any"
)
type anyCompat struct {
aminoBz []byte
jsonBz []byte
err error
}
// Deprecated: this is no longer used for anything.
var Debug = true
func anyCompatError(errType string, x interface{}) error {
if Debug {
debug.PrintStack()
}
return fmt.Errorf(
"%s marshaling error for %+v, this is likely because "+
"amino is being used directly (instead of codec.LegacyAmino which is preferred) "+
"or UnpackInterfacesMessage is not defined for some type which contains "+
"a protobuf Any either directly or via one of its members. To see a "+
"stacktrace of where the error is coming from, set the var Debug = true "+
"in codec/types/compat.go",
errType, x,
)
}
// AminoUnpacker is an alias for github.com/cosmos/gogoproto/types/any.AminoUnpacker.
type AminoUnpacker = gogoany.AminoUnpacker
func (any Any) MarshalAmino() ([]byte, error) {
ac := any.compat
if ac == nil {
return nil, anyCompatError("amino binary marshal", any)
}
return ac.aminoBz, ac.err
}
// AminoPacker is an alias for github.com/cosmos/gogoproto/types/any.AminoPacker.
type AminoPacker = gogoany.AminoPacker
func (any *Any) UnmarshalAmino(bz []byte) error {
any.compat = &anyCompat{
aminoBz: bz,
err: nil,
}
return nil
}
// AminoJSONUnpacker is an alias for github.com/cosmos/gogoproto/types/any.AminoJSONUnpacker.
type AminoJSONUnpacker = gogoany.AminoJSONUnpacker
func (any *Any) MarshalJSON() ([]byte, error) {
ac := any.compat
if ac == nil {
return nil, anyCompatError("JSON marshal", any)
}
return ac.jsonBz, ac.err
}
// AminoJSONPacker is an alias for github.com/cosmos/gogoproto/types/any.AminoJSONPacker.
type AminoJSONPacker = gogoany.AminoJSONPacker
func (any *Any) UnmarshalJSON(bz []byte) error {
any.compat = &anyCompat{
jsonBz: bz,
err: nil,
}
return nil
}
// AminoUnpacker is an AnyUnpacker provided for backwards compatibility with
// amino for the binary un-marshaling phase
type AminoUnpacker struct {
Cdc *amino.Codec
}
var _ AnyUnpacker = AminoUnpacker{}
func (a AminoUnpacker) UnpackAny(any *Any, iface interface{}) error {
ac := any.compat
if ac == nil {
return anyCompatError("amino binary unmarshal", reflect.TypeOf(iface))
}
err := a.Cdc.UnmarshalBinaryBare(ac.aminoBz, iface)
if err != nil {
return err
}
val := reflect.ValueOf(iface).Elem().Interface()
err = UnpackInterfaces(val, a)
if err != nil {
return err
}
if m, ok := val.(proto.Message); ok {
if err = any.pack(m); err != nil {
return err
}
} else {
any.cachedValue = val
}
// this is necessary for tests that use reflect.DeepEqual and compare
// proto vs amino marshaled values
any.compat = nil
return nil
}
// AminoUnpacker is an AnyUnpacker provided for backwards compatibility with
// amino for the binary marshaling phase
type AminoPacker struct {
Cdc *amino.Codec
}
var _ AnyUnpacker = AminoPacker{}
func (a AminoPacker) UnpackAny(any *Any, _ interface{}) error {
err := UnpackInterfaces(any.cachedValue, a)
if err != nil {
return err
}
bz, err := a.Cdc.MarshalBinaryBare(any.cachedValue)
any.compat = &anyCompat{
aminoBz: bz,
err: err,
}
return err
}
// AminoUnpacker is an AnyUnpacker provided for backwards compatibility with
// amino for the JSON marshaling phase
type AminoJSONUnpacker struct {
Cdc *amino.Codec
}
var _ AnyUnpacker = AminoJSONUnpacker{}
func (a AminoJSONUnpacker) UnpackAny(any *Any, iface interface{}) error {
ac := any.compat
if ac == nil {
return anyCompatError("JSON unmarshal", reflect.TypeOf(iface))
}
err := a.Cdc.UnmarshalJSON(ac.jsonBz, iface)
if err != nil {
return err
}
val := reflect.ValueOf(iface).Elem().Interface()
err = UnpackInterfaces(val, a)
if err != nil {
return err
}
if m, ok := val.(proto.Message); ok {
if err = any.pack(m); err != nil {
return err
}
} else {
any.cachedValue = val
}
// this is necessary for tests that use reflect.DeepEqual and compare
// proto vs amino marshaled values
any.compat = nil
return nil
}
// AminoUnpacker is an AnyUnpacker provided for backwards compatibility with
// amino for the JSON un-marshaling phase
type AminoJSONPacker struct {
Cdc *amino.Codec
}
var _ AnyUnpacker = AminoJSONPacker{}
func (a AminoJSONPacker) UnpackAny(any *Any, _ interface{}) error {
err := UnpackInterfaces(any.cachedValue, a)
if err != nil {
return err
}
bz, err := a.Cdc.MarshalJSON(any.cachedValue)
any.compat = &anyCompat{
jsonBz: bz,
err: err,
}
return err
}
// ProtoJSONPacker is an AnyUnpacker provided for compatibility with jsonpb
type ProtoJSONPacker struct {
JSONPBMarshaler *jsonpb.Marshaler
}
var _ AnyUnpacker = ProtoJSONPacker{}
func (a ProtoJSONPacker) UnpackAny(any *Any, _ interface{}) error {
if any == nil {
return nil
}
if any.cachedValue != nil {
err := UnpackInterfaces(any.cachedValue, a)
if err != nil {
return err
}
}
bz, err := a.JSONPBMarshaler.MarshalToString(any)
any.compat = &anyCompat{
jsonBz: []byte(bz),
err: err,
}
return err
}
// ProtoUnpacker is an alias for github.com/cosmos/gogoproto/types/any.ProtoJSONPacker.
type ProtoJSONPacker = gogoany.ProtoJSONPacker
-132
View File
@@ -1,132 +0,0 @@
package types_test
import (
"testing"
"github.com/stretchr/testify/suite"
amino "github.com/tendermint/go-amino"
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
)
type TypeWithInterface struct {
Animal testdata.Animal `json:"animal"`
X int64 `json:"x,omitempty"`
}
type Suite struct {
suite.Suite
cdc *amino.Codec
a TypeWithInterface
b testdata.HasAnimal
spot *testdata.Dog
}
func (s *Suite) SetupTest() {
s.cdc = amino.NewCodec()
s.cdc.RegisterInterface((*testdata.Animal)(nil), nil)
s.cdc.RegisterConcrete(&testdata.Dog{}, "testdata/Dog", nil)
s.spot = &testdata.Dog{Size_: "small", Name: "Spot"}
s.a = TypeWithInterface{Animal: s.spot}
any, err := types.NewAnyWithValue(s.spot)
s.Require().NoError(err)
s.b = testdata.HasAnimal{Animal: any}
}
func (s *Suite) TestAminoBinary() {
bz, err := s.cdc.MarshalBinaryBare(s.a)
s.Require().NoError(err)
// expect plain amino marshal to fail
_, err = s.cdc.MarshalBinaryBare(s.b)
s.Require().Error(err)
// expect unpack interfaces before amino marshal to succeed
err = types.UnpackInterfaces(s.b, types.AminoPacker{Cdc: s.cdc})
s.Require().NoError(err)
bz2, err := s.cdc.MarshalBinaryBare(s.b)
s.Require().NoError(err)
s.Require().Equal(bz, bz2)
var c testdata.HasAnimal
err = s.cdc.UnmarshalBinaryBare(bz, &c)
s.Require().NoError(err)
err = types.UnpackInterfaces(c, types.AminoUnpacker{Cdc: s.cdc})
s.Require().NoError(err)
s.Require().Equal(s.spot, c.Animal.GetCachedValue())
}
func (s *Suite) TestAminoJSON() {
bz, err := s.cdc.MarshalJSON(s.a)
s.Require().NoError(err)
// expect plain amino marshal to fail
_, err = s.cdc.MarshalJSON(s.b)
s.Require().Error(err)
// expect unpack interfaces before amino marshal to succeed
err = types.UnpackInterfaces(s.b, types.AminoJSONPacker{Cdc: s.cdc})
s.Require().NoError(err)
bz2, err := s.cdc.MarshalJSON(s.b)
s.Require().NoError(err)
s.Require().Equal(string(bz), string(bz2))
var c testdata.HasAnimal
err = s.cdc.UnmarshalJSON(bz, &c)
s.Require().NoError(err)
err = types.UnpackInterfaces(c, types.AminoJSONUnpacker{Cdc: s.cdc})
s.Require().NoError(err)
s.Require().Equal(s.spot, c.Animal.GetCachedValue())
}
func (s *Suite) TestNested() {
s.cdc.RegisterInterface((*testdata.HasAnimalI)(nil), nil)
s.cdc.RegisterInterface((*testdata.HasHasAnimalI)(nil), nil)
s.cdc.RegisterConcrete(&testdata.HasAnimal{}, "testdata/HasAnimal", nil)
s.cdc.RegisterConcrete(&testdata.HasHasAnimal{}, "testdata/HasHasAnimal", nil)
s.cdc.RegisterConcrete(&testdata.HasHasHasAnimal{}, "testdata/HasHasHasAnimal", nil)
any, err := types.NewAnyWithValue(&s.b)
s.Require().NoError(err)
hha := testdata.HasHasAnimal{HasAnimal: any}
any2, err := types.NewAnyWithValue(&hha)
s.Require().NoError(err)
hhha := testdata.HasHasHasAnimal{HasHasAnimal: any2}
// marshal
err = types.UnpackInterfaces(hhha, types.AminoPacker{Cdc: s.cdc})
s.Require().NoError(err)
bz, err := s.cdc.MarshalBinaryBare(hhha)
s.Require().NoError(err)
// unmarshal
var hhha2 testdata.HasHasHasAnimal
err = s.cdc.UnmarshalBinaryBare(bz, &hhha2)
s.Require().NoError(err)
err = types.UnpackInterfaces(hhha2, types.AminoUnpacker{Cdc: s.cdc})
s.Require().NoError(err)
s.Require().Equal(s.spot, hhha2.TheHasHasAnimal().TheHasAnimal().TheAnimal())
// json marshal
err = types.UnpackInterfaces(hhha, types.AminoJSONPacker{Cdc: s.cdc})
s.Require().NoError(err)
jsonBz, err := s.cdc.MarshalJSON(hhha)
s.Require().NoError(err)
// json unmarshal
var hhha3 testdata.HasHasHasAnimal
err = s.cdc.UnmarshalJSON(jsonBz, &hhha3)
s.Require().NoError(err)
err = types.UnpackInterfaces(hhha3, types.AminoJSONUnpacker{Cdc: s.cdc})
s.Require().NoError(err)
s.Require().Equal(s.spot, hhha3.TheHasHasAnimal().TheHasAnimal().TheAnimal())
}
func TestSuite(t *testing.T) {
suite.Run(t, &Suite{})
}
+3 -3
View File
@@ -1,6 +1,6 @@
/*
Package types defines a custom wrapper for google.protobuf.Any which supports
cached values as well as InterfaceRegistry which keeps track of types which can
be used with Any for both security and introspection
Package types defines the InterfaceRegistry type as well as
aliases for types that now live in the github.com/cosmos/gogoproto/types/any package
for backwards compatibility with legacy code.
*/
package types
+29 -30
View File
@@ -7,6 +7,7 @@ import (
"github.com/cosmos/gogoproto/jsonpb"
"github.com/cosmos/gogoproto/proto"
gogoprotoany "github.com/cosmos/gogoproto/types/any"
"google.golang.org/protobuf/reflect/protodesc"
"google.golang.org/protobuf/reflect/protoreflect"
@@ -24,23 +25,24 @@ var (
MaxUnpackAnyRecursionDepth = 10
)
// AnyUnpacker is an interface which allows safely unpacking types packed
// in Any's against a whitelist of registered types
type AnyUnpacker interface {
// UnpackAny unpacks the value in any to the interface pointer passed in as
// iface. Note that the type in any must have been registered in the
// underlying whitelist registry as a concrete type for that interface
// Ex:
// var msg sdk.Msg
// err := cdc.UnpackAny(any, &msg)
// ...
UnpackAny(any *Any, iface interface{}) error
// AnyUnpacker is an alias for github.com/cosmos/gogoproto/types/any.AnyUnpacker.
type AnyUnpacker = gogoprotoany.AnyUnpacker
// UnpackInterfaces is a convenience function that calls UnpackInterfaces
// on x if x implements UnpackInterfacesMessage
func UnpackInterfaces(x interface{}, unpacker gogoprotoany.AnyUnpacker) error {
if msg, ok := x.(gogoprotoany.UnpackInterfacesMessage); ok {
return msg.UnpackInterfaces(unpacker)
}
return nil
}
var protoMessageType = reflect.TypeOf((*proto.Message)(nil)).Elem()
// InterfaceRegistry provides a mechanism for registering interfaces and
// implementations that can be safely unpacked from Any
type InterfaceRegistry interface {
AnyUnpacker
gogoprotoany.AnyUnpacker
jsonpb.AnyResolver
// RegisterInterface associates protoName as the public name for the
@@ -107,7 +109,7 @@ type UnpackInterfacesMessage interface {
// }
// return nil
// }
UnpackInterfaces(unpacker AnyUnpacker) error
UnpackInterfaces(unpacker gogoprotoany.AnyUnpacker) error
}
type interfaceRegistry struct {
@@ -315,10 +317,10 @@ func (r statefulUnpacker) cloneForRecursion() *statefulUnpacker {
// UnpackAny deserializes a protobuf Any message into the provided interface, ensuring the interface is a pointer.
// It applies stateful constraints such as max depth and call limits, and unpacks interfaces if required.
func (r *statefulUnpacker) UnpackAny(any *Any, iface interface{}) error {
if r.maxDepth == 0 {
if r.maxDepth <= 0 {
return errors.New("max depth exceeded")
}
if r.maxCalls.count == 0 {
if r.maxCalls.count <= 0 {
return errors.New("call limit exceeded")
}
// here we gracefully handle the case in which `any` itself is `nil`, which may occur in message decoding
@@ -335,12 +337,12 @@ func (r *statefulUnpacker) UnpackAny(any *Any, iface interface{}) error {
rv := reflect.ValueOf(iface)
if rv.Kind() != reflect.Ptr {
return fmt.Errorf("UnpackAny expects a pointer")
return errors.New("UnpackAny expects a pointer")
}
rt := rv.Elem().Type()
cachedValue := any.cachedValue
cachedValue := any.GetCachedValue()
if cachedValue != nil {
if reflect.TypeOf(cachedValue).AssignableTo(rt) {
rv.Elem().Set(reflect.ValueOf(cachedValue))
@@ -358,11 +360,13 @@ func (r *statefulUnpacker) UnpackAny(any *Any, iface interface{}) error {
return fmt.Errorf("no concrete type registered for type URL %s against interface %T", any.TypeUrl, iface)
}
msg, ok := reflect.New(typ.Elem()).Interface().(proto.Message)
if !ok {
return fmt.Errorf("can't proto unmarshal %T", msg)
// Firstly check if the type implements proto.Message to avoid
// unnecessary invocations to reflect.New
if !typ.Implements(protoMessageType) {
return fmt.Errorf("can't proto unmarshal %T", typ)
}
msg := reflect.New(typ.Elem()).Interface().(proto.Message)
err := proto.Unmarshal(any.Value, msg)
if err != nil {
return err
@@ -375,8 +379,12 @@ func (r *statefulUnpacker) UnpackAny(any *Any, iface interface{}) error {
rv.Elem().Set(reflect.ValueOf(msg))
any.cachedValue = msg
newAnyWithCache, err := NewAnyWithValue(msg)
if err != nil {
return err
}
*any = *newAnyWithCache
return nil
}
@@ -403,15 +411,6 @@ func (registry *interfaceRegistry) SigningContext() *signing.Context {
func (registry *interfaceRegistry) mustEmbedInterfaceRegistry() {}
// UnpackInterfaces is a convenience function that calls UnpackInterfaces
// on x if x implements UnpackInterfacesMessage
func UnpackInterfaces(x interface{}, unpacker AnyUnpacker) error {
if msg, ok := x.(UnpackInterfacesMessage); ok {
return msg.UnpackInterfaces(unpacker)
}
return nil
}
type failingAddressCodec struct{}
func (f failingAddressCodec) StringToBytes(string) ([]byte, error) {
-182
View File
@@ -1,182 +0,0 @@
package types_test
import (
"strings"
"testing"
"github.com/cosmos/gogoproto/jsonpb"
"github.com/cosmos/gogoproto/proto"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
)
func TestAnyPackUnpack(t *testing.T) {
registry := testdata.NewTestInterfaceRegistry()
spot := &testdata.Dog{Name: "Spot"}
var animal testdata.Animal
// with cache
any, err := types.NewAnyWithValue(spot)
require.NoError(t, err)
require.Equal(t, spot, any.GetCachedValue())
err = registry.UnpackAny(any, &animal)
require.NoError(t, err)
require.Equal(t, spot, animal)
}
type TestI interface {
DoSomething()
}
// A struct that has the same typeURL as testdata.Dog, but is actually another
// concrete type.
type FakeDog struct{}
var (
_ proto.Message = &FakeDog{}
_ testdata.Animal = &FakeDog{}
)
// dummy implementation of proto.Message and testdata.Animal
func (dog FakeDog) Reset() {}
func (dog FakeDog) String() string { return "fakedog" }
func (dog FakeDog) ProtoMessage() {}
func (dog FakeDog) XXX_MessageName() string { return proto.MessageName(&testdata.Dog{}) }
func (dog FakeDog) Greet() string { return "fakedog" }
func TestRegister(t *testing.T) {
registry := types.NewInterfaceRegistry()
registry.RegisterInterface("Animal", (*testdata.Animal)(nil))
registry.RegisterInterface("TestI", (*TestI)(nil))
// Happy path.
require.NotPanics(t, func() {
registry.RegisterImplementations((*testdata.Animal)(nil), &testdata.Dog{})
})
// testdata.Dog doesn't implement TestI
require.Panics(t, func() {
registry.RegisterImplementations((*TestI)(nil), &testdata.Dog{})
})
// nil proto message
require.Panics(t, func() {
registry.RegisterImplementations((*TestI)(nil), nil)
})
// Not an interface.
require.Panics(t, func() {
registry.RegisterInterface("not_an_interface", (*testdata.Dog)(nil))
})
// Duplicate registration with same concrete type.
require.NotPanics(t, func() {
registry.RegisterImplementations((*testdata.Animal)(nil), &testdata.Dog{})
})
// Duplicate registration with different concrete type on same typeURL.
require.PanicsWithError(
t,
"concrete type *testdata.Dog has already been registered under typeURL /testpb.Dog, cannot register *types_test.FakeDog under same typeURL. "+
"This usually means that there are conflicting modules registering different concrete types for a same interface implementation",
func() {
registry.RegisterImplementations((*testdata.Animal)(nil), &FakeDog{})
},
)
}
func TestUnpackInterfaces(t *testing.T) {
registry := testdata.NewTestInterfaceRegistry()
spot := &testdata.Dog{Name: "Spot"}
any, err := types.NewAnyWithValue(spot)
require.NoError(t, err)
hasAny := testdata.HasAnimal{
Animal: any,
X: 1,
}
bz, err := hasAny.Marshal()
require.NoError(t, err)
var hasAny2 testdata.HasAnimal
err = hasAny2.Unmarshal(bz)
require.NoError(t, err)
err = types.UnpackInterfaces(hasAny2, registry)
require.NoError(t, err)
require.Equal(t, spot, hasAny2.Animal.GetCachedValue())
}
func TestNested(t *testing.T) {
registry := testdata.NewTestInterfaceRegistry()
spot := &testdata.Dog{Name: "Spot"}
any, err := types.NewAnyWithValue(spot)
require.NoError(t, err)
ha := &testdata.HasAnimal{Animal: any}
any2, err := types.NewAnyWithValue(ha)
require.NoError(t, err)
hha := &testdata.HasHasAnimal{HasAnimal: any2}
any3, err := types.NewAnyWithValue(hha)
require.NoError(t, err)
hhha := testdata.HasHasHasAnimal{HasHasAnimal: any3}
// marshal
bz, err := hhha.Marshal()
require.NoError(t, err)
// unmarshal
var hhha2 testdata.HasHasHasAnimal
err = hhha2.Unmarshal(bz)
require.NoError(t, err)
err = types.UnpackInterfaces(hhha2, registry)
require.NoError(t, err)
require.Equal(t, spot, hhha2.TheHasHasAnimal().TheHasAnimal().TheAnimal())
}
func TestAny_ProtoJSON(t *testing.T) {
spot := &testdata.Dog{Name: "Spot"}
any, err := types.NewAnyWithValue(spot)
require.NoError(t, err)
jm := &jsonpb.Marshaler{}
json, err := jm.MarshalToString(any)
require.NoError(t, err)
require.Equal(t, "{\"@type\":\"/testpb.Dog\",\"name\":\"Spot\"}", json)
registry := testdata.NewTestInterfaceRegistry()
jum := &jsonpb.Unmarshaler{}
var any2 types.Any
err = jum.Unmarshal(strings.NewReader(json), &any2)
require.NoError(t, err)
var animal testdata.Animal
err = registry.UnpackAny(&any2, &animal)
require.NoError(t, err)
require.Equal(t, spot, animal)
ha := &testdata.HasAnimal{
Animal: any,
}
err = ha.UnpackInterfaces(types.ProtoJSONPacker{JSONPBMarshaler: jm})
require.NoError(t, err)
json, err = jm.MarshalToString(ha)
require.NoError(t, err)
require.Equal(t, "{\"animal\":{\"@type\":\"/testpb.Dog\",\"name\":\"Spot\"}}", json)
require.NoError(t, err)
var ha2 testdata.HasAnimal
err = jum.Unmarshal(strings.NewReader(json), &ha2)
require.NoError(t, err)
err = ha2.UnpackInterfaces(registry)
require.NoError(t, err)
require.Equal(t, spot, ha2.Animal.GetCachedValue())
}