-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: implement Amino serialization for x/authz and x/feegrant #11224
Changes from 11 commits
78c16d2
d88839f
9a42a24
0c4a4f1
8b2a7dc
d3422c9
bc169a0
e963aac
04a2ff1
c73b2a0
63f5b97
e9c91fc
09d2446
db13bd5
be8bc73
d4b7104
da242c1
bac8689
a7bfed7
76b449e
060fb4c
ef26d33
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,24 @@ | ||
package authz | ||
|
||
import ( | ||
"github.com/cosmos/cosmos-sdk/codec" | ||
"github.com/cosmos/cosmos-sdk/codec/legacy" | ||
types "github.com/cosmos/cosmos-sdk/codec/types" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/types/msgservice" | ||
) | ||
|
||
// 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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We still have a problem here. GenericAuthorization allows any msg TypeURL, and then you can MsgExec with that arbitrary Msg. We would need to register There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know how to solve this problem right now. We can try to think of something before merging. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OMG, and we need to use the right type name... |
||
} | ||
|
||
// 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 global Amino codec so that this can later be | ||
// used to properly serialize MsgGrant and MsgExec instances | ||
RegisterLegacyAminoCodec(legacy.Cdc) | ||
amaury1093 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,15 @@ | ||
package authz_test | ||
|
||
import ( | ||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" | ||
"testing" | ||
"time" | ||
|
||
"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/migrations/legacytx" | ||
"github.com/cosmos/cosmos-sdk/x/authz" | ||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" | ||
) | ||
|
@@ -120,3 +122,65 @@ func TestMsgGrantGetAuthorization(t *testing.T) { | |
require.NoError(err) | ||
require.Equal(a, &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", nil)), | ||
) | ||
|
||
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", nil)), | ||
) | ||
|
||
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"}`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
yeah, I don't think there's a way to have snake_case, unless patching Amino, which I really don't want to do |
||
string(legacytx.StdSignBytes("foo", 1, 1, 1, legacytx.StdFee{}, []sdk.Msg{msg}, "memo", nil)), | ||
) | ||
|
||
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", nil)), | ||
) | ||
|
||
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", nil)), | ||
) | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,11 +1,25 @@ | ||||||||||||||||||||||||||
package feegrant | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||||
"github.com/cosmos/cosmos-sdk/codec" | ||||||||||||||||||||||||||
"github.com/cosmos/cosmos-sdk/codec/legacy" | ||||||||||||||||||||||||||
"github.com/cosmos/cosmos-sdk/codec/types" | ||||||||||||||||||||||||||
sdk "github.com/cosmos/cosmos-sdk/types" | ||||||||||||||||||||||||||
"github.com/cosmos/cosmos-sdk/types/msgservice" | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
// 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 +37,23 @@ 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) | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
Comment on lines
+40
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Is that correct? Come to think about it, we can remove ModuleCdc globals from all modules, right? (we can do in a separate PR, so that this one focused on authz/feegrant) Edit: we can do this in #11232 |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
func init() { | ||||||||||||||||||||||||||
RegisterLegacyAminoCodec(amino) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
// Register all Amino interfaces and concrete types on the global Amino codec so that this can later be | ||||||||||||||||||||||||||
// used to properly serialize x/authz MsgExec instances | ||||||||||||||||||||||||||
RegisterLegacyAminoCodec(legacy.Cdc) | ||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just to make sure the Cdc is initialized before running the various
init
methods. Since the initialization order is the following:init
methods (based on the file structure)It is safer to have the Cdc being initialized as a package variable rather than inside the
init
method.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initialization order is not necessarily defined in this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it? I found this order inside the Go specs (here)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right that vars are evaluated before init funcs, my mistake there. But this is very subtle behavior to depend on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This honestly wasn't necessary IMO. I actually think keeping it in the
init
is cleaner as that's also where we do the initialization.