diff --git a/codec/legacy/codec.go b/codec/legacy/codec.go index a4a963b6d21..d5d9a686c57 100644 --- a/codec/legacy/codec.go +++ b/codec/legacy/codec.go @@ -10,10 +10,9 @@ import ( // has all Tendermint crypto and evidence types registered. // // TODO: Deprecated - remove this global. -var Cdc *codec.LegacyAmino +var Cdc = codec.NewLegacyAmino() func init() { - Cdc = codec.NewLegacyAmino() cryptocodec.RegisterCrypto(Cdc) codec.RegisterEvidences(Cdc) } diff --git a/docs/core/encoding.md b/docs/core/encoding.md index 084cffadad2..2565e579ebd 100644 --- a/docs/core/encoding.md +++ b/docs/core/encoding.md @@ -67,6 +67,24 @@ Note, there are length-prefixed variants of the above functionality and this is typically used for when the data needs to be streamed or grouped together (e.g. `ResponseDeliverTx.Data`) +#### Authz authorizations + +Since the `MsgExec` message type can contain different messages instances, it is important that developers +add the following code inside the `init` method of their module's `codec.go` file: + +```go +import authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" + +init() { + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + RegisterLegacyAminoCodec(authzcodec.Amino) +} +``` + +This will allow the `x/authz` module to properly serialize and de-serializes `MsgExec` instances using Amino, +which is required when signing this kind of messages using a Ledger. + ### Gogoproto Modules are encouraged to utilize Protobuf encoding for their respective types. In the SDK, we use the [Gogoproto](https://github.com/gogo/protobuf) specific implementation of the Protobuf spec that offers speed and DX improvements compared to the official [Google protobuf implementation](https://github.com/protocolbuffers/protobuf). diff --git a/x/auth/types/codec.go b/x/auth/types/codec.go index 629e2919d24..ae1590a616f 100644 --- a/x/auth/types/codec.go +++ b/x/auth/types/codec.go @@ -4,7 +4,9 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" ) // RegisterLegacyAminoCodec registers the account interfaces and concrete types on the @@ -45,4 +47,9 @@ var ( func init() { RegisterLegacyAminoCodec(amino) cryptocodec.RegisterCrypto(amino) + sdk.RegisterLegacyAminoCodec(amino) + + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + RegisterLegacyAminoCodec(authzcodec.Amino) } diff --git a/x/auth/vesting/types/codec.go b/x/auth/vesting/types/codec.go index 2d26af8bdfc..d3cad4ed322 100644 --- a/x/auth/vesting/types/codec.go +++ b/x/auth/vesting/types/codec.go @@ -3,10 +3,12 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" ) // RegisterLegacyAminoCodec registers the vesting interfaces and concrete types on the @@ -63,9 +65,17 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) } -var amino = codec.NewLegacyAmino() +var ( + amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewAminoCodec(amino) +) func init() { RegisterLegacyAminoCodec(amino) - amino.Seal() + cryptocodec.RegisterCrypto(amino) + sdk.RegisterLegacyAminoCodec(amino) + + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + RegisterLegacyAminoCodec(authzcodec.Amino) } diff --git a/x/auth/vesting/types/msgs.go b/x/auth/vesting/types/msgs.go index 82b038d600c..7625b7531bb 100644 --- a/x/auth/vesting/types/msgs.go +++ b/x/auth/vesting/types/msgs.go @@ -69,7 +69,7 @@ func (msg MsgCreateVestingAccount) ValidateBasic() error { // GetSignBytes returns the bytes all expected signers must sign over for a // MsgCreateVestingAccount. func (msg MsgCreateVestingAccount) GetSignBytes() []byte { - return sdk.MustSortJSON(amino.MustMarshalJSON(&msg)) + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) } // GetSigners returns the expected signers for a MsgCreateVestingAccount. @@ -112,7 +112,7 @@ func (msg MsgCreateClawbackVestingAccount) GetSigners() []sdk.AccAddress { // GetSignBytes returns the bytes all expected signers must sign over for a // MsgCreateClawbackVestingAccount. func (msg MsgCreateClawbackVestingAccount) GetSignBytes() []byte { - return sdk.MustSortJSON(amino.MustMarshalJSON(&msg)) + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) } // ValidateBasic Implements Msg. diff --git a/x/authz/authorization_grant.go b/x/authz/authorization_grant.go index a873499b621..30bc1eec467 100644 --- a/x/authz/authorization_grant.go +++ b/x/authz/authorization_grant.go @@ -9,12 +9,12 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -// NewGrant returns new Grant -func NewGrant( /*blockTime time.Time, */ a Authorization, expiration time.Time) (Grant, error) { - // TODO: add this for 0.45 - // if !expiration.After(blockTime) { - // return Grant{}, sdkerrors.ErrInvalidRequest.Wrapf("expiration must be after the current block time (%v), got %v", blockTime.Format(time.RFC3339), expiration.Format(time.RFC3339)) - // } +// NewGrant returns new Grant. It returns an error if the expiration is before +// the current block time, which is passed into the `blockTime` arg. +func NewGrant(blockTime time.Time, a Authorization, expiration time.Time) (Grant, error) { + if !expiration.After(blockTime) { + return Grant{}, sdkerrors.ErrInvalidRequest.Wrapf("expiration must be after the current block time (%v), got %v", blockTime.Format(time.RFC3339), expiration.Format(time.RFC3339)) + } g := Grant{ Expiration: expiration, } diff --git a/x/authz/authorization_grant_test.go b/x/authz/authorization_grant_test.go index 9f9f00108c7..849b48d32ac 100644 --- a/x/authz/authorization_grant_test.go +++ b/x/authz/authorization_grant_test.go @@ -4,7 +4,6 @@ import ( "testing" "time" - // banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/stretchr/testify/require" ) @@ -18,7 +17,6 @@ func expecError(r *require.Assertions, expected string, received error) { } func TestNewGrant(t *testing.T) { - // ba := banktypes.NewSendAuthorization(sdk.NewCoins(sdk.NewInt64Coin("foo", 123))) a := NewGenericAuthorization("some-type") var tcs = []struct { title string @@ -27,16 +25,16 @@ func TestNewGrant(t *testing.T) { expire time.Time err string }{ - // {"wrong expire time (1)", a, time.Unix(10, 0), time.Unix(8, 0), "expiration must be after"}, - // {"wrong expire time (2)", a, time.Unix(10, 0), time.Unix(10, 0), "expiration must be after"}, + {"wrong expire time (1)", a, time.Unix(10, 0), time.Unix(8, 0), "expiration must be after"}, + {"wrong expire time (2)", a, time.Unix(10, 0), time.Unix(10, 0), "expiration must be after"}, {"good expire time (1)", a, time.Unix(10, 0), time.Unix(10, 1), ""}, {"good expire time (2)", a, time.Unix(10, 0), time.Unix(11, 0), ""}, } for _, tc := range tcs { + tc := tc t.Run(tc.title, func(t *testing.T) { - // _, err := NewGrant(tc.blockTime, tc.a, tc.expire) - _, err := NewGrant(tc.a, tc.expire) + _, err := NewGrant(tc.blockTime, tc.a, tc.expire) expecError(require.New(t), tc.err, err) }) } diff --git a/x/authz/client/cli/tx.go b/x/authz/client/cli/tx.go index f4117ee1c74..0200e615e03 100644 --- a/x/authz/client/cli/tx.go +++ b/x/authz/client/cli/tx.go @@ -56,7 +56,7 @@ func NewCmdGrantAuthorization() *cobra.Command { Use: "grant --from ", Short: "Grant authorization to an address", Long: strings.TrimSpace( - fmt.Sprintf(`grant authorization to an address to execute a transaction on your behalf: + fmt.Sprintf(`create a new grant authorization to an address to execute a transaction on your behalf: Examples: $ %s tx %s grant cosmos1skjw.. send %s --spend-limit=1000stake --from=cosmos1skl.. diff --git a/x/authz/client/rest/grpc_query_test.go b/x/authz/client/rest/grpc_query_test.go index 614e283a15e..0a1bb8d1138 100644 --- a/x/authz/client/rest/grpc_query_test.go +++ b/x/authz/client/rest/grpc_query_test.go @@ -1,3 +1,4 @@ +//go:build norace // +build norace package rest_test @@ -61,7 +62,7 @@ func (s *IntegrationTestSuite) SetupSuite() { s.Require().Contains(out.String(), `"code":0`) // grant authorization - out, err = authztestutil.ExecGrant(val, []string{ + out, err = authztestutil.CreateGrant(val, []string{ newAddr.String(), "send", fmt.Sprintf("--%s=100steak", cli.FlagSpendLimit), @@ -177,7 +178,7 @@ func (s *IntegrationTestSuite) TestQueryGrantsGRPC() { false, "", func() { - _, err := authztestutil.ExecGrant(val, []string{ + _, err := authztestutil.CreateGrant(val, []string{ s.grantee.String(), "generic", fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), diff --git a/x/authz/client/testutil/query.go b/x/authz/client/testutil/query.go index 529939a7019..a91362fa1b0 100644 --- a/x/authz/client/testutil/query.go +++ b/x/authz/client/testutil/query.go @@ -20,7 +20,7 @@ func (s *IntegrationTestSuite) TestQueryAuthorizations() { grantee := s.grantee twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() - _, err := ExecGrant( + _, err := CreateGrant( val, []string{ grantee.String(), @@ -98,7 +98,7 @@ func (s *IntegrationTestSuite) TestQueryAuthorization() { grantee := s.grantee twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() - _, err := ExecGrant( + _, err := CreateGrant( val, []string{ grantee.String(), diff --git a/x/authz/client/testutil/test_helpers.go b/x/authz/client/testutil/test_helpers.go index 1a1cd4830fc..f1a990c54c9 100644 --- a/x/authz/client/testutil/test_helpers.go +++ b/x/authz/client/testutil/test_helpers.go @@ -7,7 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/authz/client/cli" ) -func ExecGrant(val *network.Validator, args []string) (testutil.BufferWriter, error) { +func CreateGrant(val *network.Validator, args []string) (testutil.BufferWriter, error) { cmd := cli.NewCmdGrantAuthorization() clientCtx := val.ClientCtx return clitestutil.ExecTestCLICmd(clientCtx, cmd, args) diff --git a/x/authz/client/testutil/tx.go b/x/authz/client/testutil/tx.go index ebc3e3c31d2..7659bb8ce78 100644 --- a/x/authz/client/testutil/tx.go +++ b/x/authz/client/testutil/tx.go @@ -85,8 +85,8 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { val := s.network.Validators[0] grantee := s.grantee - twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() - pastHour := time.Now().Add(time.Minute * time.Duration(-60)).Unix() + twoHours := time.Now().Add(time.Minute * 120).Unix() + pastHour := time.Now().Add(-time.Minute * 60).Unix() testCases := []struct { name string @@ -278,15 +278,14 @@ func (s *IntegrationTestSuite) TestCLITxGrantAuthorization() { } for _, tc := range testCases { - tc := tc s.Run(tc.name, func() { clientCtx := val.ClientCtx - out, err := ExecGrant( + out, err := CreateGrant( val, tc.args, ) if tc.expectErr { - s.Require().Error(err) + s.Require().Error(err, out) } else { var txResp sdk.TxResponse s.Require().NoError(err) @@ -310,7 +309,7 @@ func (s *IntegrationTestSuite) TestCmdRevokeAuthorizations() { twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() // send-authorization - _, err := ExecGrant( + _, err := CreateGrant( val, []string{ grantee.String(), @@ -326,7 +325,7 @@ func (s *IntegrationTestSuite) TestCmdRevokeAuthorizations() { s.Require().NoError(err) // generic-authorization - _, err = ExecGrant( + _, err = CreateGrant( val, []string{ grantee.String(), @@ -342,7 +341,7 @@ func (s *IntegrationTestSuite) TestCmdRevokeAuthorizations() { s.Require().NoError(err) // generic-authorization used for amino testing - _, err = ExecGrant( + _, err = CreateGrant( val, []string{ grantee.String(), @@ -455,7 +454,7 @@ func (s *IntegrationTestSuite) TestExecAuthorizationWithExpiration() { grantee := s.grantee tenSeconds := time.Now().Add(time.Second * time.Duration(10)).Unix() - _, err := ExecGrant( + _, err := CreateGrant( val, []string{ grantee.String(), @@ -495,7 +494,7 @@ func (s *IntegrationTestSuite) TestNewExecGenericAuthorized() { grantee := s.grantee twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() - _, err := ExecGrant( + _, err := CreateGrant( val, []string{ grantee.String(), @@ -597,7 +596,7 @@ func (s *IntegrationTestSuite) TestNewExecGrantAuthorized() { grantee := s.grantee twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() - _, err := ExecGrant( + _, err := CreateGrant( val, []string{ grantee.String(), @@ -682,7 +681,7 @@ func (s *IntegrationTestSuite) TestExecDelegateAuthorization() { grantee := s.grantee twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() - _, err := ExecGrant( + _, err := CreateGrant( val, []string{ grantee.String(), @@ -774,7 +773,7 @@ func (s *IntegrationTestSuite) TestExecDelegateAuthorization() { } // test delegate no spend-limit - _, err = ExecGrant( + _, err = CreateGrant( val, []string{ grantee.String(), @@ -851,7 +850,7 @@ func (s *IntegrationTestSuite) TestExecDelegateAuthorization() { } // test delegating to denied validator - _, err = ExecGrant( + _, err = CreateGrant( val, []string{ grantee.String(), @@ -886,7 +885,7 @@ func (s *IntegrationTestSuite) TestExecUndelegateAuthorization() { twoHours := time.Now().Add(time.Minute * time.Duration(120)).Unix() // granting undelegate msg authorization - _, err := ExecGrant( + _, err := CreateGrant( val, []string{ grantee.String(), @@ -995,7 +994,7 @@ func (s *IntegrationTestSuite) TestExecUndelegateAuthorization() { } // grant undelegate authorization without limit - _, err = ExecGrant( + _, err = CreateGrant( val, []string{ grantee.String(), diff --git a/x/authz/codec.go b/x/authz/codec.go index e9a490cea37..65e67437049 100644 --- a/x/authz/codec.go +++ b/x/authz/codec.go @@ -1,11 +1,24 @@ package authz import ( + "github.com/cosmos/cosmos-sdk/codec" types "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" ) +// RegisterLegacyAminoCodec registers the necessary x/authz interfaces and concrete types +// on the provided LegacyAmino codec. These types are used for Amino JSON serialization. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgGrant{}, "cosmos-sdk/MsgGrant", nil) + cdc.RegisterConcrete(&MsgRevoke{}, "cosmos-sdk/MsgRevoke", nil) + cdc.RegisterConcrete(&MsgExec{}, "cosmos-sdk/MsgExec", nil) + + cdc.RegisterInterface((*Authorization)(nil), nil) + cdc.RegisterConcrete(&GenericAuthorization{}, "cosmos-sdk/GenericAuthorization", nil) +} + // RegisterInterfaces registers the interfaces types with the interface registry func RegisterInterfaces(registry types.InterfaceRegistry) { registry.RegisterImplementations((*sdk.Msg)(nil), @@ -22,3 +35,8 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { msgservice.RegisterMsgServiceDesc(registry, MsgServiceDesc()) } +func init() { + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + RegisterLegacyAminoCodec(authzcodec.Amino) +} diff --git a/x/authz/codec/cdc.go b/x/authz/codec/cdc.go new file mode 100644 index 00000000000..520e435afd6 --- /dev/null +++ b/x/authz/codec/cdc.go @@ -0,0 +1,18 @@ +package codec + +import ( + "github.com/cosmos/cosmos-sdk/codec" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + Amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewAminoCodec(Amino) +) + +func init() { + cryptocodec.RegisterCrypto(Amino) + codec.RegisterEvidences(Amino) + sdk.RegisterLegacyAminoCodec(Amino) +} diff --git a/x/authz/codec/doc.go b/x/authz/codec/doc.go new file mode 100644 index 00000000000..ecc365a2211 --- /dev/null +++ b/x/authz/codec/doc.go @@ -0,0 +1,18 @@ +/* +Package codec provides a singleton instance of Amino codec that should be used to register +any concrete type that can later be referenced inside a MsgGrant or MsgExec instance so that they +can be (de)serialized properly. + +Amino types should be ideally registered inside this codec within the init function of each module's +codec.go file as follows: + +func init() { + // ... + + RegisterLegacyAminoCodec(authzcodec.Amino) +} + +The codec instance is put inside this package and not the x/authz package in order to avoid any dependency cycle. + +*/ +package codec diff --git a/x/authz/keeper/keeper.go b/x/authz/keeper/keeper.go index ce4e421c125..a09063cb9f5 100644 --- a/x/authz/keeper/keeper.go +++ b/x/authz/keeper/keeper.go @@ -132,7 +132,7 @@ func (k Keeper) DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, msgs [] func (k Keeper) SaveGrant(ctx sdk.Context, grantee, granter sdk.AccAddress, authorization authz.Authorization, expiration time.Time) error { store := ctx.KVStore(k.storeKey) - grant, err := authz.NewGrant(authorization, expiration) + grant, err := authz.NewGrant(ctx.BlockTime(), authorization, expiration) if err != nil { return err } @@ -232,14 +232,20 @@ func (k Keeper) ExportGenesis(ctx sdk.Context) *authz.GenesisState { // InitGenesis new authz genesis func (k Keeper) InitGenesis(ctx sdk.Context, data *authz.GenesisState) { for _, entry := range data.Authorization { + if entry.Expiration.Before(ctx.BlockTime()) { + continue + } + grantee, err := sdk.AccAddressFromBech32(entry.Grantee) if err != nil { panic(err) } + granter, err := sdk.AccAddressFromBech32(entry.Granter) if err != nil { panic(err) } + a, ok := entry.Authorization.GetCachedValue().(authz.Authorization) if !ok { panic("expected authorization") diff --git a/x/authz/keeper/keeper_test.go b/x/authz/keeper/keeper_test.go index 8dfebac95a3..078c6e7bc47 100644 --- a/x/authz/keeper/keeper_test.go +++ b/x/authz/keeper/keeper_test.go @@ -54,13 +54,12 @@ func (s *TestSuite) TestKeeper() { s.Require().Nil(authorization) s.Require().Equal(expiration, time.Time{}) now := s.ctx.BlockHeader().Time - s.Require().NotNil(now) newCoins := sdk.NewCoins(sdk.NewInt64Coin("steak", 100)) s.T().Log("verify if expired authorization is rejected") x := &banktypes.SendAuthorization{SpendLimit: newCoins} err := app.AuthzKeeper.SaveGrant(ctx, granterAddr, granteeAddr, x, now.Add(-1*time.Hour)) - s.Require().NoError(err) + s.Require().Error(err) authorization, _ = app.AuthzKeeper.GetCleanAuthorization(ctx, granteeAddr, granterAddr, bankSendAuthMsgType) s.Require().Nil(authorization) @@ -104,14 +103,13 @@ func (s *TestSuite) TestKeeperIter() { authorization, expiration := app.AuthzKeeper.GetCleanAuthorization(ctx, granteeAddr, granterAddr, "Abcd") s.Require().Nil(authorization) s.Require().Equal(time.Time{}, expiration) - now := s.ctx.BlockHeader().Time - s.Require().NotNil(now) + now := s.ctx.BlockHeader().Time.Add(time.Second) newCoins := sdk.NewCoins(sdk.NewInt64Coin("steak", 100)) s.T().Log("verify if expired authorization is rejected") x := &banktypes.SendAuthorization{SpendLimit: newCoins} err := app.AuthzKeeper.SaveGrant(ctx, granteeAddr, granterAddr, x, now.Add(-1*time.Hour)) - s.Require().NoError(err) + s.Require().Error(err) authorization, _ = app.AuthzKeeper.GetCleanAuthorization(ctx, granteeAddr, granterAddr, "abcd") s.Require().Nil(authorization) @@ -130,8 +128,7 @@ func (s *TestSuite) TestKeeperFees() { granteeAddr := addrs[1] recipientAddr := addrs[2] s.Require().NoError(simapp.FundAccount(app.BankKeeper, s.ctx, granterAddr, sdk.NewCoins(sdk.NewInt64Coin("steak", 10000)))) - now := s.ctx.BlockHeader().Time - s.Require().NotNil(now) + expiration := s.ctx.BlockHeader().Time.Add(1 * time.Second) smallCoin := sdk.NewCoins(sdk.NewInt64Coin("steak", 20)) someCoin := sdk.NewCoins(sdk.NewInt64Coin("steak", 123)) @@ -156,7 +153,7 @@ func (s *TestSuite) TestKeeperFees() { s.T().Log("verify dispatch executes with correct information") // grant authorization - err = app.AuthzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, &banktypes.SendAuthorization{SpendLimit: smallCoin}, now) + err = app.AuthzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, &banktypes.SendAuthorization{SpendLimit: smallCoin}, expiration) s.Require().NoError(err) authorization, _ := app.AuthzKeeper.GetCleanAuthorization(s.ctx, granteeAddr, granterAddr, bankSendAuthMsgType) s.Require().NotNil(authorization) @@ -205,8 +202,7 @@ func (s *TestSuite) TestDispatchedEvents() { granteeAddr := addrs[1] recipientAddr := addrs[2] require.NoError(simapp.FundAccount(app.BankKeeper, s.ctx, granterAddr, sdk.NewCoins(sdk.NewInt64Coin("steak", 10000)))) - now := s.ctx.BlockHeader().Time - require.NotNil(now) + expiration := s.ctx.BlockHeader().Time.Add(1 * time.Second) smallCoin := sdk.NewCoins(sdk.NewInt64Coin("steak", 20)) msgs := authz.NewMsgExec(granteeAddr, []sdk.Msg{ @@ -218,7 +214,7 @@ func (s *TestSuite) TestDispatchedEvents() { }) // grant authorization - err := app.AuthzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, &banktypes.SendAuthorization{SpendLimit: smallCoin}, now) + err := app.AuthzKeeper.SaveGrant(s.ctx, granteeAddr, granterAddr, &banktypes.SendAuthorization{SpendLimit: smallCoin}, expiration) require.NoError(err) authorization, _ := app.AuthzKeeper.GetCleanAuthorization(s.ctx, granteeAddr, granterAddr, bankSendAuthMsgType) require.NotNil(authorization) diff --git a/x/authz/module/module.go b/x/authz/module/module.go index 211b814aa6d..1be2ff4f503 100644 --- a/x/authz/module/module.go +++ b/x/authz/module/module.go @@ -47,7 +47,9 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { } // RegisterLegacyAminoCodec registers the authz module's types for the given codec. -func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {} +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + authz.RegisterLegacyAminoCodec(cdc) +} // RegisterInterfaces registers the authz module's interface types func (AppModuleBasic) RegisterInterfaces(registry cdctypes.InterfaceRegistry) { diff --git a/x/authz/msgs.go b/x/authz/msgs.go index e8ecde0553b..de47c2bc29f 100644 --- a/x/authz/msgs.go +++ b/x/authz/msgs.go @@ -1,11 +1,11 @@ package authz import ( + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" "time" "github.com/gogo/protobuf/proto" - "github.com/cosmos/cosmos-sdk/codec/legacy" cdctypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -79,7 +79,7 @@ func (msg MsgGrant) Route() string { // GetSignBytes implements the LegacyMsg.GetSignBytes method. func (msg MsgGrant) GetSignBytes() []byte { - return sdk.MustSortJSON(legacy.Cdc.MustMarshalJSON(&msg)) + return sdk.MustSortJSON(authzcodec.ModuleCdc.MustMarshalJSON(&msg)) } // GetAuthorization returns the cache value from the MsgGrant.Authorization if present. @@ -172,7 +172,7 @@ func (msg MsgRevoke) Route() string { // GetSignBytes implements the LegacyMsg.GetSignBytes method. func (msg MsgRevoke) GetSignBytes() []byte { - return sdk.MustSortJSON(legacy.Cdc.MustMarshalJSON(&msg)) + return sdk.MustSortJSON(authzcodec.ModuleCdc.MustMarshalJSON(&msg)) } // NewMsgExec creates a new MsgExecAuthorized @@ -243,5 +243,5 @@ func (msg MsgExec) Route() string { // GetSignBytes implements the LegacyMsg.GetSignBytes method. func (msg MsgExec) GetSignBytes() []byte { - return sdk.MustSortJSON(legacy.Cdc.MustMarshalJSON(&msg)) + return sdk.MustSortJSON(authzcodec.ModuleCdc.MustMarshalJSON(&msg)) } diff --git a/x/authz/msgs_test.go b/x/authz/msgs_test.go index c7b4192d378..1e0698628b2 100644 --- a/x/authz/msgs_test.go +++ b/x/authz/msgs_test.go @@ -4,10 +4,13 @@ import ( "testing" "time" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/require" cdctypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" "github.com/cosmos/cosmos-sdk/x/authz" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) @@ -80,7 +83,7 @@ func TestMsgGrantAuthorization(t *testing.T) { {"nil granter and grantee address", nil, nil, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now(), false, false}, {"nil authorization", granter, grantee, nil, time.Now(), true, false}, {"valid test case", granter, grantee, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now().AddDate(0, 1, 0), false, true}, - {"past time", granter, grantee, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now().AddDate(0, 0, -1), false, true}, // TODO need 0.45 + {"past time", granter, grantee, &banktypes.SendAuthorization{SpendLimit: coinsPos}, time.Now().AddDate(0, 0, -1), true, true}, } for i, tc := range tests { msg, err := authz.NewMsgGrant( @@ -115,3 +118,65 @@ func TestMsgGrantGetAuthorization(t *testing.T) { m.SetAuthorization(&g) require.Equal(m.GetAuthorization(), &g) } + +func TestAminoJSON(t *testing.T) { + tx := legacytx.StdTx{} + var msg legacytx.LegacyMsg + someDate := time.Date(1, 1, 1, 1, 1, 1, 1, time.UTC) + msgSend := banktypes.MsgSend{FromAddress: "cosmos1ghi", ToAddress: "cosmos1jkl"} + typeURL := sdk.MsgTypeURL(&msgSend) + msgSendAny, err := cdctypes.NewAnyWithValue(&msgSend) + require.NoError(t, err) + grant, err := authz.NewGrant(someDate, authz.NewGenericAuthorization(typeURL), someDate.Add(time.Hour)) + require.NoError(t, err) + sendGrant, err := authz.NewGrant(someDate, banktypes.NewSendAuthorization(sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1000)))), someDate.Add(time.Hour)) + require.NoError(t, err) + valAddr, err := sdk.ValAddressFromBech32("cosmosvaloper1xcy3els9ua75kdm783c3qu0rfa2eples6eavqq") + require.NoError(t, err) + stakingAuth, err := stakingtypes.NewStakeAuthorization([]sdk.ValAddress{valAddr}, nil, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_DELEGATE, &sdk.Coin{Denom: "stake", Amount: sdk.NewInt(1000)}) + require.NoError(t, err) + delegateGrant, err := authz.NewGrant(someDate, stakingAuth, someDate.Add(time.Hour)) + require.NoError(t, err) + + // Amino JSON encoding has changed in authz since v0.46. + // Before, it was outputting something like: + // `{"account_number":"1","chain_id":"foo","fee":{"amount":[],"gas":"0"},"memo":"memo","msgs":[{"grant":{"authorization":{"msg":"/cosmos.bank.v1beta1.MsgSend"},"expiration":"0001-01-01T02:01:01.000000001Z"},"grantee":"cosmos1def","granter":"cosmos1abc"}],"sequence":"1","timeout_height":"1"}` + // + // This was a bug. Now, it's as below, See how there's `type` & `value` fields. + // ref: https://github.com/cosmos/cosmos-sdk/issues/11190 + // ref: https://github.com/cosmos/cosmjs/issues/1026 + msg = &authz.MsgGrant{Granter: "cosmos1abc", Grantee: "cosmos1def", Grant: grant} + tx.Msgs = []sdk.Msg{msg} + require.Equal(t, + `{"account_number":"1","chain_id":"foo","fee":{"amount":[],"gas":"0"},"memo":"memo","msgs":[{"type":"cosmos-sdk/MsgGrant","value":{"grant":{"authorization":{"type":"cosmos-sdk/GenericAuthorization","value":{"msg":"/cosmos.bank.v1beta1.MsgSend"}},"expiration":"0001-01-01T02:01:01.000000001Z"},"grantee":"cosmos1def","granter":"cosmos1abc"}}],"sequence":"1","timeout_height":"1"}`, + string(legacytx.StdSignBytes("foo", 1, 1, 1, legacytx.StdFee{}, []sdk.Msg{msg}, "memo")), + ) + + msg = &authz.MsgGrant{Granter: "cosmos1abc", Grantee: "cosmos1def", Grant: sendGrant} + tx.Msgs = []sdk.Msg{msg} + require.Equal(t, + `{"account_number":"1","chain_id":"foo","fee":{"amount":[],"gas":"0"},"memo":"memo","msgs":[{"type":"cosmos-sdk/MsgGrant","value":{"grant":{"authorization":{"type":"cosmos-sdk/SendAuthorization","value":{"spend_limit":[{"amount":"1000","denom":"stake"}]}},"expiration":"0001-01-01T02:01:01.000000001Z"},"grantee":"cosmos1def","granter":"cosmos1abc"}}],"sequence":"1","timeout_height":"1"}`, + string(legacytx.StdSignBytes("foo", 1, 1, 1, legacytx.StdFee{}, []sdk.Msg{msg}, "memo")), + ) + + msg = &authz.MsgGrant{Granter: "cosmos1abc", Grantee: "cosmos1def", Grant: delegateGrant} + tx.Msgs = []sdk.Msg{msg} + require.Equal(t, + `{"account_number":"1","chain_id":"foo","fee":{"amount":[],"gas":"0"},"memo":"memo","msgs":[{"type":"cosmos-sdk/MsgGrant","value":{"grant":{"authorization":{"type":"cosmos-sdk/StakeAuthorization","value":{"Validators":{"type":"cosmos-sdk/StakeAuthorization/AllowList","value":{"allow_list":{"address":["cosmosvaloper1xcy3els9ua75kdm783c3qu0rfa2eples6eavqq"]}}},"authorization_type":1,"max_tokens":{"amount":"1000","denom":"stake"}}},"expiration":"0001-01-01T02:01:01.000000001Z"},"grantee":"cosmos1def","granter":"cosmos1abc"}}],"sequence":"1","timeout_height":"1"}`, + string(legacytx.StdSignBytes("foo", 1, 1, 1, legacytx.StdFee{}, []sdk.Msg{msg}, "memo")), + ) + + msg = &authz.MsgRevoke{Granter: "cosmos1abc", Grantee: "cosmos1def", MsgTypeUrl: typeURL} + tx.Msgs = []sdk.Msg{msg} + require.Equal(t, + `{"account_number":"1","chain_id":"foo","fee":{"amount":[],"gas":"0"},"memo":"memo","msgs":[{"type":"cosmos-sdk/MsgRevoke","value":{"grantee":"cosmos1def","granter":"cosmos1abc","msg_type_url":"/cosmos.bank.v1beta1.MsgSend"}}],"sequence":"1","timeout_height":"1"}`, + string(legacytx.StdSignBytes("foo", 1, 1, 1, legacytx.StdFee{}, []sdk.Msg{msg}, "memo")), + ) + + msg = &authz.MsgExec{Grantee: "cosmos1def", Msgs: []*cdctypes.Any{msgSendAny}} + tx.Msgs = []sdk.Msg{msg} + require.Equal(t, + `{"account_number":"1","chain_id":"foo","fee":{"amount":[],"gas":"0"},"memo":"memo","msgs":[{"type":"cosmos-sdk/MsgExec","value":{"grantee":"cosmos1def","msgs":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[],"from_address":"cosmos1ghi","to_address":"cosmos1jkl"}}]}}],"sequence":"1","timeout_height":"1"}`, + string(legacytx.StdSignBytes("foo", 1, 1, 1, legacytx.StdFee{}, []sdk.Msg{msg}, "memo")), + ) +} diff --git a/x/authz/simulation/decoder_test.go b/x/authz/simulation/decoder_test.go index cfda286a837..de50d756a76 100644 --- a/x/authz/simulation/decoder_test.go +++ b/x/authz/simulation/decoder_test.go @@ -20,7 +20,8 @@ func TestDecodeStore(t *testing.T) { cdc := simapp.MakeTestEncodingConfig().Marshaler dec := simulation.NewDecodeStore(cdc) - grant, _ := authz.NewGrant(banktypes.NewSendAuthorization(sdk.NewCoins(sdk.NewInt64Coin("foo", 123))), time.Now().UTC()) + now := time.Now().UTC() + grant, _ := authz.NewGrant(now, banktypes.NewSendAuthorization(sdk.NewCoins(sdk.NewInt64Coin("foo", 123))), now.Add(1)) grantBz, err := cdc.Marshal(&grant) require.NoError(t, err) kvPairs := kv.Pairs{ diff --git a/x/authz/simulation/genesis.go b/x/authz/simulation/genesis.go index b95207be975..d6d0e2e8626 100644 --- a/x/authz/simulation/genesis.go +++ b/x/authz/simulation/genesis.go @@ -2,6 +2,7 @@ package simulation import ( "math/rand" + "time" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -13,7 +14,7 @@ import ( ) // genGrant returns a slice of authorization grants. -func genGrant(r *rand.Rand, accounts []simtypes.Account) []authz.GrantAuthorization { +func genGrant(r *rand.Rand, accounts []simtypes.Account, genT time.Time) []authz.GrantAuthorization { authorizations := make([]authz.GrantAuthorization, len(accounts)-1) for i := 0; i < len(accounts)-1; i++ { granter := accounts[i] @@ -22,6 +23,7 @@ func genGrant(r *rand.Rand, accounts []simtypes.Account) []authz.GrantAuthorizat Granter: granter.Address.String(), Grantee: grantee.Address.String(), Authorization: generateRandomGrant(r), + Expiration: genT.AddDate(1, 0, 0), } } @@ -50,7 +52,7 @@ func RandomizedGenState(simState *module.SimulationState) { var grants []authz.GrantAuthorization simState.AppParams.GetOrGenerate( simState.Cdc, "authz", &grants, simState.Rand, - func(r *rand.Rand) { grants = genGrant(r, simState.Accounts) }, + func(r *rand.Rand) { grants = genGrant(r, simState.Accounts, simState.GenTimestamp) }, ) authzGrantsGenesis := authz.NewGenesisState(grants) diff --git a/x/authz/simulation/operations.go b/x/authz/simulation/operations.go index 6c859bd3483..da13ff88881 100644 --- a/x/authz/simulation/operations.go +++ b/x/authz/simulation/operations.go @@ -1,11 +1,8 @@ package simulation import ( - "fmt" "math/rand" - "github.com/gogo/protobuf/proto" - "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" cdctypes "github.com/cosmos/cosmos-sdk/codec/types" @@ -49,8 +46,8 @@ func WeightedOperations( var ( weightMsgGrant int - weightRevoke int weightExec int + weightRevoke int ) appParams.GetOrGenerate(cdc, OpWeightMsgGrant, &weightMsgGrant, nil, @@ -59,15 +56,15 @@ func WeightedOperations( }, ) - appParams.GetOrGenerate(cdc, OpWeightRevoke, &weightRevoke, nil, + appParams.GetOrGenerate(cdc, OpWeightExec, &weightExec, nil, func(_ *rand.Rand) { - weightRevoke = WeightRevoke + weightExec = WeightExec }, ) - appParams.GetOrGenerate(cdc, OpWeightExec, &weightExec, nil, + appParams.GetOrGenerate(cdc, OpWeightRevoke, &weightRevoke, nil, func(_ *rand.Rand) { - weightExec = WeightExec + weightRevoke = WeightRevoke }, ) @@ -76,14 +73,14 @@ func WeightedOperations( weightMsgGrant, SimulateMsgGrant(ak, bk, k), ), - simulation.NewWeightedOperation( - weightRevoke, - SimulateMsgRevoke(ak, bk, k), - ), simulation.NewWeightedOperation( weightExec, SimulateMsgExec(ak, bk, k, appCdc), ), + simulation.NewWeightedOperation( + weightRevoke, + SimulateMsgRevoke(ak, bk, k), + ), } } @@ -238,43 +235,46 @@ func SimulateMsgExec(ak authz.AccountKeeper, bk authz.BankKeeper, k keeper.Keepe return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "Account not found"), nil, sdkerrors.Wrapf(sdkerrors.ErrNotFound, "granter account not found") } - if targetGrant.Expiration.Before(ctx.BlockHeader().Time) { - return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "grant expired"), nil, nil - } - - coins := sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(int64(simtypes.RandIntBetween(r, 100, 1000000))))) + granterspendableCoins := bk.SpendableCoins(ctx, granterAddr) + coins := simtypes.RandSubsetCoins(r, granterspendableCoins) // Check send_enabled status of each sent coin denom if err := bk.IsSendEnabledCoins(ctx, coins...); err != nil { return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, nil } - if targetGrant.Authorization.TypeUrl == fmt.Sprintf("/%s", proto.MessageName(&banktype.SendAuthorization{})) { - sendAuthorization := targetGrant.GetAuthorization().(*banktype.SendAuthorization) - if sendAuthorization.SpendLimit.IsAllLT(coins) { - return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "over spend limit"), nil, nil - } + msg := []sdk.Msg{banktype.NewMsgSend(granterAddr, granteeAddr, coins)} + sendAuth, ok := targetGrant.GetAuthorization().(*banktype.SendAuthorization) + if !ok { + return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "not a send authorization"), nil, nil } - granterspendableCoins := bk.SpendableCoins(ctx, granterAddr) - if granterspendableCoins.IsAllLTE(coins) { - return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "insufficient funds"), nil, nil + if sendAuth.SpendLimit.IsAllLTE(coins) { + return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "over spend limit"), nil, nil } + res, err := sendAuth.Accept(ctx, msg[0]) + if err != nil { + return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err + } + + if !res.Accept { + return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "expired or invalid grant"), nil, nil + } + + msgExec := authz.NewMsgExec(granteeAddr, msg) granteeSpendableCoins := bk.SpendableCoins(ctx, granteeAddr) feeCoins := granteeSpendableCoins.FilterDenoms([]string{sdk.DefaultBondDenom}) fees, err := simtypes.RandomFees(r, ctx, feeCoins) if err != nil { return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "fee error"), nil, err } - - msg := authz.NewMsgExec(granteeAddr, []sdk.Msg{banktype.NewMsgSend(granterAddr, granteeAddr, coins)}) txCfg := simappparams.MakeTestEncodingConfig().TxConfig granteeAcc := ak.GetAccount(ctx, granteeAddr) tx, err := helpers.GenTx( txCfg, - []sdk.Msg{&msg}, + []sdk.Msg{&msgExec}, fees, helpers.DefaultGenTxGas, chainID, @@ -291,10 +291,10 @@ func SimulateMsgExec(ak authz.AccountKeeper, bk authz.BankKeeper, k keeper.Keepe return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err } - err = msg.UnpackInterfaces(cdc) + err = msgExec.UnpackInterfaces(cdc) if err != nil { return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "unmarshal error"), nil, err } - return simtypes.NewOperationMsg(&msg, true, "success", nil), nil, nil + return simtypes.NewOperationMsg(&msgExec, true, "success", nil), nil, nil } } diff --git a/x/authz/simulation/operations_test.go b/x/authz/simulation/operations_test.go index bdb7f98f82d..67d603cd28a 100644 --- a/x/authz/simulation/operations_test.go +++ b/x/authz/simulation/operations_test.go @@ -43,7 +43,7 @@ func (suite *SimTestSuite) TestWeightedOperations() { // setup 3 accounts s := rand.NewSource(1) r := rand.New(s) - accs := suite.getTestingAccounts(r, 3) + accs := suite.getTestingAccounts(r, 2) expected := []struct { weight int @@ -51,8 +51,8 @@ func (suite *SimTestSuite) TestWeightedOperations() { opMsgName string }{ {simulation.WeightGrant, authz.ModuleName, simulation.TypeMsgGrant}, - {simulation.WeightRevoke, authz.ModuleName, simulation.TypeMsgRevoke}, {simulation.WeightExec, authz.ModuleName, simulation.TypeMsgExec}, + {simulation.WeightRevoke, authz.ModuleName, simulation.TypeMsgRevoke}, } for i, w := range weightedOps { @@ -106,7 +106,7 @@ func (suite *SimTestSuite) TestSimulateGrant() { suite.Require().NoError(err) var msg authz.MsgGrant - suite.app.AppCodec().UnmarshalJSON(operationMsg.Msg, &msg) + suite.app.LegacyAmino().UnmarshalJSON(operationMsg.Msg, &msg) suite.Require().True(operationMsg.OK) suite.Require().Equal(granter.Address.String(), msg.Granter) suite.Require().Equal(grantee.Address.String(), msg.Grantee) @@ -143,7 +143,7 @@ func (suite *SimTestSuite) TestSimulateRevoke() { suite.Require().NoError(err) var msg authz.MsgRevoke - suite.app.AppCodec().UnmarshalJSON(operationMsg.Msg, &msg) + suite.app.LegacyAmino().UnmarshalJSON(operationMsg.Msg, &msg) suite.Require().True(operationMsg.OK) suite.Require().Equal(granter.Address.String(), msg.Granter) @@ -179,7 +179,7 @@ func (suite *SimTestSuite) TestSimulateExec() { var msg authz.MsgExec - suite.app.AppCodec().UnmarshalJSON(operationMsg.Msg, &msg) + suite.app.LegacyAmino().UnmarshalJSON(operationMsg.Msg, &msg) suite.Require().True(operationMsg.OK) suite.Require().Equal(grantee.Address.String(), msg.Grantee) diff --git a/x/bank/types/codec.go b/x/bank/types/codec.go index 07859ceab60..a29b4864351 100644 --- a/x/bank/types/codec.go +++ b/x/bank/types/codec.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/x/authz" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" ) // RegisterLegacyAminoCodec registers the necessary x/bank interfaces and concrete types @@ -14,6 +15,7 @@ import ( func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgSend{}, "cosmos-sdk/MsgSend", nil) cdc.RegisterConcrete(&MsgMultiSend{}, "cosmos-sdk/MsgMultiSend", nil) + cdc.RegisterConcrete(&SendAuthorization{}, "cosmos-sdk/SendAuthorization", nil) } func RegisterInterfaces(registry types.InterfaceRegistry) { @@ -30,19 +32,16 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { } var ( - amino = codec.NewLegacyAmino() - - // ModuleCdc references the global x/bank module codec. Note, the codec should - // ONLY be used in certain instances of tests and for JSON encoding as Amino is - // still used for that purpose. - // - // The actual codec used for serialization should be provided to x/staking and - // defined at the application level. + amino = codec.NewLegacyAmino() ModuleCdc = codec.NewAminoCodec(amino) ) func init() { RegisterLegacyAminoCodec(amino) cryptocodec.RegisterCrypto(amino) - amino.Seal() + sdk.RegisterLegacyAminoCodec(amino) + + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + RegisterLegacyAminoCodec(authzcodec.Amino) } diff --git a/x/crisis/types/codec.go b/x/crisis/types/codec.go index ac02c54630c..abf1c2c1a6d 100644 --- a/x/crisis/types/codec.go +++ b/x/crisis/types/codec.go @@ -6,6 +6,7 @@ import ( cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" ) // RegisterLegacyAminoCodec registers the necessary x/crisis interfaces and concrete types @@ -23,19 +24,16 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { } var ( - amino = codec.NewLegacyAmino() - - // ModuleCdc references the global x/crisis module codec. Note, the codec should - // ONLY be used in certain instances of tests and for JSON encoding as Amino is - // still used for that purpose. - // - // The actual codec used for serialization should be provided to x/crisis and - // defined at the application level. + amino = codec.NewLegacyAmino() ModuleCdc = codec.NewAminoCodec(amino) ) func init() { RegisterLegacyAminoCodec(amino) cryptocodec.RegisterCrypto(amino) - amino.Seal() + sdk.RegisterLegacyAminoCodec(amino) + + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + RegisterLegacyAminoCodec(authzcodec.Amino) } diff --git a/x/distribution/simulation/operations_test.go b/x/distribution/simulation/operations_test.go index 33b59a53cca..f3cf3f0a405 100644 --- a/x/distribution/simulation/operations_test.go +++ b/x/distribution/simulation/operations_test.go @@ -166,16 +166,20 @@ func (suite *SimTestSuite) testSimulateMsgWithdrawValidatorCommission(tokenName // execute operation op := simulation.SimulateMsgWithdrawValidatorCommission(suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.DistrKeeper, suite.app.StakingKeeper) operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") - suite.Require().NoError(err) - - var msg types.MsgWithdrawValidatorCommission - types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) - - suite.Require().True(operationMsg.OK) - suite.Require().Equal("cosmosvaloper1tnh2q55v8wyygtt9srz5safamzdengsn9dsd7z", msg.ValidatorAddress) - suite.Require().Equal(types.TypeMsgWithdrawValidatorCommission, msg.Type()) - suite.Require().Equal(types.ModuleName, msg.Route()) - suite.Require().Len(futureOperations, 0) + if !operationMsg.OK { + suite.Require().Equal("could not find account", operationMsg.Comment) + } else { + suite.Require().NoError(err) + + var msg types.MsgWithdrawValidatorCommission + types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + + suite.Require().True(operationMsg.OK) + suite.Require().Equal("cosmosvaloper1tnh2q55v8wyygtt9srz5safamzdengsn9dsd7z", msg.ValidatorAddress) + suite.Require().Equal(types.TypeMsgWithdrawValidatorCommission, msg.Type()) + suite.Require().Equal(types.ModuleName, msg.Route()) + suite.Require().Len(futureOperations, 0) + } } // TestSimulateMsgFundCommunityPool tests the normal scenario of a valid message of type TypeMsgFundCommunityPool. diff --git a/x/distribution/types/codec.go b/x/distribution/types/codec.go index 73aca45060e..22bf22ce1c5 100644 --- a/x/distribution/types/codec.go +++ b/x/distribution/types/codec.go @@ -6,6 +6,7 @@ import ( cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" ) @@ -36,19 +37,16 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { } var ( - amino = codec.NewLegacyAmino() - - // ModuleCdc references the global x/distribution module codec. Note, the codec - // should ONLY be used in certain instances of tests and for JSON encoding as Amino - // is still used for that purpose. - // - // The actual codec used for serialization should be provided to x/distribution and - // defined at the application level. + amino = codec.NewLegacyAmino() ModuleCdc = codec.NewAminoCodec(amino) ) func init() { RegisterLegacyAminoCodec(amino) cryptocodec.RegisterCrypto(amino) - amino.Seal() + sdk.RegisterLegacyAminoCodec(amino) + + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + RegisterLegacyAminoCodec(authzcodec.Amino) } diff --git a/x/evidence/types/codec.go b/x/evidence/types/codec.go index 9de2743e38e..2dea1c40206 100644 --- a/x/evidence/types/codec.go +++ b/x/evidence/types/codec.go @@ -6,6 +6,7 @@ import ( cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" "github.com/cosmos/cosmos-sdk/x/evidence/exported" ) @@ -29,19 +30,16 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { } var ( - amino = codec.NewLegacyAmino() - - // ModuleCdc references the global x/evidence module codec. Note, the codec should - // ONLY be used in certain instances of tests and for JSON encoding as Amino is - // still used for that purpose. - // - // The actual codec used for serialization should be provided to x/evidence and - // defined at the application level. + amino = codec.NewLegacyAmino() ModuleCdc = codec.NewAminoCodec(amino) ) func init() { RegisterLegacyAminoCodec(amino) cryptocodec.RegisterCrypto(amino) - amino.Seal() + sdk.RegisterLegacyAminoCodec(amino) + + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + RegisterLegacyAminoCodec(authzcodec.Amino) } diff --git a/x/evidence/types/msgs.go b/x/evidence/types/msgs.go index cd2e2ba0321..f1a57ca76f1 100644 --- a/x/evidence/types/msgs.go +++ b/x/evidence/types/msgs.go @@ -2,7 +2,6 @@ package types import ( "fmt" - "github.com/gogo/protobuf/proto" "github.com/cosmos/cosmos-sdk/codec/types" diff --git a/x/feegrant/codec.go b/x/feegrant/codec.go index 7e307f42197..1b6669673d0 100644 --- a/x/feegrant/codec.go +++ b/x/feegrant/codec.go @@ -1,11 +1,26 @@ package feegrant import ( + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" ) +// RegisterLegacyAminoCodec registers the necessary x/feegrant interfaces and concrete types +// on the provided LegacyAmino codec. These types are used for Amino JSON serialization. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgGrantAllowance{}, "cosmos-sdk/MsgGrantAllowance", nil) + cdc.RegisterConcrete(&MsgRevokeAllowance{}, "cosmos-sdk/MsgRevokeAllowance", nil) + + cdc.RegisterInterface((*FeeAllowanceI)(nil), nil) + cdc.RegisterConcrete(&BasicAllowance{}, "cosmos-sdk/BasicAllowance", nil) + cdc.RegisterConcrete(&PeriodicAllowance{}, "cosmos-sdk/PeriodicAllowance", nil) + cdc.RegisterConcrete(&AllowedMsgAllowance{}, "cosmos-sdk/AllowedMsgAllowance", nil) +} + // RegisterInterfaces registers the interfaces types with the interface registry func RegisterInterfaces(registry types.InterfaceRegistry) { registry.RegisterImplementations((*sdk.Msg)(nil), @@ -23,3 +38,25 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) } + +var ( + amino = codec.NewLegacyAmino() + + // ModuleCdc references the global x/feegrant module codec. Note, the codec should + // ONLY be used in certain instances of tests and for JSON encoding as Amino is + // still used for that purpose. + // + // The actual codec used for serialization should be provided to x/feegrant and + // defined at the application level. + ModuleCdc = codec.NewAminoCodec(amino) +) + +func init() { + RegisterLegacyAminoCodec(amino) + cryptocodec.RegisterCrypto(amino) + sdk.RegisterLegacyAminoCodec(amino) + + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + RegisterLegacyAminoCodec(authzcodec.Amino) +} diff --git a/x/feegrant/module/module.go b/x/feegrant/module/module.go index c9a7555506a..1ef9648c293 100644 --- a/x/feegrant/module/module.go +++ b/x/feegrant/module/module.go @@ -53,6 +53,7 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { // RegisterLegacyAminoCodec registers the feegrant module's types for the given codec. func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + feegrant.RegisterLegacyAminoCodec(cdc) } // RegisterInterfaces registers the feegrant module's interface types diff --git a/x/feegrant/msgs.go b/x/feegrant/msgs.go index 63c29eb4eae..3be830664bc 100644 --- a/x/feegrant/msgs.go +++ b/x/feegrant/msgs.go @@ -3,7 +3,6 @@ package feegrant import ( "github.com/gogo/protobuf/proto" - "github.com/cosmos/cosmos-sdk/codec/legacy" "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -77,7 +76,7 @@ func (msg MsgGrantAllowance) Route() string { // GetSignBytes implements the LegacyMsg.GetSignBytes method. func (msg MsgGrantAllowance) GetSignBytes() []byte { - return sdk.MustSortJSON(legacy.Cdc.MustMarshalJSON(&msg)) + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) } // GetFeeAllowanceI returns unpacked FeeAllowance @@ -140,5 +139,5 @@ func (msg MsgRevokeAllowance) Route() string { // GetSignBytes implements the LegacyMsg.GetSignBytes method. func (msg MsgRevokeAllowance) GetSignBytes() []byte { - return sdk.MustSortJSON(legacy.Cdc.MustMarshalJSON(&msg)) + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) } diff --git a/x/feegrant/msgs_test.go b/x/feegrant/msgs_test.go index c5eceb3b6bd..fc3e28185c7 100644 --- a/x/feegrant/msgs_test.go +++ b/x/feegrant/msgs_test.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx" "github.com/cosmos/cosmos-sdk/x/feegrant" ) @@ -132,3 +133,31 @@ func TestMsgRevokeAllowance(t *testing.T) { } } } + +func TestAminoJSON(t *testing.T) { + tx := legacytx.StdTx{} + var msg legacytx.LegacyMsg + allowanceAny, err := codectypes.NewAnyWithValue(&feegrant.BasicAllowance{SpendLimit: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(100)))}) + require.NoError(t, err) + + // Amino JSON encoding has changed in feegrant since v0.46. + // Before, it was outputting something like: + // `{"account_number":"1","chain_id":"foo","fee":{"amount":[],"gas":"0"},"memo":"memo","msgs":[{"allowance":{"spend_limit":[{"amount":"100","denom":"foo"}]},"grantee":"cosmos1def","granter":"cosmos1abc"}],"sequence":"1","timeout_height":"1"}` + // + // This was a bug. Now, it's as below, See how there's `type` & `value` fields. + // ref: https://github.com/cosmos/cosmos-sdk/issues/11190 + // ref: https://github.com/cosmos/cosmjs/issues/1026 + msg = &feegrant.MsgGrantAllowance{Granter: "cosmos1abc", Grantee: "cosmos1def", Allowance: allowanceAny} + tx.Msgs = []sdk.Msg{msg} + require.Equal(t, + `{"account_number":"1","chain_id":"foo","fee":{"amount":[],"gas":"0"},"memo":"memo","msgs":[{"type":"cosmos-sdk/MsgGrantAllowance","value":{"allowance":{"type":"cosmos-sdk/BasicAllowance","value":{"spend_limit":[{"amount":"100","denom":"foo"}]}},"grantee":"cosmos1def","granter":"cosmos1abc"}}],"sequence":"1","timeout_height":"1"}`, + string(legacytx.StdSignBytes("foo", 1, 1, 1, legacytx.StdFee{}, []sdk.Msg{msg}, "memo")), + ) + + msg = &feegrant.MsgRevokeAllowance{Granter: "cosmos1abc", Grantee: "cosmos1def"} + tx.Msgs = []sdk.Msg{msg} + require.Equal(t, + `{"account_number":"1","chain_id":"foo","fee":{"amount":[],"gas":"0"},"memo":"memo","msgs":[{"type":"cosmos-sdk/MsgRevokeAllowance","value":{"grantee":"cosmos1def","granter":"cosmos1abc"}}],"sequence":"1","timeout_height":"1"}`, + string(legacytx.StdSignBytes("foo", 1, 1, 1, legacytx.StdFee{}, []sdk.Msg{msg}, "memo")), + ) +} diff --git a/x/feegrant/simulation/operations_test.go b/x/feegrant/simulation/operations_test.go index 4d7671f7728..542e47842fd 100644 --- a/x/feegrant/simulation/operations_test.go +++ b/x/feegrant/simulation/operations_test.go @@ -113,7 +113,7 @@ func (suite *SimTestSuite) TestSimulateMsgGrantAllowance() { require.NoError(err) var msg feegrant.MsgGrantAllowance - suite.app.AppCodec().UnmarshalJSON(operationMsg.Msg, &msg) + suite.app.LegacyAmino().UnmarshalJSON(operationMsg.Msg, &msg) require.True(operationMsg.OK) require.Equal(accounts[2].Address.String(), msg.Granter) @@ -155,7 +155,7 @@ func (suite *SimTestSuite) TestSimulateMsgRevokeAllowance() { require.NoError(err) var msg feegrant.MsgRevokeAllowance - suite.app.AppCodec().UnmarshalJSON(operationMsg.Msg, &msg) + suite.app.LegacyAmino().UnmarshalJSON(operationMsg.Msg, &msg) require.True(operationMsg.OK) require.Equal(granter.Address.String(), msg.Granter) diff --git a/x/gov/simulation/operations_test.go b/x/gov/simulation/operations_test.go index 6a5252dc378..19d7e17c132 100644 --- a/x/gov/simulation/operations_test.go +++ b/x/gov/simulation/operations_test.go @@ -113,7 +113,8 @@ func TestSimulateMsgSubmitProposal(t *testing.T) { require.NoError(t, err) var msg types.MsgSubmitProposal - types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + err = types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + require.NoError(t, err) require.True(t, operationMsg.OK) require.Equal(t, "cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.Proposer) @@ -156,7 +157,8 @@ func TestSimulateMsgDeposit(t *testing.T) { require.NoError(t, err) var msg types.MsgDeposit - types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + err = types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + require.NoError(t, err) require.True(t, operationMsg.OK) require.Equal(t, uint64(1), msg.ProposalId) @@ -198,7 +200,8 @@ func TestSimulateMsgVote(t *testing.T) { require.NoError(t, err) var msg types.MsgVote - types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + err = types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) + require.NoError(t, err) require.True(t, operationMsg.OK) require.Equal(t, uint64(1), msg.ProposalId) diff --git a/x/slashing/types/codec.go b/x/slashing/types/codec.go index bc89862f1b4..dd6cfb34526 100644 --- a/x/slashing/types/codec.go +++ b/x/slashing/types/codec.go @@ -6,6 +6,7 @@ import ( cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" ) // RegisterLegacyAminoCodec registers concrete types on LegacyAmino codec @@ -22,19 +23,16 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { } var ( - amino = codec.NewLegacyAmino() - - // ModuleCdc references the global x/slashing module codec. Note, the codec - // should ONLY be used in certain instances of tests and for JSON encoding as Amino - // is still used for that purpose. - // - // The actual codec used for serialization should be provided to x/slashing and - // defined at the application level. + amino = codec.NewLegacyAmino() ModuleCdc = codec.NewAminoCodec(amino) ) func init() { RegisterLegacyAminoCodec(amino) cryptocodec.RegisterCrypto(amino) - amino.Seal() + sdk.RegisterLegacyAminoCodec(amino) + + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + RegisterLegacyAminoCodec(authzcodec.Amino) } diff --git a/x/staking/types/codec.go b/x/staking/types/codec.go index 485549077bd..4970eea7355 100644 --- a/x/staking/types/codec.go +++ b/x/staking/types/codec.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" "github.com/cosmos/cosmos-sdk/x/authz" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" ) // RegisterLegacyAminoCodec registers the necessary x/staking interfaces and concrete types @@ -17,6 +18,11 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgDelegate{}, "cosmos-sdk/MsgDelegate", nil) cdc.RegisterConcrete(&MsgUndelegate{}, "cosmos-sdk/MsgUndelegate", nil) cdc.RegisterConcrete(&MsgBeginRedelegate{}, "cosmos-sdk/MsgBeginRedelegate", nil) + + cdc.RegisterInterface((*isStakeAuthorization_Validators)(nil), nil) + cdc.RegisterConcrete(&StakeAuthorization_AllowList{}, "cosmos-sdk/StakeAuthorization/AllowList", nil) + cdc.RegisterConcrete(&StakeAuthorization_DenyList{}, "cosmos-sdk/StakeAuthorization/DenyList", nil) + cdc.RegisterConcrete(&StakeAuthorization{}, "cosmos-sdk/StakeAuthorization", nil) } // RegisterInterfaces registers the x/staking interfaces types with the interface registry @@ -37,19 +43,16 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { } var ( - amino = codec.NewLegacyAmino() - - // ModuleCdc references the global x/staking module codec. Note, the codec should - // ONLY be used in certain instances of tests and for JSON encoding as Amino is - // still used for that purpose. - // - // The actual codec used for serialization should be provided to x/staking and - // defined at the application level. + amino = codec.NewLegacyAmino() ModuleCdc = codec.NewAminoCodec(amino) ) func init() { RegisterLegacyAminoCodec(amino) cryptocodec.RegisterCrypto(amino) - amino.Seal() + sdk.RegisterLegacyAminoCodec(amino) + + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + RegisterLegacyAminoCodec(authzcodec.Amino) } diff --git a/x/upgrade/types/codec.go b/x/upgrade/types/codec.go index 59703f57a8f..96fc3848eb6 100644 --- a/x/upgrade/types/codec.go +++ b/x/upgrade/types/codec.go @@ -3,6 +3,9 @@ package types import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" ) @@ -20,3 +23,18 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { &CancelSoftwareUpgradeProposal{}, ) } + +var ( + amino = codec.NewLegacyAmino() + ModuleCdc = codec.NewAminoCodec(amino) +) + +func init() { + RegisterLegacyAminoCodec(amino) + cryptocodec.RegisterCrypto(amino) + sdk.RegisterLegacyAminoCodec(amino) + + // Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be + // used to properly serialize MsgGrant and MsgExec instances + RegisterLegacyAminoCodec(authzcodec.Amino) +}