feat: protoreflect based amino json encoder (#14877)
This commit is contained in:
parent
7d28f63291
commit
976ecd4c08
@ -13,9 +13,11 @@ 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.2.0
|
||||
cosmossdk.io/x/upgrade v0.0.0-20230127052425-54c8e1568335
|
||||
github.com/cometbft/cometbft v0.37.0-alpha.3
|
||||
github.com/cosmos/cosmos-db v1.0.0-rc.1
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.2
|
||||
// this version is not used as it is always replaced by the latest Cosmos SDK version
|
||||
github.com/cosmos/cosmos-sdk v0.48.0
|
||||
github.com/cosmos/gogoproto v1.4.6
|
||||
@ -37,7 +39,6 @@ require (
|
||||
cosmossdk.io/client/v2 v2.0.0-20230220152935-67f04e629623 // indirect
|
||||
cosmossdk.io/collections v0.0.0-20230214153846-b6c6e4e99177 // indirect
|
||||
cosmossdk.io/core v0.5.1 // indirect
|
||||
cosmossdk.io/x/tx v0.2.0 // 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
|
||||
@ -61,7 +62,6 @@ require (
|
||||
github.com/cometbft/cometbft-db v0.7.0 // indirect
|
||||
github.com/confio/ics23/go v0.9.0 // indirect
|
||||
github.com/cosmos/btcutil v1.0.5 // indirect
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.2 // indirect
|
||||
github.com/cosmos/go-bip39 v1.0.0 // indirect
|
||||
github.com/cosmos/gogogateway v1.2.0 // indirect
|
||||
github.com/cosmos/iavl v0.21.0-alpha.1 // indirect
|
||||
@ -113,6 +113,7 @@ require (
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.1.0 // indirect
|
||||
github.com/huandu/skiplist v1.2.0 // indirect
|
||||
github.com/iancoleman/strcase v0.2.0 // indirect
|
||||
github.com/improbable-eng/grpc-web v0.15.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
@ -191,9 +192,11 @@ require (
|
||||
// It must be in sync with SimApp temporary replaces
|
||||
replace (
|
||||
// TODO tag all extracted modules after SDK refactor
|
||||
cosmossdk.io/client/v2 => ../client/v2
|
||||
cosmossdk.io/x/evidence => ../x/evidence
|
||||
cosmossdk.io/x/feegrant => ../x/feegrant
|
||||
cosmossdk.io/x/nft => ../x/nft
|
||||
cosmossdk.io/x/tx => ../x/tx
|
||||
cosmossdk.io/x/upgrade => ../x/upgrade
|
||||
// This can be deleted after the CometBFT PR is merged
|
||||
github.com/cometbft/cometbft => github.com/cometbft/cometbft v0.0.0-20230203130311-387422ac220d
|
||||
|
||||
@ -190,8 +190,6 @@ cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1V
|
||||
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
|
||||
cosmossdk.io/api v0.3.1 h1:NNiOclKRR0AOlO4KIqeaG6PS6kswOMhHD0ir0SscNXE=
|
||||
cosmossdk.io/api v0.3.1/go.mod h1:DfHfMkiNA2Uhy8fj0JJlOCYOBp4eWUUJ1te5zBGNyIw=
|
||||
cosmossdk.io/client/v2 v2.0.0-20230220152935-67f04e629623 h1:QzZA1P+twvj10ylCVXzHodLge+RvrwoNIlWpU+MrRa0=
|
||||
cosmossdk.io/client/v2 v2.0.0-20230220152935-67f04e629623/go.mod h1:Yo6R3XSXKxi+G642kK7+ZOh2czNTwTO1b8ywOvwaFQo=
|
||||
cosmossdk.io/collections v0.0.0-20230214153846-b6c6e4e99177 h1:At2M0aYQKEAWqr6JtZrJPOlfhdvENiGwJg2NCNHwIdc=
|
||||
cosmossdk.io/collections v0.0.0-20230214153846-b6c6e4e99177/go.mod h1:uqCi8FG+Bh2vv/qf5xZ8iab0E0c/DMA/cwafbp8dkcU=
|
||||
cosmossdk.io/core v0.5.1 h1:vQVtFrIYOQJDV3f7rw4pjjVqc1id4+mE0L9hHP66pyI=
|
||||
@ -206,8 +204,6 @@ cosmossdk.io/math v1.0.0-beta.6.0.20230216172121-959ce49135e4 h1:/jnzJ9zFsL7qkV8
|
||||
cosmossdk.io/math v1.0.0-beta.6.0.20230216172121-959ce49135e4/go.mod h1:gUVtWwIzfSXqcOT+lBVz2jyjfua8DoBdzRsIyaUAT/8=
|
||||
cosmossdk.io/store v0.0.0-20230206092147-e03195e4b8a7 h1:IwyDN/YaQmF+Pmuv8d7vRWMM/k2RjSmPBycMcmd3ICE=
|
||||
cosmossdk.io/store v0.0.0-20230206092147-e03195e4b8a7/go.mod h1:1XOtuYs7jsfQkn7G3VQXB6I+2tHXKHZw2U/AafNbnlk=
|
||||
cosmossdk.io/x/tx v0.2.0 h1:53f5TIXhpPYJGMm47SUslcV2i8JNBEN3eE08BmxE/Zg=
|
||||
cosmossdk.io/x/tx v0.2.0/go.mod h1:CTko7wgt7aBdbxOesZ+Wo1uO/03ueKzIQ0iI323Rqgk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
|
||||
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||
@ -685,6 +681,8 @@ github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw
|
||||
github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
|
||||
github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
|
||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||
|
||||
1
tests/integration/aminojson/.gitignore
vendored
Normal file
1
tests/integration/aminojson/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
testdata/rapid
|
||||
684
tests/integration/aminojson/aminojson_test.go
Normal file
684
tests/integration/aminojson/aminojson_test.go
Normal file
@ -0,0 +1,684 @@
|
||||
package aminojson
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
cosmos_proto "github.com/cosmos/cosmos-proto"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"pgregory.net/rapid"
|
||||
|
||||
"github.com/cosmos/cosmos-proto/rapidproto"
|
||||
gogoproto "github.com/cosmos/gogoproto/proto"
|
||||
|
||||
"cosmossdk.io/api/amino"
|
||||
authapi "cosmossdk.io/api/cosmos/auth/v1beta1"
|
||||
authzapi "cosmossdk.io/api/cosmos/authz/v1beta1"
|
||||
bankapi "cosmossdk.io/api/cosmos/bank/v1beta1"
|
||||
v1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
|
||||
consensusapi "cosmossdk.io/api/cosmos/consensus/v1"
|
||||
"cosmossdk.io/api/cosmos/crypto/ed25519"
|
||||
multisigapi "cosmossdk.io/api/cosmos/crypto/multisig"
|
||||
"cosmossdk.io/api/cosmos/crypto/secp256k1"
|
||||
distapi "cosmossdk.io/api/cosmos/distribution/v1beta1"
|
||||
evidenceapi "cosmossdk.io/api/cosmos/evidence/v1beta1"
|
||||
feegrantapi "cosmossdk.io/api/cosmos/feegrant/v1beta1"
|
||||
gov_v1_api "cosmossdk.io/api/cosmos/gov/v1"
|
||||
gov_v1beta1_api "cosmossdk.io/api/cosmos/gov/v1beta1"
|
||||
groupapi "cosmossdk.io/api/cosmos/group/v1"
|
||||
mintapi "cosmossdk.io/api/cosmos/mint/v1beta1"
|
||||
paramsapi "cosmossdk.io/api/cosmos/params/v1beta1"
|
||||
slashingapi "cosmossdk.io/api/cosmos/slashing/v1beta1"
|
||||
stakingapi "cosmossdk.io/api/cosmos/staking/v1beta1"
|
||||
upgradeapi "cosmossdk.io/api/cosmos/upgrade/v1beta1"
|
||||
vestingapi "cosmossdk.io/api/cosmos/vesting/v1beta1"
|
||||
"cosmossdk.io/x/evidence"
|
||||
evidencetypes "cosmossdk.io/x/evidence/types"
|
||||
feegranttypes "cosmossdk.io/x/feegrant"
|
||||
feegrantmodule "cosmossdk.io/x/feegrant/module"
|
||||
"cosmossdk.io/x/tx/aminojson"
|
||||
"cosmossdk.io/x/upgrade"
|
||||
upgradetypes "cosmossdk.io/x/upgrade/types"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
ed25519types "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
|
||||
secp256k1types "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
gogo_testpb "github.com/cosmos/cosmos-sdk/tests/integration/aminojson/internal/gogo/testpb"
|
||||
pulsar_testpb "github.com/cosmos/cosmos-sdk/tests/integration/aminojson/internal/pulsar/testpb"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/bech32"
|
||||
"github.com/cosmos/cosmos-sdk/types/module/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/vesting"
|
||||
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
|
||||
authztypes "github.com/cosmos/cosmos-sdk/x/authz"
|
||||
authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/consensus"
|
||||
consensustypes "github.com/cosmos/cosmos-sdk/x/consensus/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution"
|
||||
disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
gov_v1_types "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
|
||||
gov_v1beta1_types "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
|
||||
grouptypes "github.com/cosmos/cosmos-sdk/x/group"
|
||||
groupmodule "github.com/cosmos/cosmos-sdk/x/group/module"
|
||||
"github.com/cosmos/cosmos-sdk/x/mint"
|
||||
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
"github.com/cosmos/cosmos-sdk/x/params/types/proposal"
|
||||
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
type generatedType struct {
|
||||
pulsar proto.Message
|
||||
gogo gogoproto.Message
|
||||
opts rapidproto.GeneratorOptions
|
||||
}
|
||||
|
||||
func genType(gogo gogoproto.Message, pulsar proto.Message, opts rapidproto.GeneratorOptions) generatedType {
|
||||
return generatedType{
|
||||
pulsar: pulsar,
|
||||
gogo: gogo,
|
||||
opts: opts,
|
||||
}
|
||||
}
|
||||
|
||||
func withDecisionPolicy(opts rapidproto.GeneratorOptions) rapidproto.GeneratorOptions {
|
||||
return opts.
|
||||
WithAnyTypes(
|
||||
&groupapi.ThresholdDecisionPolicy{},
|
||||
&groupapi.PercentageDecisionPolicy{}).
|
||||
WithDisallowNil().
|
||||
WithInterfaceHint("cosmos.group.v1.DecisionPolicy", &groupapi.ThresholdDecisionPolicy{}).
|
||||
WithInterfaceHint("cosmos.group.v1.DecisionPolicy", &groupapi.PercentageDecisionPolicy{})
|
||||
}
|
||||
|
||||
func generatorFieldMapper(t *rapid.T, field protoreflect.FieldDescriptor, name string) (protoreflect.Value, bool) {
|
||||
opts := field.Options()
|
||||
switch {
|
||||
case proto.HasExtension(opts, cosmos_proto.E_Scalar):
|
||||
scalar := proto.GetExtension(opts, cosmos_proto.E_Scalar).(string)
|
||||
switch scalar {
|
||||
case "cosmos.Int":
|
||||
i32 := rapid.Int32().Draw(t, name)
|
||||
return protoreflect.ValueOfString(fmt.Sprintf("%d", i32)), true
|
||||
case "cosmos.Dec":
|
||||
return protoreflect.ValueOfString(""), true
|
||||
}
|
||||
case field.Kind() == protoreflect.BytesKind:
|
||||
if proto.HasExtension(opts, amino.E_Encoding) {
|
||||
encoding := proto.GetExtension(opts, amino.E_Encoding).(string)
|
||||
if encoding == "cosmos_dec_bytes" {
|
||||
return protoreflect.ValueOfBytes([]byte{}), true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return protoreflect.Value{}, false
|
||||
}
|
||||
|
||||
var (
|
||||
genOpts = rapidproto.GeneratorOptions{
|
||||
Resolver: protoregistry.GlobalTypes,
|
||||
FieldMaps: []rapidproto.FieldMapper{generatorFieldMapper},
|
||||
}
|
||||
genTypes = []generatedType{
|
||||
// auth
|
||||
genType(&authtypes.Params{}, &authapi.Params{}, genOpts),
|
||||
genType(&authtypes.BaseAccount{}, &authapi.BaseAccount{}, genOpts.WithAnyTypes(&ed25519.PubKey{})),
|
||||
genType(&authtypes.ModuleAccount{}, &authapi.ModuleAccount{}, genOpts.WithAnyTypes(&ed25519.PubKey{})),
|
||||
genType(&authtypes.ModuleCredential{}, &authapi.ModuleCredential{}, genOpts),
|
||||
genType(&authtypes.MsgUpdateParams{}, &authapi.MsgUpdateParams{}, genOpts.WithDisallowNil()),
|
||||
|
||||
// authz
|
||||
genType(&authztypes.GenericAuthorization{}, &authzapi.GenericAuthorization{}, genOpts),
|
||||
genType(&authztypes.Grant{}, &authzapi.Grant{},
|
||||
genOpts.WithAnyTypes(&authzapi.GenericAuthorization{}).
|
||||
WithDisallowNil().
|
||||
WithInterfaceHint("cosmos.authz.v1beta1.Authorization", &authzapi.GenericAuthorization{}),
|
||||
),
|
||||
genType(&authztypes.MsgGrant{}, &authzapi.MsgGrant{},
|
||||
genOpts.WithAnyTypes(&authzapi.GenericAuthorization{}).
|
||||
WithInterfaceHint("cosmos.authz.v1beta1.Authorization", &authzapi.GenericAuthorization{}).
|
||||
WithDisallowNil(),
|
||||
),
|
||||
genType(&authztypes.MsgExec{}, &authzapi.MsgExec{},
|
||||
genOpts.WithAnyTypes(&authzapi.MsgGrant{}, &authzapi.GenericAuthorization{}).
|
||||
WithDisallowNil().
|
||||
WithInterfaceHint("cosmos.authz.v1beta1.Authorization", &authzapi.GenericAuthorization{}).
|
||||
WithInterfaceHint("cosmos.base.v1beta1.Msg", &authzapi.MsgGrant{}),
|
||||
),
|
||||
|
||||
// bank
|
||||
genType(&banktypes.MsgSend{}, &bankapi.MsgSend{}, genOpts.WithDisallowNil()),
|
||||
genType(&banktypes.MsgMultiSend{}, &bankapi.MsgMultiSend{}, genOpts.WithDisallowNil()),
|
||||
genType(&banktypes.MsgUpdateParams{}, &bankapi.MsgUpdateParams{}, genOpts.WithDisallowNil()),
|
||||
genType(&banktypes.MsgSetSendEnabled{}, &bankapi.MsgSetSendEnabled{}, genOpts),
|
||||
genType(&banktypes.SendAuthorization{}, &bankapi.SendAuthorization{}, genOpts),
|
||||
genType(&banktypes.Params{}, &bankapi.Params{}, genOpts),
|
||||
|
||||
// consensus
|
||||
genType(&consensustypes.MsgUpdateParams{}, &consensusapi.MsgUpdateParams{}, genOpts.WithDisallowNil()),
|
||||
|
||||
// crypto
|
||||
genType(&multisig.LegacyAminoPubKey{}, &multisigapi.LegacyAminoPubKey{},
|
||||
genOpts.WithAnyTypes(&ed25519.PubKey{}, &secp256k1.PubKey{})),
|
||||
|
||||
// distribution
|
||||
genType(&disttypes.MsgWithdrawDelegatorReward{}, &distapi.MsgWithdrawDelegatorReward{}, genOpts),
|
||||
genType(&disttypes.MsgWithdrawValidatorCommission{}, &distapi.MsgWithdrawValidatorCommission{}, genOpts),
|
||||
genType(&disttypes.MsgSetWithdrawAddress{}, &distapi.MsgSetWithdrawAddress{}, genOpts),
|
||||
genType(&disttypes.MsgFundCommunityPool{}, &distapi.MsgFundCommunityPool{}, genOpts),
|
||||
genType(&disttypes.MsgUpdateParams{}, &distapi.MsgUpdateParams{}, genOpts.WithDisallowNil()),
|
||||
genType(&disttypes.MsgCommunityPoolSpend{}, &distapi.MsgCommunityPoolSpend{}, genOpts),
|
||||
genType(&disttypes.MsgDepositValidatorRewardsPool{}, &distapi.MsgDepositValidatorRewardsPool{}, genOpts),
|
||||
genType(&disttypes.Params{}, &distapi.Params{}, genOpts),
|
||||
|
||||
// evidence
|
||||
genType(&evidencetypes.Equivocation{}, &evidenceapi.Equivocation{}, genOpts.WithDisallowNil()),
|
||||
genType(&evidencetypes.MsgSubmitEvidence{}, &evidenceapi.MsgSubmitEvidence{},
|
||||
genOpts.WithAnyTypes(&evidenceapi.Equivocation{}).
|
||||
WithDisallowNil().
|
||||
WithInterfaceHint("cosmos.evidence.v1beta1.Evidence", &evidenceapi.Equivocation{})),
|
||||
|
||||
// feegrant
|
||||
genType(&feegranttypes.MsgGrantAllowance{}, &feegrantapi.MsgGrantAllowance{},
|
||||
genOpts.WithDisallowNil().
|
||||
WithAnyTypes(
|
||||
&feegrantapi.BasicAllowance{},
|
||||
&feegrantapi.PeriodicAllowance{}).
|
||||
WithInterfaceHint("cosmos.feegrant.v1beta1.FeeAllowanceI", &feegrantapi.BasicAllowance{}).
|
||||
WithInterfaceHint("cosmos.feegrant.v1beta1.FeeAllowanceI", &feegrantapi.PeriodicAllowance{}),
|
||||
),
|
||||
genType(&feegranttypes.MsgRevokeAllowance{}, &feegrantapi.MsgRevokeAllowance{}, genOpts),
|
||||
genType(&feegranttypes.BasicAllowance{}, &feegrantapi.BasicAllowance{}, genOpts.WithDisallowNil()),
|
||||
genType(&feegranttypes.PeriodicAllowance{}, &feegrantapi.PeriodicAllowance{}, genOpts.WithDisallowNil()),
|
||||
genType(&feegranttypes.AllowedMsgAllowance{}, &feegrantapi.AllowedMsgAllowance{},
|
||||
genOpts.WithDisallowNil().
|
||||
WithAnyTypes(
|
||||
&feegrantapi.BasicAllowance{},
|
||||
&feegrantapi.PeriodicAllowance{}).
|
||||
WithInterfaceHint("cosmos.feegrant.v1beta1.FeeAllowanceI", &feegrantapi.BasicAllowance{}).
|
||||
WithInterfaceHint("cosmos.feegrant.v1beta1.FeeAllowanceI", &feegrantapi.PeriodicAllowance{}),
|
||||
),
|
||||
|
||||
// gov v1beta1
|
||||
genType(&gov_v1beta1_types.MsgSubmitProposal{}, &gov_v1beta1_api.MsgSubmitProposal{},
|
||||
genOpts.WithAnyTypes(&gov_v1beta1_api.TextProposal{}).
|
||||
WithDisallowNil().
|
||||
WithInterfaceHint("cosmos.gov.v1beta1.Content", &gov_v1beta1_api.TextProposal{}),
|
||||
),
|
||||
genType(&gov_v1beta1_types.MsgDeposit{}, &gov_v1beta1_api.MsgDeposit{}, genOpts),
|
||||
genType(&gov_v1beta1_types.MsgVote{}, &gov_v1beta1_api.MsgVote{}, genOpts),
|
||||
genType(&gov_v1beta1_types.MsgVoteWeighted{}, &gov_v1beta1_api.MsgVoteWeighted{}, genOpts),
|
||||
genType(&gov_v1beta1_types.TextProposal{}, &gov_v1beta1_api.TextProposal{}, genOpts),
|
||||
|
||||
// gov v1
|
||||
genType(&gov_v1_types.MsgSubmitProposal{}, &gov_v1_api.MsgSubmitProposal{},
|
||||
genOpts.WithAnyTypes(&gov_v1_api.MsgVote{}, &gov_v1_api.MsgVoteWeighted{}, &gov_v1_api.MsgDeposit{},
|
||||
&gov_v1_api.MsgExecLegacyContent{}, &gov_v1_api.MsgUpdateParams{}).
|
||||
WithInterfaceHint("cosmos.gov.v1beta1.Content", &gov_v1beta1_api.TextProposal{}).
|
||||
WithDisallowNil(),
|
||||
),
|
||||
genType(&gov_v1_types.MsgDeposit{}, &gov_v1_api.MsgDeposit{}, genOpts),
|
||||
genType(&gov_v1_types.MsgVote{}, &gov_v1_api.MsgVote{}, genOpts),
|
||||
genType(&gov_v1_types.MsgVoteWeighted{}, &gov_v1_api.MsgVoteWeighted{}, genOpts),
|
||||
genType(&gov_v1_types.MsgExecLegacyContent{}, &gov_v1_api.MsgExecLegacyContent{},
|
||||
genOpts.WithAnyTypes(&gov_v1beta1_api.TextProposal{}).
|
||||
WithDisallowNil().
|
||||
WithInterfaceHint("cosmos.gov.v1beta1.Content", &gov_v1beta1_api.TextProposal{})),
|
||||
genType(&gov_v1_types.MsgUpdateParams{}, &gov_v1_api.MsgUpdateParams{}, genOpts.WithDisallowNil()),
|
||||
|
||||
// group
|
||||
genType(&grouptypes.MsgCreateGroup{}, &groupapi.MsgCreateGroup{}, genOpts),
|
||||
genType(&grouptypes.MsgUpdateGroupMembers{}, &groupapi.MsgUpdateGroupMembers{}, genOpts),
|
||||
genType(&grouptypes.MsgUpdateGroupAdmin{}, &groupapi.MsgUpdateGroupAdmin{}, genOpts),
|
||||
genType(&grouptypes.MsgUpdateGroupMetadata{}, &groupapi.MsgUpdateGroupMetadata{}, genOpts),
|
||||
genType(&grouptypes.MsgCreateGroupWithPolicy{}, &groupapi.MsgCreateGroupWithPolicy{},
|
||||
withDecisionPolicy(genOpts)),
|
||||
genType(&grouptypes.MsgCreateGroupPolicy{}, &groupapi.MsgCreateGroupPolicy{},
|
||||
withDecisionPolicy(genOpts)),
|
||||
genType(&grouptypes.MsgUpdateGroupPolicyAdmin{}, &groupapi.MsgUpdateGroupPolicyAdmin{}, genOpts),
|
||||
genType(&grouptypes.MsgUpdateGroupPolicyDecisionPolicy{}, &groupapi.MsgUpdateGroupPolicyDecisionPolicy{},
|
||||
withDecisionPolicy(genOpts)),
|
||||
genType(&grouptypes.MsgUpdateGroupPolicyMetadata{}, &groupapi.MsgUpdateGroupPolicyMetadata{}, genOpts),
|
||||
genType(&grouptypes.MsgSubmitProposal{}, &groupapi.MsgSubmitProposal{},
|
||||
genOpts.WithDisallowNil().
|
||||
WithAnyTypes(&groupapi.MsgCreateGroup{}, &groupapi.MsgUpdateGroupMembers{}).
|
||||
WithInterfaceHint("cosmos.base.v1beta1.Msg", &groupapi.MsgCreateGroup{}).
|
||||
WithInterfaceHint("cosmos.base.v1beta1.Msg", &groupapi.MsgUpdateGroupMembers{}),
|
||||
),
|
||||
genType(&grouptypes.MsgVote{}, &groupapi.MsgVote{}, genOpts),
|
||||
genType(&grouptypes.MsgExec{}, &groupapi.MsgExec{}, genOpts),
|
||||
genType(&grouptypes.MsgLeaveGroup{}, &groupapi.MsgLeaveGroup{}, genOpts),
|
||||
|
||||
// mint
|
||||
genType(&minttypes.Params{}, &mintapi.Params{}, genOpts),
|
||||
genType(&minttypes.MsgUpdateParams{}, &mintapi.MsgUpdateParams{}, genOpts.WithDisallowNil()),
|
||||
|
||||
// params
|
||||
genType(&proposal.ParameterChangeProposal{}, ¶msapi.ParameterChangeProposal{}, genOpts),
|
||||
|
||||
// slashing
|
||||
genType(&slashingtypes.Params{}, &slashingapi.Params{}, genOpts.WithDisallowNil()),
|
||||
genType(&slashingtypes.MsgUnjail{}, &slashingapi.MsgUnjail{}, genOpts),
|
||||
genType(&slashingtypes.MsgUpdateParams{}, &slashingapi.MsgUpdateParams{}, genOpts.WithDisallowNil()),
|
||||
|
||||
// staking
|
||||
genType(&stakingtypes.MsgCreateValidator{}, &stakingapi.MsgCreateValidator{},
|
||||
genOpts.WithDisallowNil().
|
||||
WithAnyTypes(&ed25519.PubKey{}).
|
||||
WithInterfaceHint("cosmos.crypto.PubKey", &ed25519.PubKey{}),
|
||||
),
|
||||
genType(&stakingtypes.MsgEditValidator{}, &stakingapi.MsgEditValidator{}, genOpts.WithDisallowNil()),
|
||||
genType(&stakingtypes.MsgDelegate{}, &stakingapi.MsgDelegate{}, genOpts.WithDisallowNil()),
|
||||
genType(&stakingtypes.MsgUndelegate{}, &stakingapi.MsgUndelegate{}, genOpts.WithDisallowNil()),
|
||||
genType(&stakingtypes.MsgBeginRedelegate{}, &stakingapi.MsgBeginRedelegate{}, genOpts.WithDisallowNil()),
|
||||
genType(&stakingtypes.MsgUpdateParams{}, &stakingapi.MsgUpdateParams{}, genOpts.WithDisallowNil()),
|
||||
genType(&stakingtypes.StakeAuthorization{}, &stakingapi.StakeAuthorization{}, genOpts),
|
||||
|
||||
// upgrade
|
||||
genType(&upgradetypes.Plan{}, &upgradeapi.Plan{}, genOpts.WithDisallowNil()),
|
||||
genType(&upgradetypes.SoftwareUpgradeProposal{}, &upgradeapi.SoftwareUpgradeProposal{}, genOpts.WithDisallowNil()),
|
||||
genType(&upgradetypes.CancelSoftwareUpgradeProposal{}, &upgradeapi.CancelSoftwareUpgradeProposal{}, genOpts),
|
||||
genType(&upgradetypes.MsgSoftwareUpgrade{}, &upgradeapi.MsgSoftwareUpgrade{}, genOpts.WithDisallowNil()),
|
||||
genType(&upgradetypes.MsgCancelUpgrade{}, &upgradeapi.MsgCancelUpgrade{}, genOpts),
|
||||
|
||||
// vesting
|
||||
genType(&vestingtypes.BaseVestingAccount{}, &vestingapi.BaseVestingAccount{}, genOpts.WithDisallowNil()),
|
||||
genType(&vestingtypes.ContinuousVestingAccount{}, &vestingapi.ContinuousVestingAccount{}, genOpts.WithDisallowNil()),
|
||||
genType(&vestingtypes.DelayedVestingAccount{}, &vestingapi.DelayedVestingAccount{}, genOpts.WithDisallowNil()),
|
||||
genType(&vestingtypes.PeriodicVestingAccount{}, &vestingapi.PeriodicVestingAccount{}, genOpts.WithDisallowNil()),
|
||||
genType(&vestingtypes.PermanentLockedAccount{}, &vestingapi.PermanentLockedAccount{}, genOpts.WithDisallowNil()),
|
||||
genType(&vestingtypes.MsgCreateVestingAccount{}, &vestingapi.MsgCreateVestingAccount{}, genOpts),
|
||||
genType(&vestingtypes.MsgCreatePermanentLockedAccount{}, &vestingapi.MsgCreatePermanentLockedAccount{}, genOpts),
|
||||
genType(&vestingtypes.MsgCreatePeriodicVestingAccount{}, &vestingapi.MsgCreatePeriodicVestingAccount{}, genOpts),
|
||||
}
|
||||
)
|
||||
|
||||
// TestAminoJSON_Equivalence tests that x/tx/Encoder encoding is equivalent to the legacy Encoder encoding.
|
||||
// A custom generator is used to generate random messages that are then encoded using both encoders. The custom
|
||||
// generator only supports proto.Message (which implement the protoreflect API) so in order to test legacy gogo types
|
||||
// we end up with a workflow as follows:
|
||||
//
|
||||
// 1. Generate a random protobuf proto.Message using the custom generator
|
||||
// 2. Marshal the proto.Message to protobuf binary bytes
|
||||
// 3. Unmarshal the protobuf bytes to a gogoproto.Message
|
||||
// 4. Marshal the gogoproto.Message to amino JSON bytes
|
||||
// 5. Marshal the proto.Message to amino JSON bytes
|
||||
// 6. Compare the amino JSON bytes from steps 4 and 5
|
||||
//
|
||||
// In order for step 3 to work certain restrictions on the data generated in step 1 must be enforced and are described
|
||||
// by the mutation of genOpts passed to the generator.
|
||||
func TestAminoJSON_Equivalence(t *testing.T) {
|
||||
encCfg := testutil.MakeTestEncodingConfig(
|
||||
auth.AppModuleBasic{}, authzmodule.AppModuleBasic{}, bank.AppModuleBasic{}, consensus.AppModuleBasic{},
|
||||
distribution.AppModuleBasic{}, evidence.AppModuleBasic{}, feegrantmodule.AppModuleBasic{},
|
||||
gov.AppModuleBasic{}, groupmodule.AppModuleBasic{}, mint.AppModuleBasic{}, params.AppModuleBasic{},
|
||||
slashing.AppModuleBasic{}, staking.AppModuleBasic{}, upgrade.AppModuleBasic{}, vesting.AppModuleBasic{})
|
||||
aj := aminojson.NewAminoJSON()
|
||||
|
||||
for _, tt := range genTypes {
|
||||
name := string(tt.pulsar.ProtoReflect().Descriptor().FullName())
|
||||
t.Run(name, func(t *testing.T) {
|
||||
gen := rapidproto.MessageGenerator(tt.pulsar, tt.opts)
|
||||
fmt.Printf("testing %s\n", tt.pulsar.ProtoReflect().Descriptor().FullName())
|
||||
rapid.Check(t, func(t *rapid.T) {
|
||||
// uncomment to debug; catch a panic and inspect application state
|
||||
//defer func() {
|
||||
// if r := recover(); r != nil {
|
||||
// //fmt.Printf("Panic: %+v\n", r)
|
||||
// t.FailNow()
|
||||
// }
|
||||
//}()
|
||||
|
||||
msg := gen.Draw(t, "msg")
|
||||
postFixPulsarMessage(msg)
|
||||
|
||||
gogo := tt.gogo
|
||||
sanity := tt.pulsar
|
||||
|
||||
protoBz, err := proto.Marshal(msg)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = proto.Unmarshal(protoBz, sanity)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = encCfg.Codec.Unmarshal(protoBz, gogo)
|
||||
require.NoError(t, err)
|
||||
|
||||
legacyAminoJson, err := encCfg.Amino.MarshalJSON(gogo)
|
||||
require.NoError(t, err)
|
||||
aminoJson, err := aj.Marshal(msg)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(legacyAminoJson), string(aminoJson))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newAny(t *testing.T, msg proto.Message) *anypb.Any {
|
||||
bz, err := proto.Marshal(msg)
|
||||
require.NoError(t, err)
|
||||
typeName := fmt.Sprintf("/%s", msg.ProtoReflect().Descriptor().FullName())
|
||||
return &anypb.Any{
|
||||
TypeUrl: typeName,
|
||||
Value: bz,
|
||||
}
|
||||
}
|
||||
|
||||
// TestAminoJSON_LegacyParity tests that the Encoder encoder produces the same output as the Encoder encoder.
|
||||
func TestAminoJSON_LegacyParity(t *testing.T) {
|
||||
encCfg := testutil.MakeTestEncodingConfig(auth.AppModuleBasic{}, authzmodule.AppModuleBasic{},
|
||||
bank.AppModuleBasic{}, distribution.AppModuleBasic{}, slashing.AppModuleBasic{}, staking.AppModuleBasic{},
|
||||
vesting.AppModuleBasic{})
|
||||
|
||||
aj := aminojson.NewAminoJSON()
|
||||
addr1 := types.AccAddress("addr1")
|
||||
now := time.Now()
|
||||
|
||||
genericAuth, _ := codectypes.NewAnyWithValue(&authztypes.GenericAuthorization{Msg: "foo"})
|
||||
genericAuthPulsar := newAny(t, &authzapi.GenericAuthorization{Msg: "foo"})
|
||||
pubkeyAny, _ := codectypes.NewAnyWithValue(&secp256k1types.PubKey{Key: []byte("foo")})
|
||||
pubkeyAnyPulsar := newAny(t, &secp256k1.PubKey{Key: []byte("foo")})
|
||||
dec10bz, _ := types.NewDec(10).Marshal()
|
||||
int123bz, _ := types.NewInt(123).Marshal()
|
||||
|
||||
cases := map[string]struct {
|
||||
gogo gogoproto.Message
|
||||
pulsar proto.Message
|
||||
pulsarMarshalFails bool
|
||||
|
||||
// this will fail in cases where a lossy encoding of an empty array to protobuf occurs. the unmarshalled bytes
|
||||
// represent the array as nil, and a subsequent marshal to JSON represent the array as null instead of empty.
|
||||
roundTripUnequal bool
|
||||
|
||||
// pulsar does not support marshalling a math.Dec as anything except a string. Therefore, we cannot unmarshal
|
||||
// a pulsar encoded Math.dec (the string representation of a Decimal) into a gogo Math.dec (expecting an int64).
|
||||
protoUnmarshalFails bool
|
||||
}{
|
||||
"auth/params": {gogo: &authtypes.Params{TxSigLimit: 10}, pulsar: &authapi.Params{TxSigLimit: 10}},
|
||||
"auth/module_account": {
|
||||
gogo: &authtypes.ModuleAccount{
|
||||
BaseAccount: authtypes.NewBaseAccountWithAddress(addr1), Permissions: []string{}},
|
||||
pulsar: &authapi.ModuleAccount{
|
||||
BaseAccount: &authapi.BaseAccount{Address: addr1.String()}, Permissions: []string{}},
|
||||
roundTripUnequal: true,
|
||||
},
|
||||
"auth/base_account": {
|
||||
gogo: &authtypes.BaseAccount{Address: addr1.String(), PubKey: pubkeyAny},
|
||||
pulsar: &authapi.BaseAccount{Address: addr1.String(), PubKey: pubkeyAnyPulsar},
|
||||
},
|
||||
"authz/msg_grant": {
|
||||
gogo: &authztypes.MsgGrant{
|
||||
Grant: authztypes.Grant{Expiration: &now, Authorization: genericAuth}},
|
||||
pulsar: &authzapi.MsgGrant{
|
||||
Grant: &authzapi.Grant{Expiration: timestamppb.New(now), Authorization: genericAuthPulsar}},
|
||||
},
|
||||
"authz/msg_update_params": {
|
||||
gogo: &authtypes.MsgUpdateParams{Params: authtypes.Params{TxSigLimit: 10}},
|
||||
pulsar: &authapi.MsgUpdateParams{Params: &authapi.Params{TxSigLimit: 10}},
|
||||
},
|
||||
"authz/msg_exec/empty_msgs": {
|
||||
gogo: &authztypes.MsgExec{Msgs: []*codectypes.Any{}},
|
||||
pulsar: &authzapi.MsgExec{Msgs: []*anypb.Any{}},
|
||||
},
|
||||
"distribution/delegator_starting_info": {
|
||||
gogo: &disttypes.DelegatorStartingInfo{},
|
||||
pulsar: &distapi.DelegatorStartingInfo{},
|
||||
},
|
||||
"distribution/delegator_starting_info/non_zero_dec": {
|
||||
gogo: &disttypes.DelegatorStartingInfo{Stake: types.NewDec(10)},
|
||||
pulsar: &distapi.DelegatorStartingInfo{Stake: "10.000000000000000000"},
|
||||
protoUnmarshalFails: true,
|
||||
},
|
||||
"distribution/delegation_delegator_reward": {
|
||||
gogo: &disttypes.DelegationDelegatorReward{},
|
||||
pulsar: &distapi.DelegationDelegatorReward{},
|
||||
},
|
||||
"distribution/community_pool_spend_proposal_with_deposit": {
|
||||
gogo: &disttypes.CommunityPoolSpendProposalWithDeposit{},
|
||||
pulsar: &distapi.CommunityPoolSpendProposalWithDeposit{},
|
||||
},
|
||||
"distribution/msg_withdraw_delegator_reward": {
|
||||
gogo: &disttypes.MsgWithdrawDelegatorReward{DelegatorAddress: "foo"},
|
||||
pulsar: &distapi.MsgWithdrawDelegatorReward{DelegatorAddress: "foo"},
|
||||
},
|
||||
"crypto/ed25519": {
|
||||
gogo: &ed25519types.PubKey{Key: []byte("key")},
|
||||
pulsar: &ed25519.PubKey{Key: []byte("key")},
|
||||
},
|
||||
"crypto/secp256k1": {
|
||||
gogo: &secp256k1types.PubKey{Key: []byte("key")},
|
||||
pulsar: &secp256k1.PubKey{Key: []byte("key")},
|
||||
},
|
||||
"crypto/legacy_amino_pubkey": {
|
||||
gogo: &multisig.LegacyAminoPubKey{PubKeys: []*codectypes.Any{pubkeyAny}},
|
||||
pulsar: &multisigapi.LegacyAminoPubKey{PublicKeys: []*anypb.Any{pubkeyAnyPulsar}},
|
||||
},
|
||||
"crypto/legacy_amino_pubkey/empty": {
|
||||
gogo: &multisig.LegacyAminoPubKey{},
|
||||
pulsar: &multisigapi.LegacyAminoPubKey{},
|
||||
},
|
||||
"consensus/evidence_params/duration": {
|
||||
gogo: &gov_v1beta1_types.VotingParams{VotingPeriod: 1e9 + 7},
|
||||
pulsar: &gov_v1beta1_api.VotingParams{VotingPeriod: &durationpb.Duration{Seconds: 1, Nanos: 7}},
|
||||
},
|
||||
"consensus/evidence_params/big_duration": {
|
||||
gogo: &gov_v1beta1_types.VotingParams{VotingPeriod: time.Duration(rapidproto.MaxDurationSeconds*1e9) + 999999999},
|
||||
pulsar: &gov_v1beta1_api.VotingParams{VotingPeriod: &durationpb.Duration{
|
||||
Seconds: rapidproto.MaxDurationSeconds, Nanos: 999999999}},
|
||||
},
|
||||
"consensus/evidence_params/too_big_duration": {
|
||||
gogo: &gov_v1beta1_types.VotingParams{VotingPeriod: time.Duration(rapidproto.MaxDurationSeconds*1e9) + 999999999},
|
||||
pulsar: &gov_v1beta1_api.VotingParams{VotingPeriod: &durationpb.Duration{
|
||||
Seconds: rapidproto.MaxDurationSeconds + 1, Nanos: 999999999}},
|
||||
pulsarMarshalFails: true,
|
||||
},
|
||||
// amino.dont_omitempty + empty/nil lists produce some surprising results
|
||||
"bank/send_authorization/empty_coins": {
|
||||
gogo: &banktypes.SendAuthorization{SpendLimit: []types.Coin{}},
|
||||
pulsar: &bankapi.SendAuthorization{SpendLimit: []*v1beta1.Coin{}},
|
||||
},
|
||||
"bank/send_authorization/nil_coins": {
|
||||
gogo: &banktypes.SendAuthorization{SpendLimit: nil},
|
||||
pulsar: &bankapi.SendAuthorization{SpendLimit: nil},
|
||||
},
|
||||
"bank/send_authorization/empty_list": {
|
||||
gogo: &banktypes.SendAuthorization{AllowList: []string{}},
|
||||
pulsar: &bankapi.SendAuthorization{AllowList: []string{}},
|
||||
},
|
||||
"bank/send_authorization/nil_list": {
|
||||
gogo: &banktypes.SendAuthorization{AllowList: nil},
|
||||
pulsar: &bankapi.SendAuthorization{AllowList: nil},
|
||||
},
|
||||
"bank/msg_multi_send/nil_everything": {
|
||||
gogo: &banktypes.MsgMultiSend{},
|
||||
pulsar: &bankapi.MsgMultiSend{},
|
||||
},
|
||||
"slashing/params/empty_dec": {
|
||||
gogo: &slashingtypes.Params{DowntimeJailDuration: 1e9 + 7},
|
||||
pulsar: &slashingapi.Params{DowntimeJailDuration: &durationpb.Duration{Seconds: 1, Nanos: 7}},
|
||||
},
|
||||
// This test cases demonstrates the expected contract and proper way to set a cosmos.Dec field represented
|
||||
// as bytes in protobuf message, namely:
|
||||
// dec10bz, _ := types.NewDec(10).Marshal()
|
||||
"slashing/params/dec": {
|
||||
gogo: &slashingtypes.Params{
|
||||
DowntimeJailDuration: 1e9 + 7,
|
||||
MinSignedPerWindow: types.NewDec(10)},
|
||||
pulsar: &slashingapi.Params{
|
||||
DowntimeJailDuration: &durationpb.Duration{Seconds: 1, Nanos: 7},
|
||||
MinSignedPerWindow: dec10bz,
|
||||
},
|
||||
},
|
||||
"staking/create_validator": {
|
||||
gogo: &stakingtypes.MsgCreateValidator{Pubkey: pubkeyAny},
|
||||
pulsar: &stakingapi.MsgCreateValidator{
|
||||
Pubkey: pubkeyAnyPulsar,
|
||||
Description: &stakingapi.Description{},
|
||||
Commission: &stakingapi.CommissionRates{},
|
||||
Value: &v1beta1.Coin{},
|
||||
},
|
||||
},
|
||||
"staking/msg_cancel_unbonding_delegation_response": {
|
||||
gogo: &stakingtypes.MsgCancelUnbondingDelegationResponse{},
|
||||
pulsar: &stakingapi.MsgCancelUnbondingDelegationResponse{},
|
||||
},
|
||||
"staking/stake_authorization_empty": {
|
||||
gogo: &stakingtypes.StakeAuthorization{},
|
||||
pulsar: &stakingapi.StakeAuthorization{},
|
||||
},
|
||||
"staking/stake_authorization_allow": {
|
||||
gogo: &stakingtypes.StakeAuthorization{
|
||||
Validators: &stakingtypes.StakeAuthorization_AllowList{
|
||||
AllowList: &stakingtypes.StakeAuthorization_Validators{Address: []string{"foo"}},
|
||||
}},
|
||||
pulsar: &stakingapi.StakeAuthorization{
|
||||
Validators: &stakingapi.StakeAuthorization_AllowList{
|
||||
AllowList: &stakingapi.StakeAuthorization_Validators{Address: []string{"foo"}},
|
||||
}},
|
||||
},
|
||||
"vesting/base_account_empty": {
|
||||
gogo: &vestingtypes.BaseVestingAccount{BaseAccount: &authtypes.BaseAccount{}},
|
||||
pulsar: &vestingapi.BaseVestingAccount{BaseAccount: &authapi.BaseAccount{}},
|
||||
},
|
||||
"vesting/base_account_pubkey": {
|
||||
gogo: &vestingtypes.BaseVestingAccount{BaseAccount: &authtypes.BaseAccount{PubKey: pubkeyAny}},
|
||||
pulsar: &vestingapi.BaseVestingAccount{BaseAccount: &authapi.BaseAccount{PubKey: pubkeyAnyPulsar}},
|
||||
},
|
||||
"math/int_as_string": {
|
||||
gogo: &gogo_testpb.IntAsString{IntAsString: types.NewInt(123)},
|
||||
pulsar: &pulsar_testpb.IntAsString{IntAsString: "123"},
|
||||
},
|
||||
"math/int_as_string/empty": {
|
||||
gogo: &gogo_testpb.IntAsString{},
|
||||
pulsar: &pulsar_testpb.IntAsString{},
|
||||
},
|
||||
"math/int_as_bytes": {
|
||||
gogo: &gogo_testpb.IntAsBytes{IntAsBytes: types.NewInt(123)},
|
||||
pulsar: &pulsar_testpb.IntAsBytes{IntAsBytes: int123bz},
|
||||
},
|
||||
"math/int_as_bytes/empty": {
|
||||
gogo: &gogo_testpb.IntAsBytes{},
|
||||
pulsar: &pulsar_testpb.IntAsBytes{},
|
||||
},
|
||||
}
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
gogoBytes, err := encCfg.Amino.MarshalJSON(tc.gogo)
|
||||
require.NoError(t, err)
|
||||
|
||||
pulsarBytes, err := aj.Marshal(tc.pulsar)
|
||||
if tc.pulsarMarshalFails {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
fmt.Printf("pulsar: %s\n", string(pulsarBytes))
|
||||
fmt.Printf(" gogo: %s\n", string(gogoBytes))
|
||||
require.Equal(t, string(gogoBytes), string(pulsarBytes))
|
||||
|
||||
pulsarProtoBytes, err := proto.Marshal(tc.pulsar)
|
||||
require.NoError(t, err)
|
||||
|
||||
gogoType := reflect.TypeOf(tc.gogo).Elem()
|
||||
newGogo := reflect.New(gogoType).Interface().(gogoproto.Message)
|
||||
|
||||
err = encCfg.Codec.Unmarshal(pulsarProtoBytes, newGogo)
|
||||
if tc.protoUnmarshalFails {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
newGogoBytes, err := encCfg.Amino.MarshalJSON(newGogo)
|
||||
if tc.roundTripUnequal {
|
||||
require.NotEqual(t, string(gogoBytes), string(newGogoBytes))
|
||||
return
|
||||
}
|
||||
require.Equal(t, string(gogoBytes), string(newGogoBytes))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendAuthorization(t *testing.T) {
|
||||
encCfg := testutil.MakeTestEncodingConfig(auth.AppModuleBasic{}, authzmodule.AppModuleBasic{},
|
||||
distribution.AppModuleBasic{}, bank.AppModuleBasic{})
|
||||
|
||||
aj := aminojson.NewAminoJSON()
|
||||
|
||||
// beware, Coins has as custom MarshalJSON method which changes how nil is handled
|
||||
// nil -> [] (empty list)
|
||||
// [] -> [] (empty list)
|
||||
// https://github.com/cosmos/cosmos-sdk/blob/be9bd7a8c1b41b115d58f4e76ee358e18a52c0af/types/coin.go#L199
|
||||
|
||||
// explicitly show the default for clarity
|
||||
pulsar := &bankapi.SendAuthorization{SpendLimit: []*v1beta1.Coin{}}
|
||||
sanityPulsar := &bankapi.SendAuthorization{}
|
||||
gogo := &banktypes.SendAuthorization{SpendLimit: types.Coins{}}
|
||||
|
||||
protoBz, err := proto.Marshal(pulsar)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = encCfg.Codec.Unmarshal(protoBz, gogo)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = proto.Unmarshal(protoBz, sanityPulsar)
|
||||
|
||||
// !!!
|
||||
// empty []string is not the same as nil []string. this is a bug in gogo.
|
||||
// `[]string` -> proto.Marshal -> legacyAmino.UnmarshalProto (unmarshals empty slice as nil)
|
||||
// -> legacyAmino.MarshalJson -> `null`
|
||||
// `[]string` -> [proto.Marshal -> pulsar.Unmarshal] -> amino.MarshalJson -> `[]`
|
||||
require.Nil(t, gogo.SpendLimit)
|
||||
require.Nil(t, sanityPulsar.SpendLimit)
|
||||
require.NotNil(t, pulsar.SpendLimit)
|
||||
require.Zero(t, len(pulsar.SpendLimit))
|
||||
|
||||
legacyAminoJson, err := encCfg.Amino.MarshalJSON(gogo)
|
||||
aminoJson, err := aj.Marshal(sanityPulsar)
|
||||
|
||||
require.Equal(t, string(legacyAminoJson), string(aminoJson))
|
||||
|
||||
aminoJson, err = aj.Marshal(pulsar)
|
||||
require.NoError(t, err)
|
||||
|
||||
// at this point, pulsar.SpendLimit = [], and gogo.SpendLimit = nil, but they will both marshal to `[]`
|
||||
// this is *only* possible because of Cosmos SDK's custom MarshalJSON method for Coins
|
||||
require.Equal(t, string(legacyAminoJson), string(aminoJson))
|
||||
}
|
||||
|
||||
func postFixPulsarMessage(msg proto.Message) {
|
||||
switch m := msg.(type) {
|
||||
case *authapi.ModuleAccount:
|
||||
if m.BaseAccount == nil {
|
||||
m.BaseAccount = &authapi.BaseAccount{}
|
||||
}
|
||||
_, _, bz := testdata.KeyTestPubAddr()
|
||||
// always set address to a valid bech32 address
|
||||
text, _ := bech32.ConvertAndEncode("cosmos", bz)
|
||||
m.BaseAccount.Address = text
|
||||
|
||||
// see negative test
|
||||
if len(m.Permissions) == 0 {
|
||||
m.Permissions = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
4
tests/integration/aminojson/internal/Makefile
Normal file
4
tests/integration/aminojson/internal/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
codegen:
|
||||
@echo "Generating proto files"
|
||||
buf --template buf.gen.gogo.yaml generate
|
||||
buf --template buf.gen.pulsar.yaml generate
|
||||
8
tests/integration/aminojson/internal/buf.gen.gogo.yaml
Normal file
8
tests/integration/aminojson/internal/buf.gen.gogo.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
version: v1
|
||||
plugins:
|
||||
- name: gocosmos
|
||||
out: ./gogo
|
||||
opt: plugins=grpc,Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types
|
||||
- name: grpc-gateway
|
||||
out: ./gogo
|
||||
opt: logtostderr=true,allow_colon_final_segments=true
|
||||
15
tests/integration/aminojson/internal/buf.gen.pulsar.yaml
Normal file
15
tests/integration/aminojson/internal/buf.gen.pulsar.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
version: v1
|
||||
managed:
|
||||
enabled: true
|
||||
go_package_prefix:
|
||||
default: github.com/cosmos/cosmos-sdk/tests/integration/aminojson/internal/testpb/pulsar
|
||||
except:
|
||||
- buf.build/googleapis/googleapis
|
||||
- buf.build/cosmos/gogo-proto
|
||||
- buf.build/cosmos/cosmos-proto
|
||||
override:
|
||||
buf.build/cosmos/cosmos-sdk: cosmossdk.io/api
|
||||
plugins:
|
||||
- name: go-pulsar
|
||||
out: ./pulsar
|
||||
opt: paths=source_relative
|
||||
11
tests/integration/aminojson/internal/buf.lock
Normal file
11
tests/integration/aminojson/internal/buf.lock
Normal file
@ -0,0 +1,11 @@
|
||||
# Generated by buf. DO NOT EDIT.
|
||||
version: v1
|
||||
deps:
|
||||
- remote: buf.build
|
||||
owner: cosmos
|
||||
repository: cosmos-proto
|
||||
commit: 1935555c206d4afb9e94615dfd0fad31
|
||||
- remote: buf.build
|
||||
owner: cosmos
|
||||
repository: gogo-proto
|
||||
commit: 34d970b699f84aa382f3c29773a60836
|
||||
12
tests/integration/aminojson/internal/buf.yaml
Normal file
12
tests/integration/aminojson/internal/buf.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
version: v1
|
||||
deps:
|
||||
- buf.build/cosmos/cosmos-proto
|
||||
- buf.build/cosmos/gogo-proto
|
||||
lint:
|
||||
use:
|
||||
- DEFAULT
|
||||
except:
|
||||
- PACKAGE_VERSION_SUFFIX
|
||||
breaking:
|
||||
ignore:
|
||||
- testpb
|
||||
1600
tests/integration/aminojson/internal/gogo/testpb/test.pb.go
Normal file
1600
tests/integration/aminojson/internal/gogo/testpb/test.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
3863
tests/integration/aminojson/internal/pulsar/testpb/test.pulsar.go
Normal file
3863
tests/integration/aminojson/internal/pulsar/testpb/test.pulsar.go
Normal file
File diff suppressed because it is too large
Load Diff
58
tests/integration/aminojson/internal/testpb/test.proto
Normal file
58
tests/integration/aminojson/internal/testpb/test.proto
Normal file
@ -0,0 +1,58 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package testpb;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "amino/amino.proto";
|
||||
import "cosmos_proto/cosmos.proto";
|
||||
|
||||
message streng {
|
||||
string value = 1;
|
||||
}
|
||||
|
||||
message TestRepeatedFields {
|
||||
repeated streng nullable_omitempty = 1;
|
||||
|
||||
// not supported for empty sets
|
||||
// go-amino emits nothing but the protoreflect library emits a null
|
||||
repeated streng nullable_dont_omitempty = 2 [(amino.dont_omitempty) = true];
|
||||
|
||||
// not supported for empty sets
|
||||
// go-amino emits a null but the protoreflect library emits nothing
|
||||
repeated streng non_nullable_omitempty = 3 [(gogoproto.nullable) = false];
|
||||
|
||||
repeated streng non_nullable_dont_omitempty = 4 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
|
||||
}
|
||||
|
||||
message TestNullableFields {
|
||||
streng nullable_omitempty = 1;
|
||||
|
||||
// not supported
|
||||
// go-amino emits nothing and the protoreflect returns an error
|
||||
// alternatively protoreflect could emit `{}`
|
||||
streng nullable_dont_omitempty = 2 [(amino.dont_omitempty) = true];
|
||||
|
||||
// not supported
|
||||
// go-amino emits `{}` but the protoreflect library emits nothing
|
||||
streng non_nullable_omitempty = 3 [(gogoproto.nullable) = false];
|
||||
|
||||
streng non_nullable_dont_omitempty = 4 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
|
||||
}
|
||||
|
||||
message IntAsString {
|
||||
string int_as_string = 1 [
|
||||
(cosmos_proto.scalar) = "cosmos.Int",
|
||||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
|
||||
(amino.dont_omitempty) = true,
|
||||
(gogoproto.nullable) = false
|
||||
];
|
||||
}
|
||||
|
||||
message IntAsBytes {
|
||||
bytes int_as_bytes = 1 [
|
||||
(cosmos_proto.scalar) = "cosmos.Int",
|
||||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
|
||||
(amino.dont_omitempty) = true,
|
||||
(gogoproto.nullable) = false
|
||||
];
|
||||
}
|
||||
81
tests/integration/aminojson/repeated_test.go
Normal file
81
tests/integration/aminojson/repeated_test.go
Normal file
@ -0,0 +1,81 @@
|
||||
package aminojson
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
gogoproto "github.com/cosmos/gogoproto/proto"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/x/tx/aminojson"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
gogopb "github.com/cosmos/cosmos-sdk/tests/integration/aminojson/internal/gogo/testpb"
|
||||
pulsarpb "github.com/cosmos/cosmos-sdk/tests/integration/aminojson/internal/pulsar/testpb"
|
||||
)
|
||||
|
||||
func TestRepeatedFields(t *testing.T) {
|
||||
cdc := codec.NewLegacyAmino()
|
||||
aj := aminojson.NewAminoJSON()
|
||||
|
||||
cases := map[string]struct {
|
||||
gogo gogoproto.Message
|
||||
pulsar proto.Message
|
||||
unequal bool
|
||||
errs bool
|
||||
}{
|
||||
"unsupported_empty_sets": {
|
||||
gogo: &gogopb.TestRepeatedFields{},
|
||||
pulsar: &pulsarpb.TestRepeatedFields{},
|
||||
unequal: true,
|
||||
},
|
||||
"unsupported_empty_sets_are_set": {
|
||||
gogo: &gogopb.TestRepeatedFields{
|
||||
NullableDontOmitempty: []*gogopb.Streng{{Value: "foo"}},
|
||||
NonNullableOmitempty: []gogopb.Streng{{Value: "foo"}},
|
||||
},
|
||||
pulsar: &pulsarpb.TestRepeatedFields{
|
||||
NullableDontOmitempty: []*pulsarpb.Streng{{Value: "foo"}},
|
||||
NonNullableOmitempty: []*pulsarpb.Streng{{Value: "foo"}},
|
||||
},
|
||||
},
|
||||
"unsupported_nullable": {
|
||||
gogo: &gogopb.TestNullableFields{},
|
||||
pulsar: &pulsarpb.TestNullableFields{},
|
||||
errs: true,
|
||||
},
|
||||
"unsupported_nullable_set": {
|
||||
gogo: &gogopb.TestNullableFields{
|
||||
NullableDontOmitempty: &gogopb.Streng{Value: "foo"},
|
||||
NonNullableDontOmitempty: gogopb.Streng{Value: "foo"},
|
||||
},
|
||||
pulsar: &pulsarpb.TestNullableFields{
|
||||
NullableDontOmitempty: &pulsarpb.Streng{Value: "foo"},
|
||||
NonNullableDontOmitempty: &pulsarpb.Streng{Value: "foo"},
|
||||
},
|
||||
unequal: true,
|
||||
},
|
||||
}
|
||||
|
||||
for n, tc := range cases {
|
||||
t.Run(n, func(t *testing.T) {
|
||||
gogoBz, err := cdc.MarshalJSON(tc.gogo)
|
||||
require.NoError(t, err)
|
||||
pulsarBz, err := aj.Marshal(tc.pulsar)
|
||||
if tc.errs {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
fmt.Printf(" gogo: %s\npulsar: %s\n", string(gogoBz), string(pulsarBz))
|
||||
|
||||
if tc.unequal {
|
||||
require.NotEqual(t, string(gogoBz), string(pulsarBz))
|
||||
} else {
|
||||
require.Equal(t, string(gogoBz), string(pulsarBz))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1,221 +0,0 @@
|
||||
package rapidproto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
"gotest.tools/v3/assert"
|
||||
"pgregory.net/rapid"
|
||||
)
|
||||
|
||||
func MessageGenerator[T proto.Message](x T, options GeneratorOptions) *rapid.Generator[T] {
|
||||
msgType := x.ProtoReflect().Type()
|
||||
return rapid.Custom(func(t *rapid.T) T {
|
||||
msg := msgType.New()
|
||||
|
||||
options.setFields(t, msg, 0)
|
||||
|
||||
return msg.Interface().(T)
|
||||
})
|
||||
}
|
||||
|
||||
type GeneratorOptions struct {
|
||||
AnyTypeURLs []string
|
||||
Resolver protoregistry.MessageTypeResolver
|
||||
}
|
||||
|
||||
const depthLimit = 10
|
||||
|
||||
func (opts GeneratorOptions) setFields(t *rapid.T, msg protoreflect.Message, depth int) bool {
|
||||
// to avoid stack overflow we limit the depth of nested messages
|
||||
if depth > depthLimit {
|
||||
return false
|
||||
}
|
||||
|
||||
descriptor := msg.Descriptor()
|
||||
fullName := descriptor.FullName()
|
||||
switch fullName {
|
||||
case timestampFullName:
|
||||
opts.genTimestamp(t, msg)
|
||||
return true
|
||||
case durationFullName:
|
||||
opts.genDuration(t, msg)
|
||||
return true
|
||||
case anyFullName:
|
||||
return opts.genAny(t, msg, depth)
|
||||
case fieldMaskFullName:
|
||||
opts.genFieldMask(t, msg)
|
||||
return true
|
||||
default:
|
||||
fields := descriptor.Fields()
|
||||
n := fields.Len()
|
||||
for i := 0; i < n; i++ {
|
||||
field := fields.Get(i)
|
||||
if !rapid.Bool().Draw(t, fmt.Sprintf("gen-%s", field.Name())) {
|
||||
continue
|
||||
}
|
||||
|
||||
opts.setFieldValue(t, msg, field, depth)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
timestampFullName = "google.protobuf.Timestamp"
|
||||
durationFullName = "google.protobuf.Duration"
|
||||
anyFullName = "google.protobuf.Any"
|
||||
fieldMaskFullName = "google.protobuf.FieldMask"
|
||||
)
|
||||
|
||||
func (opts GeneratorOptions) setFieldValue(t *rapid.T, msg protoreflect.Message, field protoreflect.FieldDescriptor, depth int) {
|
||||
name := string(field.Name())
|
||||
kind := field.Kind()
|
||||
|
||||
switch {
|
||||
case field.IsList():
|
||||
list := msg.Mutable(field).List()
|
||||
n := rapid.IntRange(0, 10).Draw(t, fmt.Sprintf("%sN", name))
|
||||
for i := 0; i < n; i++ {
|
||||
if kind == protoreflect.MessageKind || kind == protoreflect.GroupKind {
|
||||
if !opts.setFields(t, list.AppendMutable().Message(), depth+1) {
|
||||
list.Truncate(i)
|
||||
}
|
||||
} else {
|
||||
list.Append(opts.genScalarFieldValue(t, field, fmt.Sprintf("%s%d", name, i)))
|
||||
}
|
||||
}
|
||||
case field.IsMap():
|
||||
m := msg.Mutable(field).Map()
|
||||
n := rapid.IntRange(0, 10).Draw(t, fmt.Sprintf("%sN", name))
|
||||
for i := 0; i < n; i++ {
|
||||
keyField := field.MapKey()
|
||||
valueField := field.MapValue()
|
||||
valueKind := valueField.Kind()
|
||||
key := opts.genScalarFieldValue(t, keyField, fmt.Sprintf("%s%d-key", name, i))
|
||||
if valueKind == protoreflect.MessageKind || valueKind == protoreflect.GroupKind {
|
||||
if !opts.setFields(t, m.Mutable(key.MapKey()).Message(), depth+1) {
|
||||
m.Clear(key.MapKey())
|
||||
}
|
||||
} else {
|
||||
value := opts.genScalarFieldValue(t, valueField, fmt.Sprintf("%s%d-key", name, i))
|
||||
m.Set(key.MapKey(), value)
|
||||
}
|
||||
}
|
||||
default:
|
||||
if kind == protoreflect.MessageKind || kind == protoreflect.GroupKind {
|
||||
if !opts.setFields(t, msg.Mutable(field).Message(), depth+1) {
|
||||
msg.Clear(field)
|
||||
}
|
||||
} else {
|
||||
msg.Set(field, opts.genScalarFieldValue(t, field, name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (opts GeneratorOptions) genScalarFieldValue(t *rapid.T, field protoreflect.FieldDescriptor, name string) protoreflect.Value {
|
||||
switch field.Kind() {
|
||||
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
|
||||
return protoreflect.ValueOfInt32(rapid.Int32().Draw(t, name))
|
||||
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
|
||||
return protoreflect.ValueOfUint32(rapid.Uint32().Draw(t, name))
|
||||
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
|
||||
return protoreflect.ValueOfInt64(rapid.Int64().Draw(t, name))
|
||||
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
|
||||
return protoreflect.ValueOfUint64(rapid.Uint64().Draw(t, name))
|
||||
case protoreflect.BoolKind:
|
||||
return protoreflect.ValueOfBool(rapid.Bool().Draw(t, name))
|
||||
case protoreflect.BytesKind:
|
||||
return protoreflect.ValueOfBytes(rapid.SliceOf(rapid.Byte()).Draw(t, name))
|
||||
case protoreflect.FloatKind:
|
||||
return protoreflect.ValueOfFloat32(rapid.Float32().Draw(t, name))
|
||||
case protoreflect.DoubleKind:
|
||||
return protoreflect.ValueOfFloat64(rapid.Float64().Draw(t, name))
|
||||
case protoreflect.EnumKind:
|
||||
enumValues := field.Enum().Values()
|
||||
val := rapid.Int32Range(0, int32(enumValues.Len()-1)).Draw(t, name)
|
||||
return protoreflect.ValueOfEnum(protoreflect.EnumNumber(val))
|
||||
case protoreflect.StringKind:
|
||||
return protoreflect.ValueOfString(rapid.String().Draw(t, name))
|
||||
default:
|
||||
t.Fatalf("unexpected %v", field)
|
||||
return protoreflect.Value{}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
secondsName = "seconds"
|
||||
nanosName = "nanos"
|
||||
)
|
||||
|
||||
func (opts GeneratorOptions) genTimestamp(t *rapid.T, msg protoreflect.Message) {
|
||||
seconds := rapid.Int64Range(-9999999999, 9999999999).Draw(t, "seconds")
|
||||
nanos := rapid.Int32Range(0, 999999999).Draw(t, "nanos")
|
||||
setSecondsNanosFields(t, msg, seconds, nanos)
|
||||
}
|
||||
|
||||
func (opts GeneratorOptions) genDuration(t *rapid.T, msg protoreflect.Message) {
|
||||
seconds := rapid.Int64Range(0, 315576000000).Draw(t, "seconds")
|
||||
nanos := rapid.Int32Range(0, 999999999).Draw(t, "nanos")
|
||||
setSecondsNanosFields(t, msg, seconds, nanos)
|
||||
}
|
||||
|
||||
func setSecondsNanosFields(t *rapid.T, message protoreflect.Message, seconds int64, nanos int32) {
|
||||
fields := message.Descriptor().Fields()
|
||||
|
||||
secondsField := fields.ByName(secondsName)
|
||||
assert.Assert(t, secondsField != nil)
|
||||
message.Set(secondsField, protoreflect.ValueOfInt64(seconds))
|
||||
|
||||
nanosField := fields.ByName(nanosName)
|
||||
assert.Assert(t, nanosField != nil)
|
||||
message.Set(nanosField, protoreflect.ValueOfInt32(nanos))
|
||||
}
|
||||
|
||||
const (
|
||||
typeURLName = "type_url"
|
||||
valueName = "value"
|
||||
)
|
||||
|
||||
func (opts GeneratorOptions) genAny(t *rapid.T, msg protoreflect.Message, depth int) bool {
|
||||
if len(opts.AnyTypeURLs) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
fields := msg.Descriptor().Fields()
|
||||
|
||||
typeURL := rapid.SampledFrom(opts.AnyTypeURLs).Draw(t, "type_url")
|
||||
typ, err := opts.Resolver.FindMessageByURL(typeURL)
|
||||
assert.NilError(t, err)
|
||||
|
||||
typeURLField := fields.ByName(typeURLName)
|
||||
assert.Assert(t, typeURLField != nil)
|
||||
msg.Set(typeURLField, protoreflect.ValueOfString(typeURL))
|
||||
|
||||
valueMsg := typ.New()
|
||||
opts.setFields(t, valueMsg, depth+1)
|
||||
valueBz, err := proto.Marshal(valueMsg.Interface())
|
||||
assert.NilError(t, err)
|
||||
|
||||
valueField := fields.ByName(valueName)
|
||||
assert.Assert(t, valueField != nil)
|
||||
msg.Set(valueField, protoreflect.ValueOfBytes(valueBz))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const (
|
||||
pathsName = "paths"
|
||||
)
|
||||
|
||||
func (opts GeneratorOptions) genFieldMask(t *rapid.T, msg protoreflect.Message) {
|
||||
paths := rapid.SliceOfN(rapid.StringMatching("[a-z]+([.][a-z]+){0,2}"), 1, 5).Draw(t, "paths")
|
||||
pathsField := msg.Descriptor().Fields().ByName(pathsName)
|
||||
assert.Assert(t, pathsField != nil)
|
||||
pathsList := msg.NewField(pathsField).List()
|
||||
for _, path := range paths {
|
||||
pathsList.Append(protoreflect.ValueOfString(path))
|
||||
}
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
package rapidproto_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/golden"
|
||||
"pgregory.net/rapid"
|
||||
|
||||
"github.com/cosmos/cosmos-proto/testpb"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/testutil/rapidproto"
|
||||
)
|
||||
|
||||
// TestRegression checks that the generator still produces the same output
|
||||
// for the same random seeds, assuming that this data has been hand expected
|
||||
// to generally look good.
|
||||
func TestRegression(t *testing.T) {
|
||||
gen := rapidproto.MessageGenerator(&testpb.A{}, rapidproto.GeneratorOptions{})
|
||||
for i := 0; i < 5; i++ {
|
||||
testRegressionSeed(t, i, gen)
|
||||
}
|
||||
}
|
||||
|
||||
func testRegressionSeed[X proto.Message](t *testing.T, seed int, generator *rapid.Generator[X]) {
|
||||
x := generator.Example(seed)
|
||||
bz, err := protojson.Marshal(x)
|
||||
assert.NilError(t, err)
|
||||
golden.Assert(t, string(bz), fmt.Sprintf("seed%d.json", seed))
|
||||
}
|
||||
1
testutil/rapidproto/testdata/seed0.json
vendored
1
testutil/rapidproto/testdata/seed0.json
vendored
@ -1 +0,0 @@
|
||||
{"enum":"Two", "someBoolean":true, "INT32":6, "SINT32":-53, "INT64":"-261", "SFIXED32":3, "FIXED32":65302, "FIXED64":"45044", "STRING":"~Âaႃ#", "MESSAGE":{"x":"ʰ="}, "MAP":{"":{"x":"௹"}, "%󠇯º$&.":{"x":"-"}, "=A":{}, "AA|𞀠":{"x":"a\u0000ๆ"}}, "LIST":[{}], "ONEOFSTRING":"", "imported":{}}
|
||||
1
testutil/rapidproto/testdata/seed1.json
vendored
1
testutil/rapidproto/testdata/seed1.json
vendored
@ -1 +0,0 @@
|
||||
{"UINT32":177, "INT64":"-139958413", "SFIXED32":41418, "FIXED32":25381940, "FLOAT":-8.336453e+31, "SFIXED64":"-2503553836720", "DOUBLE":-0.03171187036377887, "STRING":"?˄~ע", "MESSAGE":{"x":"dDž#"}, "MAP":{"Ⱥa<":{"x":"+["}, "֑Ⱥ|@!`":{}}, "ONEOFSTRING":"\u0012\t?A", "imported":{}, "type":"A<>=*ى~~Ⱥ*ᾈാȺAᶊ?"}
|
||||
1
testutil/rapidproto/testdata/seed2.json
vendored
1
testutil/rapidproto/testdata/seed2.json
vendored
@ -1 +0,0 @@
|
||||
{"INT32":-48, "UINT32":246, "INT64":"-21558176502", "SING64":"5030347", "UINT64":"28", "FIXED32":92, "DOUBLE":2.3547259926790202e-142, "STRING":"ಾ", "LIST":[{}, {}, {}, {}, {"x":" ᾚ DzA{˭҄\nA ^$?ᾦ,:<\"?_\u0014;|"}], "ONEOFSTRING":"𝟠", "LISTENUM":["Two", "One", "One"]}
|
||||
1
testutil/rapidproto/testdata/seed3.json
vendored
1
testutil/rapidproto/testdata/seed3.json
vendored
@ -1 +0,0 @@
|
||||
{"INT32":22525032, "SINT32":897, "INT64":"-301128487533312", "SFIXED64":"-71", "FIXED64":"14", "DOUBLE":-2.983041182946181, "STRING":"-A^'", "MESSAGE":{"x":"#ऻ;́\r⋁"}, "LIST":[{}, {}, {}, {}, {}], "ONEOFSTRING":"", "imported":{}, "type":"₩\u0000^৴~౽ NjAৈ⃠𝖜ೄ"}
|
||||
1
testutil/rapidproto/testdata/seed4.json
vendored
1
testutil/rapidproto/testdata/seed4.json
vendored
@ -1 +0,0 @@
|
||||
{"SINT32":1, "INT64":"-9223372036854775808", "SING64":"1", "FLOAT":-0.00013906474, "SFIXED64":"71414010", "STRING":"ף̂", "MESSAGE":{"x":" "}, "LIST":[{}], "ONEOFSTRING":"#¯∑Ⱥ<E28891>", "LISTENUM":["One", "One", "Two", "Two", "One", "One", "One", "Two"], "imported":{}, "type":"\u001b<ʰ+`𑱐@\u001b*Dž\u0000#₻\u0000"}
|
||||
1
x/tx/aminojson/.gitignore
vendored
Normal file
1
x/tx/aminojson/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
testdata/rapid
|
||||
37
x/tx/aminojson/any.go
Normal file
37
x/tx/aminojson/any.go
Normal file
@ -0,0 +1,37 @@
|
||||
package aminojson
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"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
|
||||
|
||||
typ, err := resolver.FindMessageByURL(anyMsg.TypeUrl)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "can't resolve type URL %s", anyMsg.TypeUrl)
|
||||
}
|
||||
|
||||
valueMsg := typ.New()
|
||||
err = proto.Unmarshal(anyMsg.Value, valueMsg.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, named := getMessageAminoName(valueMsg)
|
||||
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)
|
||||
}
|
||||
170
x/tx/aminojson/encoder.go
Normal file
170
x/tx/aminojson/encoder.go
Normal file
@ -0,0 +1,170 @@
|
||||
package aminojson
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
|
||||
authapi "cosmossdk.io/api/cosmos/auth/v1beta1"
|
||||
"cosmossdk.io/api/cosmos/crypto/multisig"
|
||||
"cosmossdk.io/math"
|
||||
)
|
||||
|
||||
// cosmosIntEncoder provides legacy compatible encoding for cosmos.Int types. In gogo messages these are sometimes
|
||||
// represented by a `cosmos-sdk/types.Int` through the usage of the option:
|
||||
//
|
||||
// (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int"
|
||||
//
|
||||
// In pulsar message they represented as strings, which is the only format this encoder supports.
|
||||
func cosmosIntEncoder(_ *Encoder, v protoreflect.Value, w io.Writer) error {
|
||||
switch val := v.Interface().(type) {
|
||||
case string:
|
||||
if val == "" {
|
||||
return jsonMarshal(w, "0")
|
||||
}
|
||||
return jsonMarshal(w, val)
|
||||
case []byte:
|
||||
if len(val) == 0 {
|
||||
return jsonMarshal(w, "0")
|
||||
}
|
||||
var i math.Int
|
||||
err := i.Unmarshal(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonMarshal(w, i.String())
|
||||
default:
|
||||
return fmt.Errorf("unsupported type %T", val)
|
||||
}
|
||||
}
|
||||
|
||||
// cosmosDecEncoder provides legacy compatible encoding for cosmos.Dec and cosmos.Int types. These are sometimes
|
||||
// represented as strings in pulsar messages and sometimes as bytes. This encoder handles both cases.
|
||||
func cosmosDecEncoder(_ *Encoder, v protoreflect.Value, w io.Writer) error {
|
||||
switch val := v.Interface().(type) {
|
||||
case string:
|
||||
if val == "" {
|
||||
return jsonMarshal(w, "0")
|
||||
}
|
||||
return jsonMarshal(w, val)
|
||||
case []byte:
|
||||
if len(val) == 0 {
|
||||
return jsonMarshal(w, "0")
|
||||
}
|
||||
var dec math.LegacyDec
|
||||
err := dec.Unmarshal(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return jsonMarshal(w, dec.String())
|
||||
default:
|
||||
return fmt.Errorf("unsupported type %T", val)
|
||||
}
|
||||
}
|
||||
|
||||
// nullSliceAsEmptyEncoder replicates the behavior at:
|
||||
// https://github.com/cosmos/cosmos-sdk/blob/be9bd7a8c1b41b115d58f4e76ee358e18a52c0af/types/coin.go#L199-L205
|
||||
func nullSliceAsEmptyEncoder(enc *Encoder, v protoreflect.Value, w io.Writer) error {
|
||||
switch list := v.Interface().(type) {
|
||||
case protoreflect.List:
|
||||
if list.Len() == 0 {
|
||||
_, err := w.Write([]byte("[]"))
|
||||
return err
|
||||
}
|
||||
return enc.marshalList(list, w)
|
||||
default:
|
||||
return fmt.Errorf("unsupported type %T", list)
|
||||
}
|
||||
}
|
||||
|
||||
// keyFieldEncoder replicates the behavior at described at:
|
||||
// https://github.com/cosmos/cosmos-sdk/blob/b49f948b36bc991db5be431607b475633aed697e/proto/cosmos/crypto/secp256k1/keys.proto#L16
|
||||
// The message is treated if it were bytes directly without the key field specified.
|
||||
func keyFieldEncoder(_ *Encoder, msg protoreflect.Message, w io.Writer) error {
|
||||
keyField := msg.Descriptor().Fields().ByName("key")
|
||||
if keyField == nil {
|
||||
return errors.New(`message encoder for key_field: no field named "key" found`)
|
||||
}
|
||||
|
||||
bz := msg.Get(keyField).Bytes()
|
||||
|
||||
if len(bz) == 0 {
|
||||
_, err := fmt.Fprint(w, "null")
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := fmt.Fprintf(w, `"%s"`, base64.StdEncoding.EncodeToString(bz))
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
type moduleAccountPretty struct {
|
||||
Address string `json:"address"`
|
||||
PubKey string `json:"public_key"`
|
||||
AccountNumber uint64 `json:"account_number"`
|
||||
Sequence uint64 `json:"sequence"`
|
||||
Name string `json:"name"`
|
||||
Permissions []string `json:"permissions"`
|
||||
}
|
||||
|
||||
// moduleAccountEncoder replicates the behavior in
|
||||
// https://github.com/cosmos/cosmos-sdk/blob/41a3dfeced2953beba3a7d11ec798d17ee19f506/x/auth/types/account.go#L230-L254
|
||||
func moduleAccountEncoder(_ *Encoder, msg protoreflect.Message, w io.Writer) error {
|
||||
ma := msg.Interface().(*authapi.ModuleAccount)
|
||||
pretty := moduleAccountPretty{
|
||||
PubKey: "",
|
||||
Name: ma.Name,
|
||||
Permissions: ma.Permissions,
|
||||
}
|
||||
if ma.BaseAccount != nil {
|
||||
pretty.Address = ma.BaseAccount.Address
|
||||
pretty.AccountNumber = ma.BaseAccount.AccountNumber
|
||||
pretty.Sequence = ma.BaseAccount.Sequence
|
||||
} else {
|
||||
pretty.Address = ""
|
||||
pretty.AccountNumber = 0
|
||||
pretty.Sequence = 0
|
||||
}
|
||||
|
||||
bz, err := json.Marshal(pretty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(bz)
|
||||
return err
|
||||
}
|
||||
|
||||
// thresholdStringEncoder replicates the behavior at:
|
||||
// https://github.com/cosmos/cosmos-sdk/blob/4a6a1e3cb8de459891cb0495052589673d14ef51/crypto/keys/multisig/amino.go#L35
|
||||
// also see:
|
||||
// https://github.com/cosmos/cosmos-sdk/blob/b49f948b36bc991db5be431607b475633aed697e/proto/cosmos/crypto/multisig/keys.proto#L15/
|
||||
func thresholdStringEncoder(enc *Encoder, msg protoreflect.Message, w io.Writer) error {
|
||||
pk, ok := msg.Interface().(*multisig.LegacyAminoPubKey)
|
||||
if !ok {
|
||||
return errors.New("thresholdStringEncoder: msg not a multisig.LegacyAminoPubKey")
|
||||
}
|
||||
_, err := w.Write([]byte(fmt.Sprintf(`{"threshold":"%d","pubkeys":`, pk.Threshold)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(pk.PublicKeys) == 0 {
|
||||
_, err = w.Write([]byte(`[]}`))
|
||||
return err
|
||||
}
|
||||
|
||||
fields := msg.Descriptor().Fields()
|
||||
pubkeysField := fields.ByName("public_keys")
|
||||
pubkeys := msg.Get(pubkeysField).List()
|
||||
|
||||
err = enc.marshalList(pubkeys, w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write([]byte(`}`))
|
||||
return err
|
||||
}
|
||||
3
x/tx/aminojson/internal/Makefile
Normal file
3
x/tx/aminojson/internal/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
codegen:
|
||||
@echo "Generating proto files"
|
||||
buf generate
|
||||
15
x/tx/aminojson/internal/buf.gen.yaml
Normal file
15
x/tx/aminojson/internal/buf.gen.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
version: v1
|
||||
managed:
|
||||
enabled: true
|
||||
go_package_prefix:
|
||||
default: cosmossdk.io/x/tx/aminojson/internal/testpb
|
||||
except:
|
||||
- buf.build/googleapis/googleapis
|
||||
- buf.build/cosmos/gogo-proto
|
||||
- buf.build/cosmos/cosmos-proto
|
||||
override:
|
||||
buf.build/cosmos/cosmos-sdk: cosmossdk.io/api
|
||||
plugins:
|
||||
- name: go-pulsar
|
||||
out: .
|
||||
opt: paths=source_relative
|
||||
7
x/tx/aminojson/internal/buf.lock
Normal file
7
x/tx/aminojson/internal/buf.lock
Normal file
@ -0,0 +1,7 @@
|
||||
# Generated by buf. DO NOT EDIT.
|
||||
version: v1
|
||||
deps:
|
||||
- remote: buf.build
|
||||
owner: cosmos
|
||||
repository: cosmos-proto
|
||||
commit: 1935555c206d4afb9e94615dfd0fad31
|
||||
11
x/tx/aminojson/internal/buf.yaml
Normal file
11
x/tx/aminojson/internal/buf.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
version: v1
|
||||
deps:
|
||||
- buf.build/cosmos/cosmos-proto
|
||||
lint:
|
||||
use:
|
||||
- DEFAULT
|
||||
except:
|
||||
- PACKAGE_VERSION_SUFFIX
|
||||
breaking:
|
||||
ignore:
|
||||
- testpb
|
||||
76
x/tx/aminojson/internal/testpb/test.proto
Normal file
76
x/tx/aminojson/internal/testpb/test.proto
Normal file
@ -0,0 +1,76 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package testpb;
|
||||
|
||||
import "amino/amino.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/duration.proto";
|
||||
import "google/protobuf/struct.proto";
|
||||
import "google/protobuf/wrappers.proto";
|
||||
import "google/protobuf/field_mask.proto";
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
message WithAMap{
|
||||
map<string, string> str_map = 1;
|
||||
}
|
||||
|
||||
message WithAList{
|
||||
repeated string dont_omitempty_list = 1 [ (amino.dont_omitempty) = true ];
|
||||
repeated string list = 2;
|
||||
}
|
||||
|
||||
message ABitOfEverything {
|
||||
option (amino.name) = "ABitOfEverything";
|
||||
|
||||
NestedMessage message = 1;
|
||||
AnEnum enum = 2;
|
||||
|
||||
repeated int32 repeated = 6;
|
||||
|
||||
string str = 7;
|
||||
bool bool = 8;
|
||||
bytes bytes = 9;
|
||||
int32 i32 = 10;
|
||||
fixed32 f32 = 11;
|
||||
uint32 u32 = 12;
|
||||
sint32 si32 = 13;
|
||||
sfixed32 sf32 = 14;
|
||||
int64 i64 = 15;
|
||||
fixed64 f64 = 16;
|
||||
uint64 u64 = 17;
|
||||
sint64 si64 = 18;
|
||||
sfixed64 sf64 = 19;
|
||||
|
||||
// The following types are not tested here because they are treated fundamentally differently in
|
||||
// gogoproto. They are tested fully in /tests/integration/aminojson/aminojson_test.go
|
||||
|
||||
// Any types are not tested here because they are treated fundamentally differently in gogoproto.
|
||||
// In the go-admin/gogoproto paradigm a custom SDK type is used to wrap the Any type, which has no
|
||||
// equivalent in code in pulsar generated types.
|
||||
//
|
||||
// google.protobuf.Any any = 22
|
||||
|
||||
// Also not tested due to gogoproto differences. In gogoproto, time.Time is used instead of
|
||||
// google.protobuf.Timestamp. There is no equivalent in pulsar generated types.
|
||||
//
|
||||
// google.protobuf.Timestamp timestamp = 23;
|
||||
|
||||
// Also not tested due to gogoproto differences. In gogoproto, time.Duration is used instead of
|
||||
// google.protobuf.Duration. There is no equivalent in pulsar generated types.
|
||||
//
|
||||
// google.protobuf.Duration duration = 24;
|
||||
}
|
||||
|
||||
message NestedMessage {
|
||||
option (amino.name) = "NestedMessage";
|
||||
|
||||
string foo = 1;
|
||||
int32 bar = 2;
|
||||
}
|
||||
|
||||
enum AnEnum {
|
||||
UNDEFINED = 0;
|
||||
ONE = 1;
|
||||
TWO = 2;
|
||||
}
|
||||
3587
x/tx/aminojson/internal/testpb/test.pulsar.go
Normal file
3587
x/tx/aminojson/internal/testpb/test.pulsar.go
Normal file
File diff suppressed because it is too large
Load Diff
300
x/tx/aminojson/json_marshal.go
Normal file
300
x/tx/aminojson/json_marshal.go
Normal file
@ -0,0 +1,300 @@
|
||||
package aminojson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
)
|
||||
|
||||
// MessageEncoder is a function that can encode a protobuf protoreflect.Message to JSON.
|
||||
type MessageEncoder func(*Encoder, protoreflect.Message, io.Writer) error
|
||||
|
||||
// FieldEncoder is a function that can encode a protobuf protoreflect.Value to JSON.
|
||||
type FieldEncoder func(*Encoder, protoreflect.Value, io.Writer) error
|
||||
|
||||
// Encoder is a JSON encoder that uses the Amino JSON encoding rules for protobuf messages.
|
||||
type Encoder struct {
|
||||
// maps cosmos_proto.scalar -> field encoder
|
||||
scalarEncoders map[string]FieldEncoder
|
||||
messageEncoders map[string]MessageEncoder
|
||||
fieldEncoders map[string]FieldEncoder
|
||||
}
|
||||
|
||||
// NewAminoJSON returns a new Encoder capable of serializing protobuf messages to JSON using the Amino JSON encoding
|
||||
// rules.
|
||||
func NewAminoJSON() Encoder {
|
||||
enc := Encoder{
|
||||
scalarEncoders: map[string]FieldEncoder{
|
||||
"cosmos.Dec": cosmosDecEncoder,
|
||||
"cosmos.Int": cosmosIntEncoder,
|
||||
},
|
||||
messageEncoders: map[string]MessageEncoder{
|
||||
"key_field": keyFieldEncoder,
|
||||
"module_account": moduleAccountEncoder,
|
||||
"threshold_string": thresholdStringEncoder,
|
||||
},
|
||||
fieldEncoders: map[string]FieldEncoder{
|
||||
"legacy_coins": nullSliceAsEmptyEncoder,
|
||||
"cosmos_dec_bytes": cosmosDecEncoder,
|
||||
},
|
||||
}
|
||||
return enc
|
||||
}
|
||||
|
||||
// DefineMessageEncoding defines a custom encoding for a protobuf message. The `name` field must match a usage of
|
||||
// an (amino.message_encoding) option in the protobuf message as in the following example. This encoding will be
|
||||
// used instead of the default encoding for all usages of the tagged message.
|
||||
//
|
||||
// message ModuleAccount {
|
||||
// option (amino.name) = "cosmos-sdk/ModuleAccount";
|
||||
// option (amino.message_encoding) = "module_account";
|
||||
// ...
|
||||
// }
|
||||
func (enc Encoder) DefineMessageEncoding(name string, encoder MessageEncoder) Encoder {
|
||||
if enc.messageEncoders == nil {
|
||||
enc.messageEncoders = map[string]MessageEncoder{}
|
||||
}
|
||||
enc.messageEncoders[name] = encoder
|
||||
return enc
|
||||
}
|
||||
|
||||
// DefineFieldEncoding defines a custom encoding for a protobuf field. The `name` field must match a usage of
|
||||
// an (amino.encoding) option in the protobuf message as in the following example. This encoding will be used
|
||||
// instead of the default encoding for all usages of the tagged field.
|
||||
//
|
||||
// message Balance {
|
||||
// repeated cosmos.base.v1beta1.Coin coins = 2 [
|
||||
// (amino.encoding) = "legacy_coins",
|
||||
// (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins",
|
||||
// (gogoproto.nullable) = false,
|
||||
// (amino.dont_omitempty) = true
|
||||
// ];
|
||||
// ...
|
||||
// }
|
||||
func (enc Encoder) DefineFieldEncoding(name string, encoder FieldEncoder) Encoder {
|
||||
if enc.fieldEncoders == nil {
|
||||
enc.fieldEncoders = map[string]FieldEncoder{}
|
||||
}
|
||||
enc.fieldEncoders[name] = encoder
|
||||
return enc
|
||||
}
|
||||
|
||||
// Marshal serializes a protobuf message to JSON.
|
||||
func (enc Encoder) Marshal(message proto.Message) ([]byte, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
err := enc.beginMarshal(message.ProtoReflect(), buf)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
func (enc Encoder) beginMarshal(msg protoreflect.Message, writer io.Writer) error {
|
||||
name, named := getMessageAminoName(msg)
|
||||
if named {
|
||||
_, err := writer.Write([]byte(fmt.Sprintf(`{"type":"%s","value":`, name)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := enc.marshal(protoreflect.ValueOfMessage(msg), writer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if named {
|
||||
_, err = writer.Write([]byte("}"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (enc Encoder) marshal(value protoreflect.Value, writer io.Writer) error {
|
||||
switch val := value.Interface().(type) {
|
||||
case protoreflect.Message:
|
||||
err := enc.marshalMessage(val, writer)
|
||||
return err
|
||||
|
||||
case protoreflect.Map:
|
||||
return errors.New("maps are not supported")
|
||||
|
||||
case protoreflect.List:
|
||||
if !val.IsValid() {
|
||||
_, err := writer.Write([]byte("null"))
|
||||
return err
|
||||
}
|
||||
return enc.marshalList(val, writer)
|
||||
|
||||
case string, bool, int32, uint32, []byte, protoreflect.EnumNumber:
|
||||
return jsonMarshal(writer, val)
|
||||
|
||||
case uint64, int64:
|
||||
_, err := fmt.Fprintf(writer, `"%d"`, val) // quoted
|
||||
return err
|
||||
|
||||
default:
|
||||
return errors.Errorf("unknown type %T", val)
|
||||
}
|
||||
}
|
||||
|
||||
func (enc Encoder) marshalMessage(msg protoreflect.Message, writer io.Writer) error {
|
||||
if msg == nil {
|
||||
return errors.New("nil message")
|
||||
}
|
||||
|
||||
switch msg.Descriptor().FullName() {
|
||||
case timestampFullName:
|
||||
// replicate https://github.com/tendermint/go-amino/blob/8e779b71f40d175cd1302d3cd41a75b005225a7a/json-encode.go#L45-L51
|
||||
return marshalTimestamp(msg, writer)
|
||||
case durationFullName:
|
||||
return marshalDuration(msg, writer)
|
||||
case anyFullName:
|
||||
return enc.marshalAny(msg, writer)
|
||||
}
|
||||
|
||||
if encoder := enc.getMessageEncoder(msg); encoder != nil {
|
||||
err := encoder(&enc, msg, writer)
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := writer.Write([]byte("{"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields := msg.Descriptor().Fields()
|
||||
first := true
|
||||
emptyOneOfWritten := map[string]bool{}
|
||||
for i := 0; i < fields.Len(); i++ {
|
||||
f := fields.Get(i)
|
||||
v := msg.Get(f)
|
||||
name := getAminoFieldName(f)
|
||||
oneof := f.ContainingOneof()
|
||||
isOneOf := oneof != nil
|
||||
oneofFieldName, oneofTypeName, err := getOneOfNames(f)
|
||||
if err != nil && isOneOf {
|
||||
return err
|
||||
}
|
||||
writeNil := false
|
||||
|
||||
if !msg.Has(f) {
|
||||
// msg.WhichOneof(oneof) == nil: no field of the oneof has been set
|
||||
// !emptyOneOfWritten: we haven't written a null for this oneof yet (only write one null per empty oneof)
|
||||
if isOneOf && msg.WhichOneof(oneof) == nil && !emptyOneOfWritten[oneofFieldName] {
|
||||
name = oneofFieldName
|
||||
writeNil = true
|
||||
emptyOneOfWritten[oneofFieldName] = true
|
||||
} else if omitEmpty(f) {
|
||||
continue
|
||||
} else if f.Kind() == protoreflect.MessageKind &&
|
||||
f.Cardinality() != protoreflect.Repeated &&
|
||||
!v.Message().IsValid() {
|
||||
return errors.Errorf("not supported: dont_omit_empty=true on invalid (nil?) message field: %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
if !first {
|
||||
_, err = writer.Write([]byte(","))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if isOneOf && !writeNil {
|
||||
_, err = writer.Write([]byte(fmt.Sprintf(`"%s":{"type":"%s","value":{`,
|
||||
oneofFieldName, oneofTypeName)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = jsonMarshal(writer, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = writer.Write([]byte(":"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// encode value
|
||||
if encoder := enc.getFieldEncoding(f); encoder != nil {
|
||||
err = encoder(&enc, v, writer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if writeNil {
|
||||
_, err = writer.Write([]byte("null"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = enc.marshal(v, writer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if isOneOf && !writeNil {
|
||||
_, err = writer.Write([]byte("}}"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
first = false
|
||||
}
|
||||
|
||||
_, err = writer.Write([]byte("}"))
|
||||
return err
|
||||
}
|
||||
|
||||
func jsonMarshal(w io.Writer, v interface{}) error {
|
||||
blob, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(blob)
|
||||
return err
|
||||
}
|
||||
|
||||
func (enc Encoder) marshalList(list protoreflect.List, writer io.Writer) error {
|
||||
n := list.Len()
|
||||
|
||||
_, err := writer.Write([]byte("["))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
first := true
|
||||
for i := 0; i < n; i++ {
|
||||
if !first {
|
||||
_, err := writer.Write([]byte(","))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
first = false
|
||||
|
||||
err = enc.marshal(list.Get(i), writer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = writer.Write([]byte("]"))
|
||||
return err
|
||||
}
|
||||
|
||||
const (
|
||||
timestampFullName protoreflect.FullName = "google.protobuf.Timestamp"
|
||||
durationFullName protoreflect.FullName = "google.protobuf.Duration"
|
||||
anyFullName protoreflect.FullName = "google.protobuf.Any"
|
||||
)
|
||||
133
x/tx/aminojson/json_marshal_test.go
Normal file
133
x/tx/aminojson/json_marshal_test.go
Normal file
@ -0,0 +1,133 @@
|
||||
package aminojson_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-proto/rapidproto"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tendermint/go-amino"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"pgregory.net/rapid"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
|
||||
"cosmossdk.io/x/tx/aminojson"
|
||||
|
||||
"cosmossdk.io/x/tx/aminojson/internal/testpb"
|
||||
)
|
||||
|
||||
func marshalLegacy(msg proto.Message) ([]byte, error) {
|
||||
cdc := amino.NewCodec()
|
||||
cdc.RegisterConcrete(&testpb.ABitOfEverything{}, "ABitOfEverything", nil)
|
||||
cdc.RegisterConcrete(&testpb.NestedMessage{}, "NestedMessage", nil)
|
||||
return cdc.MarshalJSON(msg)
|
||||
}
|
||||
|
||||
func TestAminoJSON_EdgeCases(t *testing.T) {
|
||||
|
||||
cdc := amino.NewCodec()
|
||||
cdc.RegisterConcrete(&testpb.ABitOfEverything{}, "ABitOfEverything", nil)
|
||||
cdc.RegisterConcrete(&testpb.NestedMessage{}, "NestedMessage", nil)
|
||||
aj := aminojson.NewAminoJSON()
|
||||
|
||||
cases := map[string]struct {
|
||||
msg proto.Message
|
||||
shouldErr bool
|
||||
}{
|
||||
"empty": {msg: &testpb.ABitOfEverything{}},
|
||||
"single map": {msg: &testpb.WithAMap{StrMap: map[string]string{"foo": "bar"}}, shouldErr: true},
|
||||
}
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
bz, err := aj.Marshal(tc.msg)
|
||||
|
||||
if tc.shouldErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
rv := reflect.New(reflect.TypeOf(tc.msg).Elem()).Elem()
|
||||
msg2 := rv.Addr().Interface().(proto.Message)
|
||||
|
||||
legacyBz, err := cdc.MarshalJSON(tc.msg)
|
||||
assert.NilError(t, err)
|
||||
|
||||
require.Equal(t, string(legacyBz), string(bz))
|
||||
|
||||
goProtoJSON, err := protojson.Marshal(tc.msg)
|
||||
err = cdc.UnmarshalJSON(bz, msg2)
|
||||
assert.NilError(t, err, "unmarshal failed: %s vs %s", legacyBz, goProtoJSON)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAminoJSON(t *testing.T) {
|
||||
cdc := amino.NewCodec()
|
||||
cdc.RegisterConcrete(&testpb.ABitOfEverything{}, "ABitOfEverything", nil)
|
||||
cdc.RegisterConcrete(&testpb.NestedMessage{}, "NestedMessage", nil)
|
||||
|
||||
msg := &testpb.ABitOfEverything{
|
||||
Message: &testpb.NestedMessage{
|
||||
Foo: "test",
|
||||
Bar: 0, // this is the default value and should be omitted from output
|
||||
},
|
||||
Enum: testpb.AnEnum_ONE,
|
||||
Repeated: []int32{3, -7, 2, 6, 4},
|
||||
Str: `abcxyz"foo"def`,
|
||||
Bool: true,
|
||||
Bytes: []byte{0, 1, 2, 3},
|
||||
I32: -15,
|
||||
F32: 1001,
|
||||
U32: 1200,
|
||||
Si32: -376,
|
||||
Sf32: -1000,
|
||||
I64: 14578294827584932,
|
||||
F64: 9572348124213523654,
|
||||
U64: 4759492485,
|
||||
Si64: -59268425823934,
|
||||
Sf64: -659101379604211154,
|
||||
}
|
||||
bz, err := aminojson.NewAminoJSON().Marshal(msg)
|
||||
assert.NilError(t, err)
|
||||
legacyBz, err := cdc.MarshalJSON(msg)
|
||||
assert.NilError(t, err)
|
||||
require.Equal(t, string(legacyBz), string(bz))
|
||||
}
|
||||
|
||||
func TestRapid(t *testing.T) {
|
||||
gen := rapidproto.MessageGenerator(&testpb.ABitOfEverything{}, rapidproto.GeneratorOptions{})
|
||||
rapid.Check(t, func(t *rapid.T) {
|
||||
msg := gen.Draw(t, "msg")
|
||||
bz, err := aminojson.NewAminoJSON().Marshal(msg)
|
||||
assert.NilError(t, err)
|
||||
checkInvariants(t, msg, bz)
|
||||
})
|
||||
}
|
||||
|
||||
func checkInvariants(t *rapid.T, message proto.Message, marshaledBytes []byte) {
|
||||
checkLegacyParity(t, message, marshaledBytes)
|
||||
checkRoundTrip(t, message, marshaledBytes)
|
||||
}
|
||||
|
||||
func checkLegacyParity(t *rapid.T, message proto.Message, marshaledBytes []byte) {
|
||||
legacyBz, err := marshalLegacy(message)
|
||||
assert.NilError(t, err)
|
||||
require.Equal(t, string(legacyBz), string(marshaledBytes), "%s vs legacy: %s", string(marshaledBytes),
|
||||
string(legacyBz))
|
||||
}
|
||||
|
||||
func checkRoundTrip(t *rapid.T, message proto.Message, marshaledBytes []byte) {
|
||||
cdc := amino.NewCodec()
|
||||
cdc.RegisterConcrete(&testpb.ABitOfEverything{}, "ABitOfEverything", nil)
|
||||
cdc.RegisterConcrete(&testpb.NestedMessage{}, "NestedMessage", nil)
|
||||
|
||||
message2 := message.ProtoReflect().New().Interface()
|
||||
goProtoJSON, err := cdc.MarshalJSON(message)
|
||||
assert.NilError(t, err)
|
||||
err = cdc.UnmarshalJSON(marshaledBytes, message2)
|
||||
assert.NilError(t, err, "%s vs %s", string(marshaledBytes), string(goProtoJSON))
|
||||
}
|
||||
90
x/tx/aminojson/options.go
Normal file
90
x/tx/aminojson/options.go
Normal file
@ -0,0 +1,90 @@
|
||||
package aminojson
|
||||
|
||||
import (
|
||||
cosmos_proto "github.com/cosmos/cosmos-proto"
|
||||
"github.com/iancoleman/strcase"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
|
||||
"cosmossdk.io/api/amino"
|
||||
)
|
||||
|
||||
// getMessageAminoName returns the amino name of a message if it has been set by the `amino.name` option.
|
||||
// If the message does not have an amino name, then the function returns false.
|
||||
func getMessageAminoName(msg protoreflect.Message) (string, bool) {
|
||||
opts := msg.Descriptor().Options()
|
||||
if proto.HasExtension(opts, amino.E_Name) {
|
||||
name := proto.GetExtension(opts, amino.E_Name)
|
||||
return name.(string), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// omitEmpty returns true if the field should be omitted if empty. Empty field omission is the default behavior.
|
||||
func omitEmpty(field protoreflect.FieldDescriptor) bool {
|
||||
opts := field.Options()
|
||||
if proto.HasExtension(opts, amino.E_DontOmitempty) {
|
||||
dontOmitEmpty := proto.GetExtension(opts, amino.E_DontOmitempty).(bool)
|
||||
return !dontOmitEmpty
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// getAminoFieldName returns the amino field name of a field if it has been set by the `amino.field_name` option.
|
||||
// If the field does not have an amino field name, then the function returns the protobuf field name.
|
||||
func getAminoFieldName(field protoreflect.FieldDescriptor) string {
|
||||
opts := field.Options()
|
||||
if proto.HasExtension(opts, amino.E_FieldName) {
|
||||
return proto.GetExtension(opts, amino.E_FieldName).(string)
|
||||
}
|
||||
return string(field.Name())
|
||||
}
|
||||
|
||||
func getOneOfNames(field protoreflect.FieldDescriptor) (string, string, error) {
|
||||
opts := field.Options()
|
||||
oneOf := field.ContainingOneof()
|
||||
if oneOf == nil {
|
||||
return "", "", errors.Errorf("field %s must be within a oneof", field.Name())
|
||||
}
|
||||
|
||||
fieldName := strcase.ToCamel(string(oneOf.Name()))
|
||||
var typeName string
|
||||
|
||||
if proto.HasExtension(opts, amino.E_OneofName) {
|
||||
typeName = proto.GetExtension(opts, amino.E_OneofName).(string)
|
||||
} else {
|
||||
return "", "", errors.Errorf("field %s within a oneof must have the amino.oneof_type_name option set",
|
||||
field.Name())
|
||||
}
|
||||
|
||||
return fieldName, typeName, nil
|
||||
}
|
||||
|
||||
func (enc Encoder) getMessageEncoder(message protoreflect.Message) MessageEncoder {
|
||||
opts := message.Descriptor().Options()
|
||||
if proto.HasExtension(opts, amino.E_MessageEncoding) {
|
||||
encoding := proto.GetExtension(opts, amino.E_MessageEncoding).(string)
|
||||
if fn, ok := enc.messageEncoders[encoding]; ok {
|
||||
return fn
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (enc Encoder) getFieldEncoding(field protoreflect.FieldDescriptor) FieldEncoder {
|
||||
opts := field.Options()
|
||||
if proto.HasExtension(opts, amino.E_Encoding) {
|
||||
encoding := proto.GetExtension(opts, amino.E_Encoding).(string)
|
||||
if fn, ok := enc.fieldEncoders[encoding]; ok {
|
||||
return fn
|
||||
}
|
||||
}
|
||||
if proto.HasExtension(opts, cosmos_proto.E_Scalar) {
|
||||
scalar := proto.GetExtension(opts, cosmos_proto.E_Scalar).(string)
|
||||
if fn, ok := enc.scalarEncoders[scalar]; ok {
|
||||
return fn
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
73
x/tx/aminojson/time.go
Normal file
73
x/tx/aminojson/time.go
Normal file
@ -0,0 +1,73 @@
|
||||
package aminojson
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"io"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
secondsName protoreflect.Name = "seconds"
|
||||
nanosName protoreflect.Name = "nanos"
|
||||
)
|
||||
|
||||
func marshalTimestamp(message protoreflect.Message, writer io.Writer) error {
|
||||
fields := message.Descriptor().Fields()
|
||||
secondsField := fields.ByName(secondsName)
|
||||
if secondsField == nil {
|
||||
return fmt.Errorf("expected seconds field")
|
||||
}
|
||||
|
||||
nanosField := fields.ByName(nanosName)
|
||||
if nanosField == nil {
|
||||
return fmt.Errorf("expected nanos field")
|
||||
}
|
||||
|
||||
seconds := message.Get(secondsField).Int()
|
||||
nanos := message.Get(nanosField).Int()
|
||||
if nanos < 0 {
|
||||
return fmt.Errorf("nanos must be non-negative on timestamp %v", message)
|
||||
}
|
||||
|
||||
t := time.Unix(seconds, nanos).UTC()
|
||||
var str string
|
||||
if nanos == 0 {
|
||||
str = t.Format(time.RFC3339)
|
||||
} else {
|
||||
str = t.Format(time.RFC3339Nano)
|
||||
}
|
||||
|
||||
_, err := fmt.Fprintf(writer, `"%s"`, str)
|
||||
return err
|
||||
}
|
||||
|
||||
// MaxDurationSeconds the maximum number of seconds (when expressed as nanoseconds) which can fit in an int64.
|
||||
// gogoproto encodes google.protobuf.Duration as a time.Duration, which is 64-bit signed integer.
|
||||
const MaxDurationSeconds = int64(math.MaxInt64/int(1e9)) - 1
|
||||
|
||||
func marshalDuration(message protoreflect.Message, writer io.Writer) error {
|
||||
fields := message.Descriptor().Fields()
|
||||
secondsField := fields.ByName(secondsName)
|
||||
if secondsField == nil {
|
||||
return fmt.Errorf("expected seconds field")
|
||||
}
|
||||
|
||||
// todo
|
||||
// check signs are consistent
|
||||
seconds := message.Get(secondsField).Int()
|
||||
if seconds > MaxDurationSeconds {
|
||||
return fmt.Errorf("%d seconds would overflow an int64 when represented as nanoseconds", seconds)
|
||||
}
|
||||
|
||||
nanosField := fields.ByName(nanosName)
|
||||
if nanosField == nil {
|
||||
return fmt.Errorf("expected nanos field")
|
||||
}
|
||||
|
||||
nanos := message.Get(nanosField).Int()
|
||||
totalNanos := nanos + (seconds * 1e9)
|
||||
_, err := writer.Write([]byte(fmt.Sprintf(`"%d"`, totalNanos)))
|
||||
return err
|
||||
}
|
||||
@ -8,8 +8,13 @@ require (
|
||||
cosmossdk.io/math v1.0.0-beta.6
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.2
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/iancoleman/strcase v0.2.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/stretchr/testify v1.8.1
|
||||
google.golang.org/protobuf v1.28.1
|
||||
github.com/tendermint/go-amino v0.16.0
|
||||
google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8
|
||||
gotest.tools/v3 v3.4.0
|
||||
pgregory.net/rapid v0.5.5
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
44
x/tx/go.sum
44
x/tx/go.sum
@ -11,45 +11,85 @@ github.com/cosmos/gogoproto v1.4.6/go.mod h1:VS/ASYmPgv6zkPKLjR9EB91lwbLHOzaGCir
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
|
||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E=
|
||||
github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20230203172020-98cc5a0785f9 h1:frX3nT9RkKybPnjyI+yvZh6ZucTZatCCEm9D47sZ2zo=
|
||||
golang.org/x/exp v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 h1:vArvWooPH749rNHpBGgVl+U9B9dATjiEhJzcWGlovNs=
|
||||
google.golang.org/genproto v0.0.0-20230202175211-008b39050e57/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
|
||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8 h1:KR8+MyP7/qOlV+8Af01LtjL04bu7on42eVsxT4EyBQk=
|
||||
google.golang.org/protobuf v1.28.2-0.20220831092852-f930b1dc76e8/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
||||
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
||||
pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA=
|
||||
pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
|
||||
Loading…
Reference in New Issue
Block a user