Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proto Any init + evidence #6076

Merged
merged 52 commits into from
May 5, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
f8ddff6
Convert evidence state proto to use Any for benchmarking
aaronc Apr 23, 2020
62efb2e
Revert changes for benchmarking
aaronc Apr 23, 2020
59eec83
Add safe Any implementation
aaronc Apr 24, 2020
27ba717
Have Any Pack/Unpack working
aaronc Apr 25, 2020
e21f8a2
Add Any value cache
aaronc Apr 25, 2020
f1b5f18
Implement Any Rehydrate
aaronc Apr 25, 2020
9e92ca9
WIP on UnpackInterfaces
aaronc Apr 26, 2020
ef82e23
Merge branch 'master' of github.com:cosmos/cosmos-sdk into aaronc/pro…
aaronc Apr 29, 2020
82a7a8a
WIP on ADR 019 Any init
aaronc Apr 29, 2020
08a02d3
Merge branch 'master' into aaronc/proto-any-init
aaronc Apr 29, 2020
d9ef343
Merge branch 'master' of github.com:cosmos/cosmos-sdk into aaronc/pro…
aaronc Apr 29, 2020
a3ee74b
WIP on fixing tests
aaronc Apr 29, 2020
22828ad
Fix buf.yaml
aaronc Apr 29, 2020
86d42c1
WIP on test fixes
aaronc Apr 29, 2020
09051fc
Remove duplicate Any type registration
aaronc Apr 29, 2020
a92a8e9
reorganize, nolint
aaronc Apr 29, 2020
3ef37a0
Fix tests, reorganization
aaronc Apr 30, 2020
57c9842
Merge branch 'master' of github.com:cosmos/cosmos-sdk into aaronc/pro…
aaronc Apr 30, 2020
bf2491d
Doc and linting updates
aaronc Apr 30, 2020
1e174a3
Doc and linting updates
aaronc Apr 30, 2020
3c0bfaf
Test fix
aaronc Apr 30, 2020
3835634
wsl
aaronc Apr 30, 2020
83e1be2
Improve tests
aaronc Apr 30, 2020
3fa3d97
Improve tests
aaronc Apr 30, 2020
68ee2c8
Merge branch 'master' into aaronc/proto-any-init
Apr 30, 2020
7ef3a22
Update scripts/protocgen-any.sh
aaronc May 1, 2020
eeb871d
Merge branch 'master' into aaronc/proto-any-init
aaronc May 1, 2020
2ad95ff
Refactor evidence Codec from std to x/evidence
aaronc May 1, 2020
d7d5506
Merge branch 'aaronc/proto-any-init' of github.com:cosmos/cosmos-sdk …
aaronc May 1, 2020
8fe1ad2
Address review feedback
aaronc May 1, 2020
ef0c11d
Fix bug
aaronc May 1, 2020
84560f1
Address review feedback
aaronc May 1, 2020
99c0034
fix tests
aaronc May 1, 2020
4f24c55
Remove GetCodec from evidence Keeper
aaronc May 1, 2020
1c93275
Change evidence NewKeeper to just take an evidence Codec
aaronc May 1, 2020
b5a95a8
Make NewAnyWithValue require a proto.Message
aaronc May 1, 2020
de52d69
Fix error
aaronc May 1, 2020
3ada5b1
Update codec/types/any.go
aaronc May 1, 2020
9015a45
Update codec/types/interface_registry.go
aaronc May 1, 2020
6306127
Update x/evidence/types/codec.go
aaronc May 1, 2020
96cbff7
Update x/evidence/types/codec.go
aaronc May 1, 2020
ba19f15
Update x/evidence/types/codec.go
aaronc May 1, 2020
be25032
Update codec/types/any.go
aaronc May 1, 2020
2293862
Merge branch 'master' into aaronc/proto-any-init
alexanderbez May 1, 2020
0d050d8
fix tests
aaronc May 1, 2020
cd60b8f
Merge branch 'master' into aaronc/proto-any-init
alexanderbez May 2, 2020
a42a387
Merge branch 'master' of github.com:cosmos/cosmos-sdk into aaronc/pro…
aaronc May 4, 2020
b1a5d8e
Merge branch 'master' of github.com:cosmos/cosmos-sdk into aaronc/pro…
aaronc May 4, 2020
b167e09
Merge branch 'master' into aaronc/proto-any-init
aaronc May 4, 2020
49e0538
Merge branch 'master' into aaronc/proto-any-init
alexanderbez May 4, 2020
3a5931c
Merge branch 'master' into aaronc/proto-any-init
aaronc May 5, 2020
70eba6f
Merge branch 'master' into aaronc/proto-any-init
alexanderbez May 5, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ proto-all: proto-gen proto-lint proto-check-breaking
proto-gen:
@./scripts/protocgen.sh

