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

refactor(x/tx): use dynamicpb instead of type resolver in any marshal #16048

Merged
merged 50 commits into from
May 8, 2023
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
09d9e6b
refactor(sims): rm usage of GetSignBytes from NewOperationMsg
kocubinski May 4, 2023
82ca71f
replace usage of GetSignBytes with AminoJSON encoder
kocubinski May 4, 2023
3091b43
add comment
kocubinski May 4, 2023
e7bf7bb
fix comment
kocubinski May 5, 2023
4a367d4
mid work
kocubinski May 5, 2023
5d45211
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocubinski/s…
kocubinski May 5, 2023
f28eda3
try allow unnamed any types in sims
kocubinski May 5, 2023
53b1522
push the bad up to sims package
kocubinski May 5, 2023
38c95b8
revert proto gen (out of scope)
kocubinski May 5, 2023
bbf9a4e
revert go mod change
kocubinski May 5, 2023
01e92b9
improve comment of impots
kocubinski May 5, 2023
2c120b5
spelling
kocubinski May 5, 2023
b7e7c22
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocubinski/s…
kocubinski May 5, 2023
1370739
use dynamicpb for marshal target
kocubinski May 5, 2023
a9e188d
rm generated imports
kocubinski May 5, 2023
a2127f8
chore(x/tx): Update changelog for x/tx v0.6.3.
kocubinski May 5, 2023
3986662
update to x/tx/v0.6.3
kocubinski May 5, 2023
0e4559d
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocubinski/s…
kocubinski May 5, 2023
5a92828
update constructor call
kocubinski May 5, 2023
ec384c5
aminojson.Encoder now takes EncoderOptions as an arugment.
kocubinski May 5, 2023
3b11f31
update changelog
kocubinski May 5, 2023
b1f4fde
Merge branch 'kocubinski/x-tx-0.6.3' into kocubinski/x-tx-0.7.0
kocubinski May 5, 2023
890a927
using dynamicpb instead of type resolver
kocubinski May 5, 2023
5815c54
go mod tidy
kocubinski May 5, 2023
abf3c56
go mod tidy
kocubinski May 5, 2023
1ba32d7
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocubinski/x…
kocubinski May 6, 2023
e95633b
refactoring
kocubinski May 6, 2023
184baab
simplify NewOperationMsg with aminojson changes
kocubinski May 6, 2023
20517ce
Revert "simplify NewOperationMsg with aminojson changes"
kocubinski May 6, 2023
8d6dc66
add comments
kocubinski May 7, 2023
7209dec
lint fix
kocubinski May 7, 2023
0dbf476
use signing.ProtoFileResolver throughout
kocubinski May 7, 2023
e18817b
fix spelling
kocubinski May 7, 2023
58c019f
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocubinski/x…
kocubinski May 7, 2023
4636726
go mod tidy
kocubinski May 7, 2023
90527b4
rm extra whitespace
kocubinski May 7, 2023
87cfdda
clean up comment
kocubinski May 7, 2023
0a76cb9
Merge branch 'main' into kocubinski/x-tx-0.7.0
kocubinski May 8, 2023
70a6870
Create MarshalAminoJSON on ProtoCodec
kocubinski May 8, 2023
f16c9e7
clean up comment
kocubinski May 8, 2023
f63f47e
use struct interfaceRegistry as resolver
kocubinski May 8, 2023
0687069
Merge branch 'main' into kocubinski/x-tx-0.7.0
kocubinski May 8, 2023
51dca43
include tip in signDoc
kocubinski May 8, 2023
ad360d4
Merge branch 'kocubinski/x-tx-0.7.0' of github.com:cosmos/cosmos-sdk …
kocubinski May 8, 2023
ba4a5e7
Update codec/proto_codec.go
kocubinski May 8, 2023
693c267
add test coverage for both set and unset tip
kocubinski May 8, 2023
73cac56
Merge branch 'kocubinski/x-tx-0.7.0' of github.com:cosmos/cosmos-sdk …
kocubinski May 8, 2023
e9c8c53
only fallback to dynamicpb in ProtoCodec.MarshalAminoJSON when type n…
kocubinski May 8, 2023
93d1362
fixing some whitespace
kocubinski May 8, 2023
09e7597
add nil guard
kocubinski May 8, 2023
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
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ require (
cosmossdk.io/log v1.1.0
cosmossdk.io/math v1.0.0
cosmossdk.io/store v0.1.0-alpha.1.0.20230328185921-37ba88872dbc
cosmossdk.io/x/tx v0.6.1
cosmossdk.io/x/tx v0.6.3
github.com/99designs/keyring v1.2.1
github.com/armon/go-metrics v0.4.1
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816
Expand Down Expand Up @@ -164,7 +164,7 @@ require (
replace (
cosmossdk.io/core => ./core
cosmossdk.io/store => ./store
// TODO: remove after release 0.6.2
// TODO: remove after 0.7.0 release
cosmossdk.io/x/tx => ./x/tx
// use cosmos fork of keyring
github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0
Expand Down
6 changes: 3 additions & 3 deletions simapp/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ require (
cloud.google.com/go/storage v1.30.0 // indirect
cosmossdk.io/collections v0.1.0 // indirect
cosmossdk.io/errors v1.0.0-beta.7.0.20230429155654-3ee8242364e4 // indirect
cosmossdk.io/x/tx v0.6.1 // indirect
cosmossdk.io/x/tx v0.6.3 // indirect
filippo.io/edwards25519 v1.0.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.1 // indirect
Expand Down Expand Up @@ -204,14 +204,14 @@ replace (
cosmossdk.io/x/evidence => ../x/evidence
cosmossdk.io/x/feegrant => ../x/feegrant
cosmossdk.io/x/nft => ../x/nft
// TODO: remove after release 0.6.2
cosmossdk.io/x/tx => ../x/tx
cosmossdk.io/x/upgrade => ../x/upgrade
)

// Below are the long-lived replace of the SimApp
replace (
cosmossdk.io/core => ../core
// TODO: remove after 0.7.0 release
cosmossdk.io/x/tx => ../x/tx
// use cosmos fork of keyring
github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0
// Simapp always use the latest version of the cosmos-sdk
Expand Down
7 changes: 3 additions & 4 deletions tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
cosmossdk.io/x/evidence v0.1.0
cosmossdk.io/x/feegrant v0.0.0-20230117113717-50e7c4a4ceff
cosmossdk.io/x/nft v0.0.0-20230113085233-fae3332d62fc
cosmossdk.io/x/tx v0.6.1
cosmossdk.io/x/tx v0.6.3
cosmossdk.io/x/upgrade v0.0.0-20230127052425-54c8e1568335
github.com/cometbft/cometbft v0.37.1
github.com/cosmos/cosmos-db v1.0.0-rc.1
Expand Down Expand Up @@ -200,14 +200,13 @@ replace (
cosmossdk.io/x/upgrade => ../x/upgrade
)

// temporary replace
replace cosmossdk.io/x/tx => ../x/tx

// Below are the long-lived replace for tests.
replace (
cosmossdk.io/core => ../core
// We always want to test against the latest version of the simapp.
cosmossdk.io/simapp => ../simapp
// TODO: remove after 0.7.0 release
cosmossdk.io/x/tx => ../x/tx
github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0
// We always want to test against the latest version of the SDK.
github.com/cosmos/cosmos-sdk => ../.
Expand Down
6 changes: 3 additions & 3 deletions tests/integration/aminojson/aminojson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func TestAminoJSON_Equivalence(t *testing.T) {
distribution.AppModuleBasic{}, evidence.AppModuleBasic{}, feegrantmodule.AppModuleBasic{},
gov.AppModuleBasic{}, groupmodule.AppModuleBasic{}, mint.AppModuleBasic{}, params.AppModuleBasic{},
slashing.AppModuleBasic{}, staking.AppModuleBasic{}, upgrade.AppModuleBasic{}, vesting.AppModuleBasic{})
aj := aminojson.NewEncoder()
aj := aminojson.NewEncoder(aminojson.EncoderOptions{})

for _, tt := range rapidgen.DefaultGeneratedTypes {
name := string(tt.Pulsar.ProtoReflect().Descriptor().FullName())
Expand Down Expand Up @@ -193,7 +193,7 @@ func TestAminoJSON_LegacyParity(t *testing.T) {
bank.AppModuleBasic{}, distribution.AppModuleBasic{}, slashing.AppModuleBasic{}, staking.AppModuleBasic{},
vesting.AppModuleBasic{})

aj := aminojson.NewEncoder()
aj := aminojson.NewEncoder(aminojson.EncoderOptions{})
addr1 := types.AccAddress("addr1")
now := time.Now()

Expand Down Expand Up @@ -481,7 +481,7 @@ func TestSendAuthorization(t *testing.T) {
encCfg := testutil.MakeTestEncodingConfig(auth.AppModuleBasic{}, authzmodule.AppModuleBasic{},
distribution.AppModuleBasic{}, bank.AppModuleBasic{})

aj := aminojson.NewEncoder()
aj := aminojson.NewEncoder(aminojson.EncoderOptions{})

// beware, Coins has as custom MarshalJSON method which changes how nil is handled
// nil -> [] (empty list)
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/aminojson/repeated_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (

func TestRepeatedFields(t *testing.T) {
cdc := codec.NewLegacyAmino()
aj := aminojson.NewEncoder()
aj := aminojson.NewEncoder(aminojson.EncoderOptions{})

cases := map[string]struct {
gogo gogoproto.Message
Expand Down
2 changes: 1 addition & 1 deletion tools/rosetta/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ require (
cosmossdk.io/depinject v1.0.0-alpha.3 // indirect
cosmossdk.io/errors v1.0.0-beta.7.0.20230429155654-3ee8242364e4 // indirect
cosmossdk.io/store v0.1.0-alpha.1.0.20230328185921-37ba88872dbc // indirect
cosmossdk.io/x/tx v0.6.1 // indirect
cosmossdk.io/x/tx v0.6.3 // indirect
filippo.io/edwards25519 v1.0.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.1 // indirect
Expand Down
38 changes: 34 additions & 4 deletions types/simulation/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@ package simulation

import (
"encoding/json"
"fmt"
"math/rand"
"time"

gogoproto "github.com/cosmos/gogoproto/proto"
"google.golang.org/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/dynamicpb"

"cosmossdk.io/x/tx/signing/aminojson"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/kv"
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
)

// Deprecated: Use WeightedProposalMsg instead.
Expand Down Expand Up @@ -95,11 +101,35 @@ func NewOperationMsg(msg sdk.Msg, ok bool, comment string, cdc *codec.ProtoCodec
moduleName = msgType
}

if legacyMsg, okType := msg.(legacytx.LegacyMsg); okType {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option I explored was packing sdk.Msg into an any message and then marshaling that, but if we do
the encoder will reject messages that are not tagged with an amino.name. Strictly speaking we don't need
an amino.name on messages which aren't expected to be packed into any fields except for this one case.

return NewOperationMsgBasic(moduleName, msgType, comment, ok, legacyMsg.GetSignBytes())
// Below is logic added for aminojson.Encoder compatibility with gogoproto messages.
// x/tx/signing/aminojson cannot marshal sdk.Msg (an alias to gogoproto.message) directly since this type is not
// protoreflect enabled like the std library google.golang.org/protobuf/proto.Message is.
//
// So we just convert to a dynamicpb message and marshal that directly
resolver := gogoproto.HybridResolver
desc, err := resolver.FindDescriptorByName(protoreflect.FullName(gogoproto.MessageName(msg)))
if err != nil {
panic(fmt.Errorf("failed to find proto descriptor for %s: %w", msgType, err))
}

dynamicMsgType := dynamicpb.NewMessageType(desc.(protoreflect.MessageDescriptor))
dynamicMsg := dynamicMsgType.New().Interface()
gogoBytes, err := gogoproto.Marshal(msg)
if err != nil {
panic(fmt.Errorf("failed to marshal msg: %w", err))
}
err = proto.Unmarshal(gogoBytes, dynamicMsg)
if err != nil {
panic(fmt.Errorf("failed to unmarshal msg: %w", err))
}

encoder := aminojson.NewEncoder(aminojson.EncoderOptions{FileResolver: resolver})
jsonBytes, err := encoder.Marshal(dynamicMsg)
if err != nil {
panic(fmt.Errorf("failed to marshal msg: %w", err))
}
Copy link
Member

@aaronc aaronc May 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this will be pretty common let's create some helpers for it. I would even go as far as saying that we can add a MarshalAminoJSON method to codec.Codec which does exactly this - it already has the proper file resolver from InterfaceRegistry

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you suggesting adding MarshalAminoJSON to JSONCodec?


return NewOperationMsgBasic(moduleName, msgType, comment, ok, cdc.MustMarshalJSON(msg))
return NewOperationMsgBasic(moduleName, msgType, comment, ok, jsonBytes)
}

// NoOpMsg - create a no-operation message
Expand Down
2 changes: 0 additions & 2 deletions x/auth/tx/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,9 @@ func NewTxConfigWithOptions(protoCodec codec.ProtoCodecMarshaler, configOptions
panic(err)
}
case signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON:
aminoJSONEncoder := aminojson.NewEncoder()
handlers[i] = aminojson.NewSignModeHandler(aminojson.SignModeHandlerOptions{
FileResolver: signingOpts.FileResolver,
TypeResolver: signingOpts.TypeResolver,
Encoder: &aminoJSONEncoder,
})
case signingtypes.SignMode_SIGN_MODE_TEXTUAL:
handlers[i], err = textual.NewSignModeHandler(textual.SignModeOptions{
Expand Down
2 changes: 1 addition & 1 deletion x/evidence/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ require (

require (
cosmossdk.io/collections v0.1.0 // indirect
cosmossdk.io/x/tx v0.6.1 // indirect
cosmossdk.io/x/tx v0.6.3 // indirect
filippo.io/edwards25519 v1.0.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.1 // indirect
Expand Down
7 changes: 7 additions & 0 deletions x/tx/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ Ref: https://keepachangelog.com/en/1.0.0/

## Unreleased

## v0.7.0

### API Breaking

* [#16044](https://github.com/cosmos/cosmos-sdk/pull/16044): rename aminojson.NewAminoJSON -> aminojson.NewEncoder
* [#16047](https://github.com/cosmos/cosmos-sdk/pull/16047): aminojson.NewEncoder now takes EncoderOptions as an argument.

## v0.6.2

### Improvements
Expand Down
10 changes: 6 additions & 4 deletions x/tx/signing/aminojson/aminojson.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"

"google.golang.org/protobuf/reflect/protodesc"
"google.golang.org/protobuf/reflect/protoregistry"

signingv1beta1 "cosmossdk.io/api/cosmos/tx/signing/v1beta1"
Expand All @@ -16,14 +15,14 @@ import (

// SignModeHandler implements the SIGN_MODE_LEGACY_AMINO_JSON signing mode.
type SignModeHandler struct {
fileResolver protodesc.Resolver
fileResolver signing.ProtoFileResolver
typeResolver protoregistry.MessageTypeResolver
encoder Encoder
}

// SignModeHandlerOptions are the options for the SignModeHandler.
type SignModeHandlerOptions struct {
FileResolver protodesc.Resolver
FileResolver signing.ProtoFileResolver
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we just use the file resolver on the Encoder? Or is this so that Encoder can be left nil?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right this supports he case where Encoder is nil

TypeResolver protoregistry.MessageTypeResolver
Encoder *Encoder
}
Expand All @@ -42,7 +41,10 @@ func NewSignModeHandler(options SignModeHandlerOptions) *SignModeHandler {
h.typeResolver = options.TypeResolver
}
if options.Encoder == nil {
h.encoder = NewEncoder()
h.encoder = NewEncoder(EncoderOptions{
FileResolver: options.FileResolver,
TypeResolver: options.TypeResolver,
})
} else {
h.encoder = *options.Encoder
}
Expand Down
2 changes: 1 addition & 1 deletion x/tx/signing/aminojson/aminojson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func TestAminoJsonSignMode(t *testing.T) {
func TestNewSignModeHandler(t *testing.T) {
handler := aminojson.NewSignModeHandler(aminojson.SignModeHandlerOptions{})
require.NotNil(t, handler)
aj := aminojson.NewEncoder()
aj := aminojson.NewEncoder(aminojson.EncoderOptions{})
handler = aminojson.NewSignModeHandler(aminojson.SignModeHandlerOptions{
FileResolver: protoregistry.GlobalFiles,
TypeResolver: protoregistry.GlobalTypes,
Expand Down
77 changes: 65 additions & 12 deletions x/tx/signing/aminojson/any.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,87 @@ import (
"fmt"
"io"

"google.golang.org/protobuf/types/dynamicpb"
"google.golang.org/protobuf/types/known/anypb"

"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
)

func (enc Encoder) marshalAny(message protoreflect.Message, writer io.Writer) error {
anyMsg := message.Interface().(*anypb.Any)
resolver := protoregistry.GlobalTypes
// when a message contains a nested any field, and the top-level message has been unmarshalled into a dyanmicpb,
// the nested any field will also be a dynamicpb. In this case, we must use the dynamicpb API.
_, ok := message.Interface().(*dynamicpb.Message)
if ok {
return enc.marshalDynamic(message, writer)
}

typ, err := resolver.FindMessageByURL(anyMsg.TypeUrl)
if err != nil {
return errors.Wrapf(err, "can't resolve type URL %s", anyMsg.TypeUrl)
anyMsg, ok := message.Interface().(*anypb.Any)
if !ok {
return fmt.Errorf("expected *anypb.Any, got %T", message.Interface())
}

valueMsg := typ.New()
err = proto.Unmarshal(anyMsg.Value, valueMsg.Interface())
if err != nil {
return err
// build a message of the correct type
var protoMessage protoreflect.Message
typ, err := enc.typeResolver.FindMessageByURL(anyMsg.TypeUrl)
if err == nil {
// If the type is registered, we can use the proto API to unmarshal into a concrete type.
valueMsg := typ.New()
err = proto.Unmarshal(anyMsg.Value, valueMsg.Interface())
if err != nil {
return err
}
protoMessage = valueMsg
} else {
// otherwise we use the dynamicpb API to unmarshal into a dynamic message.
desc, err := enc.fileResolver.FindDescriptorByName(protoreflect.FullName(anyMsg.TypeUrl[1:]))
if err != nil {
return errors.Wrapf(err, "can't resolve type URL %s", anyMsg.TypeUrl)
}

valueMsg := dynamicpb.NewMessageType(desc.(protoreflect.MessageDescriptor)).New().Interface()
err = proto.Unmarshal(anyMsg.Value, valueMsg)
if err != nil {
return err
}
protoMessage = valueMsg.ProtoReflect()
}

_, named := getMessageAminoName(valueMsg)
_, named := getMessageAminoName(protoMessage.Descriptor().Options())
if !named {
return fmt.Errorf("message %s is packed into an any field, so requires an amino.name annotation",
anyMsg.TypeUrl)
}

return enc.beginMarshal(valueMsg, writer)
return enc.beginMarshal(protoMessage, writer)
}

const (
anyTypeURLFieldName = "type_url"
anyValueFieldName = "value"
)

func (enc Encoder) marshalDynamic(message protoreflect.Message, writer io.Writer) error {
msgName := message.Get(message.Descriptor().Fields().ByName(anyTypeURLFieldName)).String()[1:]
msgBytes := message.Get(message.Descriptor().Fields().ByName(anyValueFieldName)).Bytes()

desc, err := enc.fileResolver.FindDescriptorByName(protoreflect.FullName(msgName))
if err != nil {
return errors.Wrapf(err, "can't resolve type URL %s", msgName)
}

_, named := getMessageAminoName(desc.Options())
if !named {
return fmt.Errorf("message %s is packed into an any field, so requires an amino.name annotation",
msgName)
}

valueMsg := dynamicpb.NewMessageType(desc.(protoreflect.MessageDescriptor)).New().Interface()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should use the type from the type resolver if it resolves first, if not then use dynamicpb

err = proto.Unmarshal(msgBytes, valueMsg)
if err != nil {
return err
}

return enc.beginMarshal(valueMsg.ProtoReflect(), writer)
}
Loading