proto-gen-any:
@./scripts/protocgen-any.sh

proto-lint:
@buf check lint --error-format=json

Expand Down
6 changes: 4 additions & 2 deletions codec/hybrid_codec.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package codec

import "github.com/cosmos/cosmos-sdk/codec/types"

// HybridCodec defines a codec that utilizes Protobuf for binary encoding
// and Amino for JSON encoding.
type HybridCodec struct {
proto Marshaler
amino Marshaler
}

func NewHybridCodec(amino *Codec) Marshaler {
func NewHybridCodec(amino *Codec, unpacker types.AnyUnpacker) Marshaler {
return &HybridCodec{
proto: NewProtoCodec(),
proto: NewProtoCodec(unpacker),
amino: NewAminoCodec(amino),
}
}
Expand Down
22 changes: 17 additions & 5 deletions codec/proto_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ import (
"fmt"
"strings"

"github.com/cosmos/cosmos-sdk/codec/types"

"github.com/gogo/protobuf/jsonpb"
)

// ProtoCodec defines a codec that utilizes Protobuf for both binary and JSON
// encoding.
type ProtoCodec struct{}
type ProtoCodec struct {
anyUnpacker types.AnyUnpacker
}

func NewProtoCodec() Marshaler {
return &ProtoCodec{}
func NewProtoCodec(anyUnpacker types.AnyUnpacker) Marshaler {
return &ProtoCodec{anyUnpacker: anyUnpacker}
}

func (pc *ProtoCodec) MarshalBinaryBare(o ProtoMarshaler) ([]byte, error) {
Expand Down Expand Up @@ -58,7 +62,15 @@ func (pc *ProtoCodec) MustMarshalBinaryLengthPrefixed(o ProtoMarshaler) []byte {
}

func (pc *ProtoCodec) UnmarshalBinaryBare(bz []byte, ptr ProtoMarshaler) error {
return ptr.Unmarshal(bz)
err := ptr.Unmarshal(bz)
if err != nil {
return err
}
err = types.UnpackInterfaces(ptr, pc.anyUnpacker)
if err != nil {
return err
}
return nil
}

func (pc *ProtoCodec) MustUnmarshalBinaryBare(bz []byte, ptr ProtoMarshaler) {
Expand All @@ -80,7 +92,7 @@ func (pc *ProtoCodec) UnmarshalBinaryLengthPrefixed(bz []byte, ptr ProtoMarshale
}

bz = bz[n:]
return ptr.Unmarshal(bz)
return pc.UnmarshalBinaryBare(bz, ptr)
}

func (pc *ProtoCodec) MustUnmarshalBinaryLengthPrefixed(bz []byte, ptr ProtoMarshaler) {
Expand Down
188 changes: 188 additions & 0 deletions codec/types/any.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package types

import (
"fmt"
"reflect"

"github.com/gogo/protobuf/proto"
)

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.
//
aaronc marked this conversation as resolved.
Show resolved Hide resolved
TypeUrl string `protobuf:"bytes,1,opt,name=type_url,json=typeUrl,proto3" json:"type_url,omitempty"`
aaronc marked this conversation as resolved.
Show resolved Hide resolved
// 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:"-"`
aaronc marked this conversation as resolved.
Show resolved Hide resolved
XXX_unrecognized []byte `json:"-"`
aaronc marked this conversation as resolved.
Show resolved Hide resolved
XXX_sizecache int32 `json:"-"`
aaronc marked this conversation as resolved.
Show resolved Hide resolved
cachedValue interface{}
}

func NewAnyWithValue(value interface{}) (*Any, error) {
any := &Any{}
msg, ok := value.(proto.Message)
if !ok {
return nil, fmt.Errorf("can't pack %T", msg)
}
err := any.Pack(msg)
aaronc marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, err
}
return any, nil
}

type interfaceMap = map[string]reflect.Type

type interfaceRegistry struct {
interfaceNames map[string]reflect.Type
interfaceImpls map[reflect.Type]interfaceMap
}

func NewInterfaceRegistry() InterfaceRegistry {
return &interfaceRegistry{
interfaceNames: map[string]reflect.Type{},
interfaceImpls: map[reflect.Type]interfaceMap{},
}
}

type InterfaceRegistry interface {
AnyUnpacker

RegisterInterface(protoName string, ptr interface{})
RegisterImplementation(iface interface{}, impl proto.Message)
}

type AnyUnpacker interface {
UnpackAny(any *Any, iface interface{}) error
}

func (a *interfaceRegistry) RegisterInterface(protoName string, ptr interface{}) {
a.interfaceNames[protoName] = reflect.TypeOf(ptr)
}

func (a *interfaceRegistry) RegisterImplementation(iface interface{}, impl proto.Message) {
ityp := reflect.TypeOf(iface).Elem()
imap, found := a.interfaceImpls[ityp]
if !found {
imap = map[string]reflect.Type{}
}
implType := reflect.TypeOf(impl)
if !implType.AssignableTo(ityp) {
panic(fmt.Errorf("type %T doesn't actually implement interface %T", implType, ityp))
}
imap["/"+proto.MessageName(impl)] = implType
a.interfaceImpls[ityp] = imap
}

func (any *Any) Pack(x proto.Message) error {
any.TypeUrl = "/" + proto.MessageName(x)
bz, err := proto.Marshal(x)
if err != nil {
return err
}
any.Value = bz
aaronc marked this conversation as resolved.
Show resolved Hide resolved
any.cachedValue = x
return nil
}

func (any *Any) CachedValue() interface{} {
return any.cachedValue
}

func (ctx *interfaceRegistry) UnpackAny(any *Any, iface interface{}) error {
aaronc marked this conversation as resolved.
Show resolved Hide resolved
rv := reflect.ValueOf(iface)
if rv.Kind() != reflect.Ptr {
return fmt.Errorf("UnpackAny expects a pointer")
}
rt := rv.Elem().Type()
cachedValue := any.cachedValue
if cachedValue != nil {
if reflect.TypeOf(cachedValue).AssignableTo(rt) {
rv.Elem().Set(reflect.ValueOf(cachedValue))
return nil
}
}
imap, found := ctx.interfaceImpls[rt]
if !found {
return fmt.Errorf("no registered implementations of interface type %T", iface)
}
typ, found := imap[any.TypeUrl]
if !found {
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)
}
err := proto.Unmarshal(any.Value, msg)
if err != nil {
return err
}
err = UnpackInterfaces(msg, ctx)
if err != nil {
return err
}
rv.Elem().Set(reflect.ValueOf(msg))
any.cachedValue = msg
return nil
}

func MarshalAny(x interface{}) ([]byte, error) {
msg, ok := x.(proto.Message)
if !ok {
return nil, fmt.Errorf("can't proto marshal %T", x)
}
any := &Any{}
aaronc marked this conversation as resolved.
Show resolved Hide resolved
err := any.Pack(msg)
if err != nil {
return nil, err
}
return any.Marshal()
}

func UnmarshalAny(ctx AnyUnpacker, iface interface{}, bz []byte) error {
any := &Any{}
err := any.Unmarshal(bz)
if err != nil {
return err
}
return ctx.UnpackAny(any, iface)
}

type UnpackInterfacesMsg interface {
UnpackInterfaces(ctx AnyUnpacker) error
}

func UnpackInterfaces(x interface{}, ctx AnyUnpacker) error {
if msg, ok := x.(UnpackInterfacesMsg); ok {
return msg.UnpackInterfaces(ctx)
}
return nil
}
Loading