From 9038dfe3b6e782ea7669fa03c7b9d5e846fd6acc Mon Sep 17 00:00:00 2001 From: Robert Zaremba Date: Mon, 10 May 2021 11:28:08 +0200 Subject: [PATCH 1/5] authz: Update MsgGrant proto (#9280) * adding GetAuthorization test * update MsgGrant proto * update comment --- docs/core/proto-docs.md | 3 +- proto/cosmos/authz/v1beta1/tx.proto | 4 +- x/authz/authorization_grant.go | 41 ++++--- x/authz/keeper/msg_server.go | 2 +- x/authz/msgs.go | 29 ++--- x/authz/msgs_test.go | 18 ++++ x/authz/tx.pb.go | 161 +++++++++------------------- 7 files changed, 110 insertions(+), 148 deletions(-) diff --git a/docs/core/proto-docs.md b/docs/core/proto-docs.md index d8b85efe7b..682831615c 100644 --- a/docs/core/proto-docs.md +++ b/docs/core/proto-docs.md @@ -1323,8 +1323,7 @@ account with the provided expiration time. | ----- | ---- | ----- | ----------- | | `granter` | [string](#string) | | | | `grantee` | [string](#string) | | | -| `authorization` | [google.protobuf.Any](#google.protobuf.Any) | | | -| `expiration` | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | +| `grant` | [Grant](#cosmos.authz.v1beta1.Grant) | | | diff --git a/proto/cosmos/authz/v1beta1/tx.proto b/proto/cosmos/authz/v1beta1/tx.proto index 8a2aaf76a7..472b81a0e4 100644 --- a/proto/cosmos/authz/v1beta1/tx.proto +++ b/proto/cosmos/authz/v1beta1/tx.proto @@ -6,6 +6,7 @@ import "gogoproto/gogo.proto"; import "google/protobuf/timestamp.proto"; import "google/protobuf/any.proto"; import "cosmos/base/abci/v1beta1/abci.proto"; +import "cosmos/authz/v1beta1/authz.proto"; option go_package = "github.com/cosmos/cosmos-sdk/x/authz"; option (gogoproto.goproto_getters_all) = false; @@ -32,8 +33,7 @@ message MsgGrant { string granter = 1; string grantee = 2; - google.protobuf.Any authorization = 3 [(cosmos_proto.accepts_interface) = "Authorization"]; - google.protobuf.Timestamp expiration = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + cosmos.authz.v1beta1.Grant grant = 3 [(gogoproto.nullable) = false]; } // MsgExecResponse defines the Msg/MsgExecResponse response type. diff --git a/x/authz/authorization_grant.go b/x/authz/authorization_grant.go index 8a5995c5d0..f5ebf8797b 100644 --- a/x/authz/authorization_grant.go +++ b/x/authz/authorization_grant.go @@ -9,24 +9,23 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -// NewGrant returns new AuthrizationGrant -func NewGrant(authorization Authorization, expiration time.Time) (Grant, error) { - auth := Grant{ +// NewGrant returns new Grant +func NewGrant(a Authorization, expiration time.Time) (Grant, error) { + g := Grant{ Expiration: expiration, } - msg, ok := authorization.(proto.Message) + msg, ok := a.(proto.Message) if !ok { - return Grant{}, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", authorization) + return Grant{}, sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", a) } any, err := cdctypes.NewAnyWithValue(msg) if err != nil { return Grant{}, err } + g.Authorization = any - auth.Authorization = any - - return auth, nil + return g, nil } var ( @@ -34,16 +33,32 @@ var ( ) // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces -func (auth Grant) UnpackInterfaces(unpacker cdctypes.AnyUnpacker) error { +func (g Grant) UnpackInterfaces(unpacker cdctypes.AnyUnpacker) error { var authorization Authorization - return unpacker.UnpackAny(auth.Authorization, &authorization) + return unpacker.UnpackAny(g.Authorization, &authorization) } // GetAuthorization returns the cached value from the Grant.Authorization if present. -func (auth Grant) GetAuthorization() Authorization { - authorization, ok := auth.Authorization.GetCachedValue().(Authorization) +func (g Grant) GetAuthorization() Authorization { + if g.Authorization == nil { + return nil + } + a, ok := g.Authorization.GetCachedValue().(Authorization) if !ok { return nil } - return authorization + return a +} + +func (g Grant) ValidateBasic() error { + if g.Expiration.Unix() < time.Now().Unix() { + return sdkerrors.Wrap(ErrInvalidExpirationTime, "Time can't be in the past") + } + + av := g.Authorization.GetCachedValue() + a, ok := av.(Authorization) + if !ok { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected %T, got %T", (Authorization)(nil), av) + } + return a.ValidateBasic() } diff --git a/x/authz/keeper/msg_server.go b/x/authz/keeper/msg_server.go index 0ade948821..0d96599087 100644 --- a/x/authz/keeper/msg_server.go +++ b/x/authz/keeper/msg_server.go @@ -31,7 +31,7 @@ func (k Keeper) Grant(goCtx context.Context, msg *authz.MsgGrant) (*authz.MsgGra return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "%s doesn't exist.", t) } - err = k.SaveGrant(ctx, grantee, granter, authorization, msg.Expiration) + err = k.SaveGrant(ctx, grantee, granter, authorization, msg.Grant.Expiration) if err != nil { return nil, err } diff --git a/x/authz/msgs.go b/x/authz/msgs.go index a747d2c7a5..dd6bc8b19d 100644 --- a/x/authz/msgs.go +++ b/x/authz/msgs.go @@ -23,9 +23,9 @@ var ( //nolint:interfacer func NewMsgGrant(granter sdk.AccAddress, grantee sdk.AccAddress, a Authorization, expiration time.Time) (*MsgGrant, error) { m := &MsgGrant{ - Granter: granter.String(), - Grantee: grantee.String(), - Expiration: expiration, + Granter: granter.String(), + Grantee: grantee.String(), + Grant: Grant{Expiration: expiration}, } err := m.SetAuthorization(a) if err != nil { @@ -57,26 +57,12 @@ func (msg MsgGrant) ValidateBasic() error { if granter.Equals(grantee) { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "granter and grantee cannot be same") } - - if msg.Expiration.Unix() < time.Now().Unix() { - return sdkerrors.Wrap(ErrInvalidExpirationTime, "Time can't be in the past") - } - - av := msg.Authorization.GetCachedValue() - a, ok := av.(Authorization) - if !ok { - return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "expected %T, got %T", (Authorization)(nil), av) - } - return a.ValidateBasic() + return msg.Grant.ValidateBasic() } // GetAuthorization returns the cache value from the MsgGrant.Authorization if present. func (msg *MsgGrant) GetAuthorization() Authorization { - a, ok := msg.Authorization.GetCachedValue().(Authorization) - if !ok { - return nil - } - return a + return msg.Grant.GetAuthorization() } // SetAuthorization converts Authorization to any and adds it to MsgGrant.Authorization. @@ -89,7 +75,7 @@ func (msg *MsgGrant) SetAuthorization(a Authorization) error { if err != nil { return err } - msg.Authorization = any + msg.Grant.Authorization = any return nil } @@ -108,8 +94,7 @@ func (msg MsgExec) UnpackInterfaces(unpacker cdctypes.AnyUnpacker) error { // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces func (msg MsgGrant) UnpackInterfaces(unpacker cdctypes.AnyUnpacker) error { - var a Authorization - return unpacker.UnpackAny(msg.Authorization, &a) + return msg.Grant.UnpackInterfaces(unpacker) } // NewMsgRevoke creates a new MsgRevoke diff --git a/x/authz/msgs_test.go b/x/authz/msgs_test.go index 0d9ad12c44..7a41c1befb 100644 --- a/x/authz/msgs_test.go +++ b/x/authz/msgs_test.go @@ -6,6 +6,7 @@ import ( "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/authz" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -97,3 +98,20 @@ func TestMsgGrantAuthorization(t *testing.T) { } } } + +func TestMsgGrantGetAuthorization(t *testing.T) { + require := require.New(t) + + m := authz.MsgGrant{} + require.Nil(m.GetAuthorization()) + + g := authz.GenericAuthorization{Msg: "some_type"} + var err error + m.Grant.Authorization, err = cdctypes.NewAnyWithValue(&g) + require.NoError(err) + require.Equal(m.GetAuthorization(), &g) + + g = authz.GenericAuthorization{Msg: "some_type2"} + m.SetAuthorization(&g) + require.Equal(m.GetAuthorization(), &g) +} diff --git a/x/authz/tx.pb.go b/x/authz/tx.pb.go index 7042d70173..69e9fd72c7 100644 --- a/x/authz/tx.pb.go +++ b/x/authz/tx.pb.go @@ -6,12 +6,11 @@ package authz import ( context "context" fmt "fmt" - types "github.com/cosmos/cosmos-sdk/codec/types" - types1 "github.com/cosmos/cosmos-sdk/types" + types1 "github.com/cosmos/cosmos-sdk/codec/types" + types "github.com/cosmos/cosmos-sdk/types" _ "github.com/gogo/protobuf/gogoproto" grpc1 "github.com/gogo/protobuf/grpc" proto "github.com/gogo/protobuf/proto" - github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" _ "github.com/regen-network/cosmos-proto" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -20,14 +19,12 @@ import ( io "io" math "math" math_bits "math/bits" - time "time" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf -var _ = time.Kitchen // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. @@ -38,10 +35,9 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // MsgGrant grants the provided authorization to the grantee on the granter's // account with the provided expiration time. type MsgGrant struct { - Granter string `protobuf:"bytes,1,opt,name=granter,proto3" json:"granter,omitempty"` - Grantee string `protobuf:"bytes,2,opt,name=grantee,proto3" json:"grantee,omitempty"` - Authorization *types.Any `protobuf:"bytes,3,opt,name=authorization,proto3" json:"authorization,omitempty"` - Expiration time.Time `protobuf:"bytes,4,opt,name=expiration,proto3,stdtime" json:"expiration"` + Granter string `protobuf:"bytes,1,opt,name=granter,proto3" json:"granter,omitempty"` + Grantee string `protobuf:"bytes,2,opt,name=grantee,proto3" json:"grantee,omitempty"` + Grant Grant `protobuf:"bytes,3,opt,name=grant,proto3" json:"grant"` } func (m *MsgGrant) Reset() { *m = MsgGrant{} } @@ -79,7 +75,7 @@ var xxx_messageInfo_MsgGrant proto.InternalMessageInfo // MsgExecResponse defines the Msg/MsgExecResponse response type. type MsgExecResponse struct { - Result *types1.Result `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"` + Result *types.Result `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"` } func (m *MsgExecResponse) Reset() { *m = MsgExecResponse{} } @@ -123,7 +119,7 @@ type MsgExec struct { // Authorization Msg requests to execute. Each msg must implement Authorization interface // The x/authz will try to find a grant matching (msg.signers[0], grantee, MsgTypeURL(msg)) // triple and validate it. - Msgs []*types.Any `protobuf:"bytes,2,rep,name=msgs,proto3" json:"msgs,omitempty"` + Msgs []*types1.Any `protobuf:"bytes,2,rep,name=msgs,proto3" json:"msgs,omitempty"` } func (m *MsgExec) Reset() { *m = MsgExec{} } @@ -286,40 +282,39 @@ func init() { func init() { proto.RegisterFile("cosmos/authz/v1beta1/tx.proto", fileDescriptor_3ceddab7d8589ad1) } var fileDescriptor_3ceddab7d8589ad1 = []byte{ - // 523 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x54, 0x4d, 0x6f, 0xd3, 0x30, - 0x18, 0xae, 0xd7, 0xd2, 0xad, 0xef, 0x98, 0x60, 0xa1, 0x87, 0x2e, 0x62, 0x69, 0x14, 0xbe, 0x76, - 0xa0, 0x8e, 0x56, 0x2e, 0x5c, 0x57, 0x81, 0x90, 0x80, 0x08, 0x29, 0x1a, 0x17, 0x2e, 0x55, 0xd2, - 0x19, 0x2f, 0x6a, 0x13, 0x47, 0xb1, 0x33, 0xb5, 0xfb, 0x15, 0xfb, 0x31, 0xfb, 0x11, 0xd5, 0x4e, - 0x93, 0xb8, 0x70, 0xe2, 0xa3, 0xfd, 0x13, 0x1c, 0x51, 0x6c, 0xa7, 0xb4, 0x40, 0x87, 0xc4, 0xa9, - 0x7e, 0xfd, 0x3c, 0x7e, 0xfc, 0xf8, 0x79, 0xdf, 0x06, 0xf6, 0x07, 0x8c, 0xc7, 0x8c, 0xbb, 0x41, - 0x2e, 0x4e, 0xcf, 0xdd, 0xb3, 0xc3, 0x90, 0x88, 0xe0, 0xd0, 0x15, 0x63, 0x9c, 0x66, 0x4c, 0x30, - 0xa3, 0xa9, 0x60, 0x2c, 0x61, 0xac, 0x61, 0x73, 0x4f, 0xed, 0xf6, 0x25, 0xc7, 0xd5, 0x14, 0x59, - 0x98, 0x4d, 0xca, 0x28, 0x53, 0xfb, 0xc5, 0x4a, 0xef, 0xb6, 0x29, 0x63, 0x74, 0x44, 0x5c, 0x59, - 0x85, 0xf9, 0x47, 0x57, 0x44, 0x31, 0xe1, 0x22, 0x88, 0x53, 0x4d, 0xd8, 0xfb, 0x9d, 0x10, 0x24, - 0x13, 0x0d, 0x3d, 0xd0, 0x0e, 0xc3, 0x80, 0x13, 0x37, 0x08, 0x07, 0xd1, 0xc2, 0x65, 0x51, 0x28, - 0x92, 0xf3, 0x09, 0xc1, 0x96, 0xc7, 0xe9, 0xab, 0x2c, 0x48, 0x84, 0xd1, 0x82, 0x4d, 0x5a, 0x2c, - 0x48, 0xd6, 0x42, 0x36, 0x3a, 0x68, 0xf8, 0x65, 0xf9, 0x0b, 0x21, 0xad, 0x8d, 0x65, 0x84, 0x18, - 0x1e, 0xec, 0x14, 0x6f, 0x64, 0x59, 0x74, 0x1e, 0x88, 0x88, 0x25, 0xad, 0xaa, 0x8d, 0x0e, 0xb6, - 0xbb, 0x4d, 0xac, 0x8c, 0xe1, 0xd2, 0x18, 0x3e, 0x4a, 0x26, 0xbd, 0xdd, 0xab, 0xcb, 0xce, 0xce, - 0xd1, 0x32, 0xdd, 0x5f, 0x3d, 0x6d, 0xbc, 0x00, 0x20, 0xe3, 0x34, 0xca, 0x94, 0x56, 0x4d, 0x6a, - 0x99, 0x7f, 0x68, 0x1d, 0x97, 0x29, 0xf4, 0xb6, 0xa6, 0x5f, 0xda, 0x95, 0x8b, 0xaf, 0x6d, 0xe4, - 0x2f, 0x9d, 0x73, 0xde, 0xc0, 0x1d, 0x8f, 0xd3, 0x97, 0x63, 0x32, 0xf0, 0x09, 0x4f, 0x59, 0xc2, - 0x89, 0xf1, 0x1c, 0xea, 0x19, 0xe1, 0xf9, 0x48, 0xc8, 0xa7, 0x6d, 0x77, 0x6d, 0xac, 0xe3, 0x2f, - 0xe2, 0xc1, 0x32, 0x11, 0x1d, 0x0f, 0xf6, 0x25, 0xcf, 0xd7, 0x7c, 0x87, 0xc1, 0xa6, 0x16, 0x5b, - 0x8e, 0x01, 0xad, 0xc6, 0xf0, 0x1a, 0x6a, 0x31, 0xa7, 0xbc, 0xb5, 0x61, 0x57, 0xd7, 0xbe, 0xde, - 0xbe, 0xba, 0xec, 0xdc, 0xe7, 0x27, 0x43, 0xec, 0x71, 0xfa, 0xd4, 0x56, 0x93, 0xb1, 0x1a, 0x86, - 0xd4, 0x70, 0x0c, 0xb8, 0x5b, 0xb6, 0xa4, 0xb4, 0xef, 0x04, 0xd0, 0xf0, 0x38, 0xf5, 0xc9, 0x19, - 0x1b, 0x92, 0xff, 0xea, 0x93, 0x0d, 0xb7, 0x63, 0x4e, 0xfb, 0x62, 0x92, 0x92, 0x7e, 0x9e, 0x8d, - 0x64, 0x9b, 0x1a, 0x3e, 0xc4, 0x9c, 0x1e, 0x4f, 0x52, 0xf2, 0x3e, 0x1b, 0x39, 0xf7, 0x60, 0x77, - 0x71, 0x45, 0x79, 0x6f, 0xf7, 0x07, 0x82, 0xaa, 0xc7, 0xa9, 0xf1, 0x0e, 0x6e, 0xa9, 0x19, 0xb1, - 0xf0, 0xdf, 0x26, 0x1b, 0x97, 0x86, 0xcd, 0xc7, 0x37, 0xe3, 0x8b, 0x7e, 0xbc, 0x85, 0x9a, 0x8c, - 0x74, 0x7f, 0x2d, 0xbf, 0x80, 0xcd, 0x47, 0x37, 0xc2, 0x0b, 0x35, 0x1f, 0xea, 0x3a, 0x9b, 0xf6, - 0xda, 0x03, 0x8a, 0x60, 0x3e, 0xf9, 0x07, 0xa1, 0xd4, 0xec, 0xf5, 0xa6, 0xdf, 0xad, 0xca, 0x74, - 0x66, 0xa1, 0xeb, 0x99, 0x85, 0xbe, 0xcd, 0x2c, 0x74, 0x31, 0xb7, 0x2a, 0xd7, 0x73, 0xab, 0xf2, - 0x79, 0x6e, 0x55, 0x3e, 0x3c, 0xa4, 0x91, 0x38, 0xcd, 0x43, 0x3c, 0x60, 0xb1, 0xfe, 0x23, 0xeb, - 0x9f, 0x0e, 0x3f, 0x19, 0xba, 0x63, 0xf5, 0x5d, 0x08, 0xeb, 0x72, 0x00, 0x9e, 0xfd, 0x0c, 0x00, - 0x00, 0xff, 0xff, 0x5b, 0x69, 0x39, 0xef, 0x2e, 0x04, 0x00, 0x00, + // 498 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x53, 0xcb, 0x6e, 0xd3, 0x40, + 0x14, 0xb5, 0x9b, 0x34, 0x25, 0x13, 0x24, 0xc0, 0x64, 0xe1, 0x1a, 0xea, 0x58, 0xe6, 0x95, 0x05, + 0x1d, 0xab, 0x61, 0x01, 0xdb, 0x46, 0x42, 0x48, 0x80, 0x85, 0x64, 0xc1, 0x86, 0x4d, 0x64, 0xa7, + 0xc3, 0xc4, 0x4a, 0xec, 0xb1, 0x7c, 0xc7, 0x25, 0xe9, 0x57, 0xf0, 0x31, 0x7c, 0x44, 0xc4, 0xaa, + 0x4b, 0x56, 0x08, 0x92, 0x9f, 0x60, 0x89, 0x3c, 0x8f, 0x50, 0x50, 0x5a, 0x24, 0x56, 0x99, 0x3b, + 0xe7, 0xe4, 0xdc, 0x73, 0xcf, 0xf5, 0xa0, 0x83, 0x31, 0x83, 0x8c, 0x41, 0x10, 0x57, 0x7c, 0x72, + 0x16, 0x9c, 0x1e, 0x25, 0x84, 0xc7, 0x47, 0x01, 0x9f, 0xe3, 0xa2, 0x64, 0x9c, 0x59, 0x5d, 0x09, + 0x63, 0x01, 0x63, 0x05, 0x3b, 0xfb, 0xf2, 0x76, 0x24, 0x38, 0x81, 0xa2, 0x88, 0xc2, 0xe9, 0x52, + 0x46, 0x99, 0xbc, 0xaf, 0x4f, 0xea, 0xb6, 0x47, 0x19, 0xa3, 0x33, 0x12, 0x88, 0x2a, 0xa9, 0x3e, + 0x04, 0x3c, 0xcd, 0x08, 0xf0, 0x38, 0x2b, 0x14, 0x61, 0xff, 0x6f, 0x42, 0x9c, 0x2f, 0x14, 0x74, + 0x4f, 0x39, 0x4c, 0x62, 0x20, 0x41, 0x9c, 0x8c, 0xd3, 0x8d, 0xcb, 0xba, 0x50, 0x24, 0x6f, 0xeb, + 0x18, 0xd2, 0xb5, 0x60, 0xf8, 0x1f, 0xd1, 0xb5, 0x10, 0xe8, 0x8b, 0x32, 0xce, 0xb9, 0x65, 0xa3, + 0x3d, 0x5a, 0x1f, 0x48, 0x69, 0x9b, 0x9e, 0xd9, 0x6f, 0x47, 0xba, 0xfc, 0x8d, 0x10, 0x7b, 0xe7, + 0x22, 0x42, 0xac, 0xa7, 0x68, 0x57, 0x1c, 0xed, 0x86, 0x67, 0xf6, 0x3b, 0x83, 0x3b, 0x78, 0x5b, + 0x32, 0x58, 0xe8, 0x0f, 0x9b, 0xcb, 0x6f, 0x3d, 0x23, 0x92, 0x7c, 0xff, 0x15, 0xba, 0x11, 0x02, + 0x7d, 0x3e, 0x27, 0xe3, 0x88, 0x40, 0xc1, 0x72, 0x20, 0xd6, 0x33, 0xd4, 0x2a, 0x09, 0x54, 0x33, + 0x2e, 0xda, 0x77, 0x06, 0x9e, 0x16, 0xab, 0x67, 0xc4, 0x62, 0x2c, 0x2d, 0x18, 0x09, 0x5e, 0xa4, + 0xf8, 0x3e, 0x43, 0x7b, 0x4a, 0xec, 0xa2, 0x55, 0xf3, 0x4f, 0xab, 0x2f, 0x51, 0x33, 0x03, 0x0a, + 0xf6, 0x8e, 0xd7, 0xe8, 0x77, 0x06, 0x5d, 0x2c, 0xb3, 0xc5, 0x3a, 0x5b, 0x7c, 0x9c, 0x2f, 0x86, + 0xde, 0x97, 0xcf, 0x87, 0x77, 0xe1, 0x64, 0x8a, 0x43, 0xa0, 0x8f, 0x3d, 0x39, 0xc4, 0x71, 0xc5, + 0x27, 0xac, 0x4c, 0xcf, 0x62, 0x9e, 0xb2, 0x3c, 0x12, 0x1a, 0xbe, 0x85, 0x6e, 0xea, 0xd8, 0xb4, + 0x7d, 0x3f, 0x46, 0xed, 0x10, 0x68, 0x44, 0x4e, 0xd9, 0x94, 0xfc, 0x57, 0x96, 0x1e, 0xba, 0x9e, + 0x01, 0x1d, 0xf1, 0x45, 0x41, 0x46, 0x55, 0x39, 0x13, 0x91, 0xb6, 0x23, 0x94, 0x01, 0x7d, 0xbb, + 0x28, 0xc8, 0xbb, 0x72, 0xe6, 0xdf, 0x46, 0xb7, 0x36, 0x2d, 0x74, 0xdf, 0xc1, 0x4f, 0x13, 0x35, + 0x42, 0xa0, 0xd6, 0x1b, 0xb4, 0x2b, 0xf7, 0xe8, 0x6e, 0x5f, 0x82, 0x36, 0xec, 0x3c, 0xbc, 0x1a, + 0xdf, 0xec, 0xe3, 0x35, 0x6a, 0x8a, 0x48, 0x0f, 0x2e, 0xe5, 0xd7, 0xb0, 0xf3, 0xe0, 0x4a, 0x78, + 0xa3, 0x16, 0xa1, 0x96, 0xca, 0xa6, 0x77, 0xe9, 0x1f, 0x24, 0xc1, 0x79, 0xf4, 0x0f, 0x82, 0xd6, + 0x1c, 0x0e, 0x97, 0x3f, 0x5c, 0x63, 0xb9, 0x72, 0xcd, 0xf3, 0x95, 0x6b, 0x7e, 0x5f, 0xb9, 0xe6, + 0xa7, 0xb5, 0x6b, 0x9c, 0xaf, 0x5d, 0xe3, 0xeb, 0xda, 0x35, 0xde, 0xdf, 0xa7, 0x29, 0x9f, 0x54, + 0x09, 0x1e, 0xb3, 0x4c, 0xbd, 0x46, 0xf5, 0x73, 0x08, 0x27, 0xd3, 0x60, 0x2e, 0xdf, 0x41, 0xd2, + 0x12, 0x1f, 0xc0, 0x93, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x4a, 0xa9, 0xb0, 0x59, 0xf3, 0x03, + 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -508,26 +503,16 @@ func (m *MsgGrant) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Expiration, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Expiration):]) - if err1 != nil { - return 0, err1 - } - i -= n1 - i = encodeVarintTx(dAtA, i, uint64(n1)) - i-- - dAtA[i] = 0x22 - if m.Authorization != nil { - { - size, err := m.Authorization.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTx(dAtA, i, uint64(size)) + { + size, err := m.Grant.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err } - i-- - dAtA[i] = 0x1a + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) } + i-- + dAtA[i] = 0x1a if len(m.Grantee) > 0 { i -= len(m.Grantee) copy(dAtA[i:], m.Grantee) @@ -739,11 +724,7 @@ func (m *MsgGrant) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } - if m.Authorization != nil { - l = m.Authorization.Size() - n += 1 + l + sovTx(uint64(l)) - } - l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Expiration) + l = m.Grant.Size() n += 1 + l + sovTx(uint64(l)) return n } @@ -920,7 +901,7 @@ func (m *MsgGrant) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Authorization", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Grant", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -947,43 +928,7 @@ func (m *MsgGrant) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Authorization == nil { - m.Authorization = &types.Any{} - } - if err := m.Authorization.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Expiration", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTx - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthTx - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthTx - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Expiration, dAtA[iNdEx:postIndex]); err != nil { + if err := m.Grant.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -1067,7 +1012,7 @@ func (m *MsgExecResponse) Unmarshal(dAtA []byte) error { return io.ErrUnexpectedEOF } if m.Result == nil { - m.Result = &types1.Result{} + m.Result = &types.Result{} } if err := m.Result.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err @@ -1184,7 +1129,7 @@ func (m *MsgExec) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Msgs = append(m.Msgs, &types.Any{}) + m.Msgs = append(m.Msgs, &types1.Any{}) if err := m.Msgs[len(m.Msgs)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } From e96839de2f30f7afecb2b697434fe2c33f772f95 Mon Sep 17 00:00:00 2001 From: likhita-809 <78951027+likhita-809@users.noreply.github.com> Date: Mon, 10 May 2021 18:11:24 +0530 Subject: [PATCH 2/5] x/staking v0.43 Audit updates (#9267) * update staking specs * use gosimple code in x/staking/client/testutil/suite.go * small fixes * add godoc to methods in msg_server and grpc_query * changes to godoc and small fixes * remove unnecessary lines * remove mentions of serviceMsg and avoid having slash after Msg --- docs/core/proto-docs.md | 16 +++++------ x/staking/client/cli/query.go | 1 - x/staking/client/testutil/suite.go | 14 +++++----- x/staking/keeper/delegation_test.go | 5 +++- x/staking/keeper/grpc_query.go | 4 ++- x/staking/keeper/grpc_query_test.go | 4 +-- x/staking/keeper/msg_server.go | 5 ++++ x/staking/simulation/genesis.go | 18 ++++++------ x/staking/simulation/genesis_test.go | 2 +- x/staking/simulation/params.go | 9 +++--- x/staking/spec/01_state.md | 4 +++ x/staking/spec/03_messages.md | 42 ++++++++++++++-------------- x/staking/spec/07_events.md | 12 ++++---- x/staking/spec/README.md | 12 ++++---- 14 files changed, 80 insertions(+), 68 deletions(-) diff --git a/docs/core/proto-docs.md b/docs/core/proto-docs.md index 682831615c..254ce496c8 100644 --- a/docs/core/proto-docs.md +++ b/docs/core/proto-docs.md @@ -6182,9 +6182,9 @@ AuthorizationType defines the type of staking module authorization type | Name | Number | Description | | ---- | ------ | ----------- | | AUTHORIZATION_TYPE_UNSPECIFIED | 0 | AUTHORIZATION_TYPE_UNSPECIFIED specifies an unknown authorization type | -| AUTHORIZATION_TYPE_DELEGATE | 1 | AUTHORIZATION_TYPE_DELEGATE defines an authorization type for Msg/Delegate | -| AUTHORIZATION_TYPE_UNDELEGATE | 2 | AUTHORIZATION_TYPE_UNDELEGATE defines an authorization type for Msg/Undelegate | -| AUTHORIZATION_TYPE_REDELEGATE | 3 | AUTHORIZATION_TYPE_REDELEGATE defines an authorization type for Msg/BeginRedelegate | +| AUTHORIZATION_TYPE_DELEGATE | 1 | AUTHORIZATION_TYPE_DELEGATE defines an authorization type for MsgDelegate | +| AUTHORIZATION_TYPE_UNDELEGATE | 2 | AUTHORIZATION_TYPE_UNDELEGATE defines an authorization type for MsgUndelegate | +| AUTHORIZATION_TYPE_REDELEGATE | 3 | AUTHORIZATION_TYPE_REDELEGATE defines an authorization type for MsgBeginRedelegate | @@ -7167,7 +7167,7 @@ of coins from a delegator and source validator to a destination validator. ### MsgBeginRedelegateResponse -MsgBeginRedelegateResponse defines the Msg/BeginRedelegate response type. +MsgBeginRedelegateResponse defines the MsgBeginRedelegate response type. | Field | Type | Label | Description | @@ -7203,7 +7203,7 @@ MsgCreateValidator defines a SDK message for creating a new validator. ### MsgCreateValidatorResponse -MsgCreateValidatorResponse defines the Msg/CreateValidator response type. +MsgCreateValidatorResponse defines the MsgCreateValidator response type. @@ -7231,7 +7231,7 @@ from a delegator to a validator. ### MsgDelegateResponse -MsgDelegateResponse defines the Msg/Delegate response type. +MsgDelegateResponse defines the MsgDelegate response type. @@ -7259,7 +7259,7 @@ MsgEditValidator defines a SDK message for editing an existing validator. ### MsgEditValidatorResponse -MsgEditValidatorResponse defines the Msg/EditValidator response type. +MsgEditValidatorResponse defines the MsgEditValidator response type. @@ -7287,7 +7287,7 @@ delegate and a validator. ### MsgUndelegateResponse -MsgUndelegateResponse defines the Msg/Undelegate response type. +MsgUndelegateResponse defines the MsgUndelegate response type. | Field | Type | Label | Description | diff --git a/x/staking/client/cli/query.go b/x/staking/client/cli/query.go index 7bf3ae41cc..0982296161 100644 --- a/x/staking/client/cli/query.go +++ b/x/staking/client/cli/query.go @@ -660,7 +660,6 @@ $ %s query staking historical-info 5 params := &types.QueryHistoricalInfoRequest{Height: height} res, err := queryClient.HistoricalInfo(cmd.Context(), params) - if err != nil { return err } diff --git a/x/staking/client/testutil/suite.go b/x/staking/client/testutil/suite.go index a6e385b405..d75978cb6f 100644 --- a/x/staking/client/testutil/suite.go +++ b/x/staking/client/testutil/suite.go @@ -230,7 +230,7 @@ func (s *IntegrationTestSuite) TestGetCmdQueryValidator() { }, { "happy case", - []string{fmt.Sprintf("%s", val.ValAddress), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, + []string{val.ValAddress.String(), fmt.Sprintf("--%s=json", tmcli.OutputFlag)}, false, }, } @@ -415,7 +415,7 @@ func (s *IntegrationTestSuite) TestGetCmdQueryDelegations() { } } -func (s *IntegrationTestSuite) TestGetCmdQueryDelegationsTo() { +func (s *IntegrationTestSuite) TestGetCmdQueryValidatorDelegations() { val := s.network.Validators[0] testCases := []struct { @@ -755,7 +755,7 @@ func (s *IntegrationTestSuite) TestGetCmdQueryRedelegation() { } } -func (s *IntegrationTestSuite) TestGetCmdQueryRedelegationsFrom() { +func (s *IntegrationTestSuite) TestGetCmdQueryValidatorRedelegations() { val := s.network.Validators[0] val2 := s.network.Validators[1] @@ -925,7 +925,7 @@ not_bonded_tokens: "0"`, cli.DefaultTokens.Mul(sdk.NewInt(2)).String()), } } -func (s *IntegrationTestSuite) TestNewCmdEditValidator() { +func (s *IntegrationTestSuite) TestNewEditValidatorCmd() { val := s.network.Validators[0] details := "bio" @@ -1041,7 +1041,7 @@ func (s *IntegrationTestSuite) TestNewCmdEditValidator() { } } -func (s *IntegrationTestSuite) TestNewCmdDelegate() { +func (s *IntegrationTestSuite) TestNewDelegateCmd() { val := s.network.Validators[0] info, _, err := val.ClientCtx.Keyring.NewMnemonic("NewAccount", keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) @@ -1123,7 +1123,7 @@ func (s *IntegrationTestSuite) TestNewCmdDelegate() { } } -func (s *IntegrationTestSuite) TestNewCmdRedelegate() { +func (s *IntegrationTestSuite) TestNewRedelegateCmd() { val := s.network.Validators[0] val2 := s.network.Validators[1] @@ -1209,7 +1209,7 @@ func (s *IntegrationTestSuite) TestNewCmdRedelegate() { } } -func (s *IntegrationTestSuite) TestNewCmdUnbond() { +func (s *IntegrationTestSuite) TestNewUnbondCmd() { val := s.network.Validators[0] testCases := []struct { diff --git a/x/staking/keeper/delegation_test.go b/x/staking/keeper/delegation_test.go index de7a9ba0e6..49083d2376 100644 --- a/x/staking/keeper/delegation_test.go +++ b/x/staking/keeper/delegation_test.go @@ -384,6 +384,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { selfDelegation := types.NewDelegation(addrVals[0].Bytes(), addrVals[0], issuedShares) app.StakingKeeper.SetDelegation(ctx, selfDelegation) + // add bonded tokens to pool for delegations bondedPool := app.StakingKeeper.GetBondedPool(ctx) require.NoError(t, simapp.FundModuleAccount(app, ctx, bondedPool.GetName(), delCoins)) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) @@ -470,6 +471,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) { selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) app.StakingKeeper.SetDelegation(ctx, selfDelegation) + // add bonded tokens to pool for delegations bondedPool := app.StakingKeeper.GetBondedPool(ctx) require.NoError(t, simapp.FundModuleAccount(app, ctx, bondedPool.GetName(), delCoins)) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) @@ -518,7 +520,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) { _, err = app.StakingKeeper.Undelegate(ctx, addrDels[1], addrVals[0], remainingTokens.ToDec()) require.NoError(t, err) - // now validator should now be deleted from state + // now validator should be deleted from state validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[0]) require.False(t, found, "%v", validator) } @@ -556,6 +558,7 @@ func TestUnbondingAllDelegationFromValidator(t *testing.T) { validator, issuedShares = validator.AddTokensFromDel(delTokens) require.Equal(t, delTokens, issuedShares.RoundInt()) + // add bonded tokens to pool for delegations bondedPool := app.StakingKeeper.GetBondedPool(ctx) require.NoError(t, simapp.FundModuleAccount(app, ctx, bondedPool.GetName(), delCoins)) app.AccountKeeper.SetModuleAccount(ctx, bondedPool) diff --git a/x/staking/keeper/grpc_query.go b/x/staking/keeper/grpc_query.go index 49d9b92ce2..4852848255 100644 --- a/x/staking/keeper/grpc_query.go +++ b/x/staking/keeper/grpc_query.go @@ -61,7 +61,7 @@ func (k Querier) Validators(c context.Context, req *types.QueryValidatorsRequest return &types.QueryValidatorsResponse{Validators: validators, Pagination: pageRes}, nil } -// Validator queries validator info for given validator addr +// Validator queries validator info for given validator address func (k Querier) Validator(c context.Context, req *types.QueryValidatorRequest) (*types.QueryValidatorResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "empty request") @@ -380,6 +380,7 @@ func (k Querier) HistoricalInfo(c context.Context, req *types.QueryHistoricalInf return &types.QueryHistoricalInfoResponse{Hist: &hi}, nil } +// Redelegations queries redelegations of given address func (k Querier) Redelegations(c context.Context, req *types.QueryRedelegationsRequest) (*types.QueryRedelegationsResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "empty request") @@ -410,6 +411,7 @@ func (k Querier) Redelegations(c context.Context, req *types.QueryRedelegationsR return &types.QueryRedelegationsResponse{RedelegationResponses: redelResponses, Pagination: pageRes}, nil } +// DelegatorValidators queries all validators info for given delegator address func (k Querier) DelegatorValidators(c context.Context, req *types.QueryDelegatorValidatorsRequest) (*types.QueryDelegatorValidatorsResponse, error) { if req == nil { return nil, status.Error(codes.InvalidArgument, "empty request") diff --git a/x/staking/keeper/grpc_query_test.go b/x/staking/keeper/grpc_query_test.go index c82f71552e..dc47dc34c1 100644 --- a/x/staking/keeper/grpc_query_test.go +++ b/x/staking/keeper/grpc_query_test.go @@ -84,7 +84,7 @@ func (suite *KeeperTestSuite) TestGRPCQueryValidators() { } } -func (suite *KeeperTestSuite) TestGRPCValidator() { +func (suite *KeeperTestSuite) TestGRPCQueryValidator() { app, ctx, queryClient, vals := suite.app, suite.ctx, suite.queryClient, suite.vals validator, found := app.StakingKeeper.GetValidator(ctx, vals[0].GetOperator()) suite.True(found) @@ -586,7 +586,7 @@ func (suite *KeeperTestSuite) TestGRPCQueryHistoricalInfo() { } } -func (suite *KeeperTestSuite) TestGRPCQueryRedelegation() { +func (suite *KeeperTestSuite) TestGRPCQueryRedelegations() { app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals addrAcc, addrAcc1 := addrs[0], addrs[1] diff --git a/x/staking/keeper/msg_server.go b/x/staking/keeper/msg_server.go index 37e5edb042..25031c9638 100644 --- a/x/staking/keeper/msg_server.go +++ b/x/staking/keeper/msg_server.go @@ -26,6 +26,7 @@ func NewMsgServerImpl(keeper Keeper) types.MsgServer { var _ types.MsgServer = msgServer{} +// CreateValidator defines a method for creating a new validator func (k msgServer) CreateValidator(goCtx context.Context, msg *types.MsgCreateValidator) (*types.MsgCreateValidatorResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) @@ -119,6 +120,7 @@ func (k msgServer) CreateValidator(goCtx context.Context, msg *types.MsgCreateVa return &types.MsgCreateValidatorResponse{}, nil } +// EditValidator defines a method for editing an existing validator func (k msgServer) EditValidator(goCtx context.Context, msg *types.MsgEditValidator) (*types.MsgEditValidatorResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress) @@ -181,6 +183,7 @@ func (k msgServer) EditValidator(goCtx context.Context, msg *types.MsgEditValida return &types.MsgEditValidatorResponse{}, nil } +// Delegate defines a method for performing a delegation of coins from a delegator to a validator func (k msgServer) Delegate(goCtx context.Context, msg *types.MsgDelegate) (*types.MsgDelegateResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) valAddr, valErr := sdk.ValAddressFromBech32(msg.ValidatorAddress) @@ -237,6 +240,7 @@ func (k msgServer) Delegate(goCtx context.Context, msg *types.MsgDelegate) (*typ return &types.MsgDelegateResponse{}, nil } +// BeginRedelegate defines a method for performing a redelegation of coins from a delegator and source validator to a destination validator func (k msgServer) BeginRedelegate(goCtx context.Context, msg *types.MsgBeginRedelegate) (*types.MsgBeginRedelegateResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) valSrcAddr, err := sdk.ValAddressFromBech32(msg.ValidatorSrcAddress) @@ -302,6 +306,7 @@ func (k msgServer) BeginRedelegate(goCtx context.Context, msg *types.MsgBeginRed }, nil } +// Undelegate defines a method for performing an undelegation from a delegate and a validator func (k msgServer) Undelegate(goCtx context.Context, msg *types.MsgUndelegate) (*types.MsgUndelegateResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) diff --git a/x/staking/simulation/genesis.go b/x/staking/simulation/genesis.go index bf01b90bfe..2f1caf2692 100644 --- a/x/staking/simulation/genesis.go +++ b/x/staking/simulation/genesis.go @@ -21,18 +21,18 @@ const ( historicalEntries = "historical_entries" ) -// GenUnbondingTime randomized UnbondingTime -func GenUnbondingTime(r *rand.Rand) (ubdTime time.Duration) { +// genUnbondingTime returns randomized UnbondingTime +func genUnbondingTime(r *rand.Rand) (ubdTime time.Duration) { return time.Duration(simulation.RandIntBetween(r, 60, 60*60*24*3*2)) * time.Second } -// GenMaxValidators randomized MaxValidators -func GenMaxValidators(r *rand.Rand) (maxValidators uint32) { +// genMaxValidators returns randomized MaxValidators +func genMaxValidators(r *rand.Rand) (maxValidators uint32) { return uint32(r.Intn(250) + 1) } -// GetHistEntries randomized HistoricalEntries between 0-100. -func GetHistEntries(r *rand.Rand) uint32 { +// getHistEntries returns randomized HistoricalEntries between 0-100. +func getHistEntries(r *rand.Rand) uint32 { return uint32(r.Intn(int(types.DefaultHistoricalEntries + 1))) } @@ -47,17 +47,17 @@ func RandomizedGenState(simState *module.SimulationState) { simState.AppParams.GetOrGenerate( simState.Cdc, unbondingTime, &unbondTime, simState.Rand, - func(r *rand.Rand) { unbondTime = GenUnbondingTime(r) }, + func(r *rand.Rand) { unbondTime = genUnbondingTime(r) }, ) simState.AppParams.GetOrGenerate( simState.Cdc, maxValidators, &maxVals, simState.Rand, - func(r *rand.Rand) { maxVals = GenMaxValidators(r) }, + func(r *rand.Rand) { maxVals = genMaxValidators(r) }, ) simState.AppParams.GetOrGenerate( simState.Cdc, historicalEntries, &histEntries, simState.Rand, - func(r *rand.Rand) { histEntries = GetHistEntries(r) }, + func(r *rand.Rand) { histEntries = getHistEntries(r) }, ) // NOTE: the slashing module need to be defined after the staking module on the diff --git a/x/staking/simulation/genesis_test.go b/x/staking/simulation/genesis_test.go index a307ce9ad7..f7b31b29e1 100644 --- a/x/staking/simulation/genesis_test.go +++ b/x/staking/simulation/genesis_test.go @@ -68,7 +68,7 @@ func TestRandomizedGenState(t *testing.T) { require.Equal(t, "1", stakingGenesis.Validators[2].MinSelfDelegation.String()) } -// TestRandomizedGenState tests abnormal scenarios of applying RandomizedGenState. +// TestRandomizedGenState1 tests abnormal scenarios of applying RandomizedGenState. func TestRandomizedGenState1(t *testing.T) { interfaceRegistry := codectypes.NewInterfaceRegistry() cdc := codec.NewProtoCodec(interfaceRegistry) diff --git a/x/staking/simulation/params.go b/x/staking/simulation/params.go index ffbbe4fe47..30789d3467 100644 --- a/x/staking/simulation/params.go +++ b/x/staking/simulation/params.go @@ -6,9 +6,8 @@ import ( "fmt" "math/rand" - "github.com/cosmos/cosmos-sdk/x/simulation" - simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/simulation" "github.com/cosmos/cosmos-sdk/x/staking/types" ) @@ -18,17 +17,17 @@ func ParamChanges(r *rand.Rand) []simtypes.ParamChange { return []simtypes.ParamChange{ simulation.NewSimParamChange(types.ModuleName, string(types.KeyMaxValidators), func(r *rand.Rand) string { - return fmt.Sprintf("%d", GenMaxValidators(r)) + return fmt.Sprintf("%d", genMaxValidators(r)) }, ), simulation.NewSimParamChange(types.ModuleName, string(types.KeyUnbondingTime), func(r *rand.Rand) string { - return fmt.Sprintf("\"%d\"", GenUnbondingTime(r)) + return fmt.Sprintf("\"%d\"", genUnbondingTime(r)) }, ), simulation.NewSimParamChange(types.ModuleName, string(types.KeyHistoricalEntries), func(r *rand.Rand) string { - return fmt.Sprintf("%d", GetHistEntries(r)) + return fmt.Sprintf("%d", getHistEntries(r)) }, ), } diff --git a/x/staking/spec/01_state.md b/x/staking/spec/01_state.md index 7238994118..9dacbb22fd 100644 --- a/x/staking/spec/01_state.md +++ b/x/staking/spec/01_state.md @@ -4,6 +4,10 @@ order: 1 # State +## Pool + +Pool is used for tracking bonded and not-bonded token supply of the bond denomination. + ## LastTotalPower LastTotalPower tracks the total amounts of bonded tokens recorded during the previous end block. diff --git a/x/staking/spec/03_messages.md b/x/staking/spec/03_messages.md index 8b5fa28d66..4dd654227d 100644 --- a/x/staking/spec/03_messages.md +++ b/x/staking/spec/03_messages.md @@ -6,16 +6,16 @@ order: 3 In this section we describe the processing of the staking messages and the corresponding updates to the state. All created/modified state objects specified by each message are defined within the [state](./02_state_transitions.md) section. -## Msg/CreateValidator +## MsgCreateValidator -A validator is created using the `Msg/CreateValidator` service message. +A validator is created using the `MsgCreateValidator` message. The validator must be created with an initial delegation from the operator. +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L16-L17 +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L35-L51 -This service message is expected to fail if: +This message is expected to fail if: - another validator with this operator address is already registered - another validator with this pubkey is already registered @@ -26,32 +26,32 @@ This service message is expected to fail if: - the initial `MaxChangeRate` is either negative or > `MaxRate` - the description fields are too large -This service message creates and stores the `Validator` object at appropriate indexes. +This message creates and stores the `Validator` object at appropriate indexes. Additionally a self-delegation is made with the initial tokens delegation tokens `Delegation`. The validator always starts as unbonded but may be bonded in the first end-block. -## Msg/EditValidator +## MsgEditValidator The `Description`, `CommissionRate` of a validator can be updated using the -`Msg/EditCandidacy` service message. +`MsgEditValidator` message. +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L19-L20 +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L56-L76 -This service message is expected to fail if: +This message is expected to fail if: - the initial `CommissionRate` is either negative or > `MaxRate` - the `CommissionRate` has already been updated within the previous 24 hours - the `CommissionRate` is > `MaxChangeRate` - the description fields are too large -This service message stores the updated `Validator` object. +This message stores the updated `Validator` object. -## Msg/Delegate +## MsgDelegate -Within this service message the delegator provides coins, and in return receives +Within this message the delegator provides coins, and in return receives some amount of their validator's (newly created) delegator-shares that are assigned to `Delegation.Shares`. @@ -59,9 +59,9 @@ assigned to `Delegation.Shares`. +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L81-L90 -This service message is expected to fail if: +This message is expected to fail if: -- the validator is does not exist +- the validator does not exist - the `Amount` `Coin` has a denomination different than one defined by `params.BondDenom` - the exchange rate is invalid, meaning the validator has no tokens (due to slashing) but there are outstanding shares - the amount delegated is less than the minimum allowed delegation @@ -82,20 +82,20 @@ will not be added to the power index until it is unjailed. ![Delegation sequence](../../../docs/uml/svg/delegation_sequence.svg) -## Msg/Undelegate +## MsgUndelegate -The `Msg/Undelegate` service message allows delegators to undelegate their tokens from +The `MsgUndelegate` message allows delegators to undelegate their tokens from validator. +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L30-L32 +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L112-L121 -This service message returns a response containing the completion time of the undelegation: +This message returns a response containing the completion time of the undelegation: +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L123-L126 -This service message is expected to fail if: +This message is expected to fail if: - the delegation doesn't exist - the validator doesn't exist @@ -103,7 +103,7 @@ This service message is expected to fail if: - existing `UnbondingDelegation` has maximum entries as defined by `params.MaxEntries` - the `Amount` has a denomination different than one defined by `params.BondDenom` -When this service message is processed the following actions occur: +When this message is processed the following actions occur: - validator's `DelegatorShares` and the delegation's `Shares` are both reduced by the message `SharesAmount` - calculate the token worth of the shares remove that amount tokens held within the validator @@ -116,7 +116,7 @@ When this service message is processed the following actions occur: ![Unbond sequence](../../../docs/uml/svg/unbond_sequence.svg) -## Msg/BeginRedelegate +## MsgBeginRedelegate The redelegation command allows delegators to instantly switch validators. Once the unbonding period has passed, the redelegation is automatically completed in @@ -126,11 +126,11 @@ the EndBlocker. +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L95-L105 -This service message returns a response containing the completion time of the redelegation: +This message returns a response containing the completion time of the redelegation: +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/staking/v1beta1/tx.proto#L107-L110 -This service message is expected to fail if: +This message is expected to fail if: - the delegation doesn't exist - the source or destination validators don't exist @@ -139,7 +139,7 @@ This service message is expected to fail if: - existing `Redelegation` has maximum entries as defined by `params.MaxEntries` - the `Amount` `Coin` has a denomination different than one defined by `params.BondDenom` -When this service message is processed the following actions occur: +When this message is processed the following actions occur: - the source validator's `DelegatorShares` and the delegations `Shares` are both reduced by the message `SharesAmount` - calculate the token worth of the shares remove that amount tokens held within the source validator. diff --git a/x/staking/spec/07_events.md b/x/staking/spec/07_events.md index 660319b3c1..f219fa2ca4 100644 --- a/x/staking/spec/07_events.md +++ b/x/staking/spec/07_events.md @@ -18,9 +18,9 @@ The staking module emits the following events: | complete_redelegation | destination_validator | {dstValidatorAddress} | | complete_redelegation | delegator | {delegatorAddress} | -## Service Messages +## Msg's -### Msg/CreateValidator +### MsgCreateValidator | Type | Attribute Key | Attribute Value | | ---------------- | ------------- | ------------------ | @@ -30,7 +30,7 @@ The staking module emits the following events: | message | action | create_validator | | message | sender | {senderAddress} | -### Msg/EditValidator +### MsgEditValidator | Type | Attribute Key | Attribute Value | | -------------- | ------------------- | ------------------- | @@ -40,7 +40,7 @@ The staking module emits the following events: | message | action | edit_validator | | message | sender | {senderAddress} | -### Msg/Delegate +### MsgDelegate | Type | Attribute Key | Attribute Value | | -------- | ------------- | ------------------ | @@ -50,7 +50,7 @@ The staking module emits the following events: | message | action | delegate | | message | sender | {senderAddress} | -### Msg/Undelegate +### MsgUndelegate | Type | Attribute Key | Attribute Value | | ------- | ------------------- | ------------------ | @@ -63,7 +63,7 @@ The staking module emits the following events: - [0] Time is formatted in the RFC3339 standard -### Msg/BeginRedelegate +### MsgBeginRedelegate | Type | Attribute Key | Attribute Value | | ---------- | --------------------- | --------------------- | diff --git a/x/staking/spec/README.md b/x/staking/spec/README.md index a2cb7cf3a8..8eb42de8aa 100644 --- a/x/staking/spec/README.md +++ b/x/staking/spec/README.md @@ -38,11 +38,11 @@ network. - [Delegations](02_state_transitions.md#delegations) - [Slashing](02_state_transitions.md#slashing) 3. **[Messages](03_messages.md)** - - [Msg/CreateValidator](03_messages.md#msgcreatevalidator) - - [Msg/EditValidator](03_messages.md#msgeditvalidator) - - [Msg/Delegate](03_messages.md#msgdelegate) - - [Msg/BeginUnbonding](03_messages.md#msgbeginunbonding) - - [Msg/BeginRedelegate](03_messages.md#msgbeginredelegate) + - [MsgCreateValidator](03_messages.md#msgcreatevalidator) + - [MsgEditValidator](03_messages.md#msgeditvalidator) + - [MsgDelegate](03_messages.md#msgdelegate) + - [MsgUndelegate](03_messages.md#msgundelegate) + - [MsgBeginRedelegate](03_messages.md#msgbeginredelegate) 4. **[Begin-Block](04_begin_block.md)** - [Historical Info Tracking](04_begin_block.md#historical-info-tracking) 5. **[End-Block ](05_end_block.md)** @@ -51,5 +51,5 @@ network. 6. **[Hooks](06_hooks.md)** 7. **[Events](07_events.md)** - [EndBlocker](07_events.md#endblocker) - - [Handlers](07_events.md#handlers) + - [Msg's](07_events.md#msg's) 8. **[Parameters](08_params.md)** From 6425825cac9b53d9281230b4f3f395d0ddf0e199 Mon Sep 17 00:00:00 2001 From: Marie Gauthier Date: Mon, 10 May 2021 14:53:47 +0200 Subject: [PATCH 3/5] ADR-042: Group module (#9089) * Add ADR-042 * Fix link * Small improvements * Update link * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba * Update docs/architecture/adr-042-group-module.md * Update docs/architecture/adr-042-group-module.md * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba * Update docs/architecture/adr-042-group-module.md * Update docs/architecture/adr-042-group-module.md * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba * Move orm to specific section * Update docs/architecture/adr-042-group-module.md * Update naming * Add concrete use cases * Rework ### Proposal * Rework Vote, Exec and implementation sections * Update to account for removal of ServiceMsg Co-authored-by: Alessio Treglia Co-authored-by: Robert Zaremba Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- docs/architecture/adr-042-group-module.md | 277 ++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 docs/architecture/adr-042-group-module.md diff --git a/docs/architecture/adr-042-group-module.md b/docs/architecture/adr-042-group-module.md new file mode 100644 index 0000000000..dcca987bcd --- /dev/null +++ b/docs/architecture/adr-042-group-module.md @@ -0,0 +1,277 @@ +# ADR 042: Group Module + +## Changelog + +- 2020/04/09: Initial Draft + +## Status + +Draft + +## Abstract + +This ADR defines the `x/group` module which allows the creation and management of on-chain multi-signature accounts and enables voting for message execution based on configurable decision policies. + +## Context + +The legacy amino multi-signature mechanism of the Cosmos SDK has certain limitations: +- Key rotation is not possible, although this can be solved with [account rekeying](adr-034-account-rekeying.md). +- Thresholds can't be changed. +- UX is cumbersome for non-technical users ([#5661](https://github.com/cosmos/cosmos-sdk/issues/5661)). +- It requires `legacy_amino` sign mode ([#8141](https://github.com/cosmos/cosmos-sdk/issues/8141)). + +While the group module is not meant to be a total replacement for the current multi-signature accounts, it provides a solution to the limitations described above, with a more flexible key management system where keys can be added, updated or removed, as well as configurable thresholds. +It's meant to be used with other access control modules such as [`x/feegrant`](./adr-029-fee-grant-module.md) ans [`x/authz`](adr-030-authz-module.md) to simplify key management for individuals and organizations. + +The proof of concept of the group module can be found in https://github.com/regen-network/regen-ledger/tree/master/proto/regen/group/v1alpha1 and https://github.com/regen-network/regen-ledger/tree/master/x/group. + +## Decision + +We propose merging the `x/group` module with its supporting [ORM/Table Store package](https://github.com/regen-network/regen-ledger/tree/master/orm) ([#7098](https://github.com/cosmos/cosmos-sdk/issues/7098)) into the Cosmos SDK and continuing development here. There will be a dedicated ADR for the ORM package. + +### Group + +A group is a composition of accounts with associated weights. It is not +an account and doesn't have a balance. It doesn't in and of itself have any +sort of voting or decision weight. +Group members can create proposals and vote on them through group accounts using different decision policies. + +It has an `admin` account which can manage members in the group, update the group +metadata and set a new admin. + +```proto +message GroupInfo { + + // group_id is the unique ID of this group. + uint64 group_id = 1; + + // admin is the account address of the group's admin. + string admin = 2; + + // metadata is any arbitrary metadata to attached to the group. + bytes metadata = 3; + + // version is used to track changes to a group's membership structure that + // would break existing proposals. Whenever a member weight has changed, + // or any member is added or removed, the version is incremented and will + // invalidate all proposals from older versions. + uint64 version = 4; + + // total_weight is the sum of the group members' weights. + string total_weight = 5; +} +``` + +```proto +message GroupMember { + + // group_id is the unique ID of the group. + uint64 group_id = 1; + + // member is the member data. + Member member = 2; +} + +// Member represents a group member with an account address, +// non-zero weight and metadata. +message Member { + + // address is the member's account address. + string address = 1; + + // weight is the member's voting weight that should be greater than 0. + string weight = 2; + + // metadata is any arbitrary metadata to attached to the member. + bytes metadata = 3; +} +``` + +### Group Account + +A group account is an account associated with a group and a decision policy. +A group account does have a balance. + +Group accounts are abstracted from groups because a single group may have +multiple decision policies for different types of actions. Managing group +membership separately from decision policies results in the least overhead +and keeps membership consistent across different policies. The pattern that +is recommended is to have a single master group account for a given group, +and then to create separate group accounts with different decision policies +and delegate the desired permissions from the master account to +those "sub-accounts" using the [`x/authz` module](adr-030-authz-module.md). + +```proto +message GroupAccountInfo { + + // address is the group account address. + string address = 1; + + // group_id is the ID of the Group the GroupAccount belongs to. + uint64 group_id = 2; + + // admin is the account address of the group admin. + string admin = 3; + + // metadata is any arbitrary metadata of this group account. + bytes metadata = 4; + + // version is used to track changes to a group's GroupAccountInfo structure that + // invalidates active proposal from old versions. + uint64 version = 5; + + // decision_policy specifies the group account's decision policy. + google.protobuf.Any decision_policy = 6 [(cosmos_proto.accepts_interface) = "DecisionPolicy"]; +} +``` + +Similarly to a group admin, a group account admin can update its metadata, decision policy or set a new group account admin. + +A group account can also be an admin or a member of a group. +For instance, a group admin could be another group account which could "elects" the members or it could be the same group that elects itself. + +### Decision Policy + +A decision policy is the mechanism by which members of a group can vote on +proposals. + +All decision policies should have a minimum and maximum voting window. +The minimum voting window is the minimum duration that must pass in order +for a proposal to potentially pass, and it may be set to 0. The maximum voting +window is the maximum time that a proposal may be voted on and executed if +it reached enough support before it is closed. +Both of these values must be less than a chain-wide max voting window parameter. + +We define the `DecisionPolicy` interface that all decision policies must implement: + +```go +type DecisionPolicy interface { + codec.ProtoMarshaler + + ValidateBasic() error + GetTimeout() types.Duration + Allow(tally Tally, totalPower string, votingDuration time.Duration) (DecisionPolicyResult, error) + Validate(g GroupInfo) error +} + +type DecisionPolicyResult struct { + Allow bool + Final bool +} +``` + +#### Threshold decision policy + +A threshold decision policy defines a minimum support votes (_yes_), based on a tally +of voter weights, for a proposal to pass. For +this decision policy, abstain and veto are treated as no support (_no_). + +```proto +message ThresholdDecisionPolicy { + + // threshold is the minimum weighted sum of support votes for a proposal to succeed. + string threshold = 1; + + // voting_period is the duration from submission of a proposal to the end of voting period + // Within this period, votes and exec messages can be submitted. + google.protobuf.Duration voting_period = 2 [(gogoproto.nullable) = false]; +} +``` + +### Proposal + +Any member of a group can submit a proposal for a group account to decide upon. +A proposal consists of a set of `sdk.Msg`s that will be executed if the proposal +passes as well as any metadata associated with the proposal. These `sdk.Msg`s get validated as part of the `Msg/CreateProposal` request validation. They should also have their signer set as the group account. + +Internally, a proposal also tracks: +- its current `Status`: submitted, closed or aborted +- its `Result`: unfinalized, accepted or rejected +- its `VoteState` in the form of a `Tally`, which is calculated on new votes and when executing the proposal. + +```proto +// Tally represents the sum of weighted votes. +message Tally { + option (gogoproto.goproto_getters) = false; + + // yes_count is the weighted sum of yes votes. + string yes_count = 1; + + // no_count is the weighted sum of no votes. + string no_count = 2; + + // abstain_count is the weighted sum of abstainers. + string abstain_count = 3; + + // veto_count is the weighted sum of vetoes. + string veto_count = 4; +} +``` + +### Voting + +Members of a group can vote on proposals. There are four choices to choose while voting - yes, no, abstain and veto. Not +all decision policies will support them. Votes can contain some optional metadata. +In the current implementation, the voting window begins as soon as a proposal +is submitted. + +Voting internally updates the proposal `VoteState` as well as `Status` and `Result` if needed. + +### Executing Proposals + +Proposals will not be automatically executed by the chain in this current design, +but rather a user must submit a `Msg/Exec` transaction to attempt to execute the +proposal based on the current votes and decision policy. A future upgrade could +automate this and have the group account (or a fee granter) pay. + +#### Changing Group Membership + +In the current implementation, updating a group or a group account after submitting a proposal will make it invalid. It will simply fail if someone calls `Msg/Exec` and will eventually be garbage collected. + +### Notes on current implementation + +This section outlines the current implementation used in the proof of concept of the group module but this could be subject to changes and iterated on. + +#### ORM + +The [ORM package](https://github.com/cosmos/cosmos-sdk/discussions/9156) defines tables, sequences and secondary indexes which are used in the group module. + +Groups are stored in state as part of a `groupTable`, the `group_id` being an auto-increment integer. Group members are stored in a `groupMemberTable`. + +Group accounts are stored in a `groupAccountTable`. The group account address is generated based on an auto-increment integer which is used to derive the group module `RootModuleKey` into a `DerivedModuleKey`, as stated in [ADR-033](adr-033-protobuf-inter-module-comm.md#modulekeys-and-moduleids). The group account is added as a new `ModuleAccount` through `x/auth`. + +Proposals are stored as part of the `proposalTable` using the `Proposal` type. The `proposal_id` is an auto-increment integer. + +Votes are stored in the `voteTable`. The primary key is based on the vote's `proposal_id` and `voter` account address. + +#### ADR-033 to route proposal messages + +Inter-module communication introduced by [ADR-033](adr-033-protobuf-inter-module-comm.md) can be used to route a proposal's messages using the `DerivedModuleKey` corresponding to the proposal's group account. + +## Consequences + +### Positive + +- Improved UX for multi-signature accounts allowing key rotation and custom decision policies. + +### Negative + +### Neutral + +- It uses ADR 033 so it will need to be implemented within the Cosmos SDK, but this doesn't imply necessarily any large refactoring of existing Cosmos SDK modules. +- The current implementation of the group module uses the ORM package. + +## Further Discussions + +- Convergence of `/group` and `x/gov` as both support proposals and voting: https://github.com/cosmos/cosmos-sdk/discussions/9066 +- `x/group` possible future improvements: + - Execute proposals on submission (https://github.com/regen-network/regen-ledger/issues/288) + - Withdraw a proposal (https://github.com/regen-network/cosmos-modules/issues/41) + - Make `Tally` more flexible and support non-binary choices + +## References + +- Initial specification: + - https://gist.github.com/aaronc/b60628017352df5983791cad30babe56#group-module + - [#5236](https://github.com/cosmos/cosmos-sdk/pull/5236) +- Proposal to add `x/group` into the SDK: [#7633](https://github.com/cosmos/cosmos-sdk/issues/7633) From 75d3547e805fdebde453119820545e9c9e740712 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 May 2021 14:03:11 +0000 Subject: [PATCH 4/5] build(deps): bump github.com/otiai10/copy from 1.5.1 to 1.6.0 (#9289) Bumps [github.com/otiai10/copy](https://github.com/otiai10/copy) from 1.5.1 to 1.6.0. - [Release notes](https://github.com/otiai10/copy/releases) - [Commits](https://github.com/otiai10/copy/compare/v1.5.1...v1.6.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4ee3d42a96..bc23379ef6 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/jhump/protoreflect v1.8.2 github.com/magiconair/properties v1.8.5 github.com/mattn/go-isatty v0.0.12 - github.com/otiai10/copy v1.5.1 + github.com/otiai10/copy v1.6.0 github.com/pelletier/go-toml v1.8.1 // indirect github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.10.0 diff --git a/go.sum b/go.sum index 6bad6bbff0..e76a4a0baa 100644 --- a/go.sum +++ b/go.sum @@ -512,8 +512,8 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/otiai10/copy v1.5.1 h1:a/cs2E1/1V0az8K5nblbl+ymEa4E11AfaOLMar8V34w= -github.com/otiai10/copy v1.5.1/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E= +github.com/otiai10/copy v1.6.0 h1:IinKAryFFuPONZ7cm6T6E2QX/vcJwSnlaA5lfoaXIiQ= +github.com/otiai10/copy v1.6.0/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= From 709ab089c118370e5733729df50f7e2d7ee4e375 Mon Sep 17 00:00:00 2001 From: technicallyty <48813565+technicallyty@users.noreply.github.com> Date: Mon, 10 May 2021 09:17:55 -0700 Subject: [PATCH 5/5] x/bank v0.43 Audit updates (#9271) * add godoc to keeper functions * re-add ValidateBasic to MsgSend CLI tx * add comment to reflect new return value on method * remove unecessary variable * cleanup key comments * typo * unused param * update messages spec * move event emission to end of method * update keeper spec * update proto message to point correct path to interface * keeper spec typos * fix test for event emission being moved * change to blocklist * rename SendEnabledCoin(s) -> IsSendEnabledCoins * typo * remove unecessary check * move changelog line Co-authored-by: technicallyty <48813565+tytech3@users.noreply.github.com> --- CHANGELOG.md | 1 + proto/cosmos/bank/v1beta1/bank.proto | 2 +- x/auth/vesting/msg_server.go | 2 +- x/auth/vesting/types/expected_keepers.go | 2 +- x/bank/atlas/atlas-v0.39.1.md | 4 +- x/bank/keeper/keeper.go | 10 ++- x/bank/keeper/keeper_test.go | 43 ++++------ x/bank/keeper/msg_server.go | 4 +- x/bank/keeper/send.go | 40 ++++----- x/bank/module.go | 2 +- x/bank/simulation/operations.go | 4 +- x/bank/spec/02_keepers.md | 100 +++++++++++------------ x/bank/spec/03_messages.md | 27 +++--- x/bank/types/bank.pb.go | 74 ++++++++--------- x/bank/types/key.go | 6 +- 15 files changed, 159 insertions(+), 162 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33f45f7e88..f0ac315e0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -105,6 +105,7 @@ if input key is empty, or input data contains empty key. * `sdk.Msg` now only contains `ValidateBasic` and `GetSigners` methods. The remaining methods `GetSignBytes`, `Route` and `Type` are moved to `legacytx.LegacyMsg`. * The `RegisterCustomTypeURL` function and the `cosmos.base.v1beta1.ServiceMsg` interface have been removed from the interface registry. * (codec) [\#9251](https://github.com/cosmos/cosmos-sdk/pull/9251) Rename `clientCtx.JSONMarshaler` to `clientCtx.JSONCodec` as per #9226. +* (x/bank) [\#9271](https://github.com/cosmos/cosmos-sdk/pull/9271) SendEnabledCoin(s) renamed to IsSendEnabledCoin(s) to better reflect its functionality. ### State Machine Breaking diff --git a/proto/cosmos/bank/v1beta1/bank.proto b/proto/cosmos/bank/v1beta1/bank.proto index 1a4cc289ea..eb843b2cb6 100644 --- a/proto/cosmos/bank/v1beta1/bank.proto +++ b/proto/cosmos/bank/v1beta1/bank.proto @@ -52,7 +52,7 @@ message Supply { option (gogoproto.equal) = true; option (gogoproto.goproto_getters) = false; - option (cosmos_proto.implements_interface) = "*github.com/cosmos/cosmos-sdk/x/bank/exported.SupplyI"; + option (cosmos_proto.implements_interface) = "*github.com/cosmos/cosmos-sdk/x/bank/legacy/v040.SupplyI"; repeated cosmos.base.v1beta1.Coin total = 1 [(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"]; diff --git a/x/auth/vesting/msg_server.go b/x/auth/vesting/msg_server.go index dadd65dbcf..795fc2c605 100644 --- a/x/auth/vesting/msg_server.go +++ b/x/auth/vesting/msg_server.go @@ -32,7 +32,7 @@ func (s msgServer) CreateVestingAccount(goCtx context.Context, msg *types.MsgCre ak := s.AccountKeeper bk := s.BankKeeper - if err := bk.SendEnabledCoins(ctx, msg.Amount...); err != nil { + if err := bk.IsSendEnabledCoins(ctx, msg.Amount...); err != nil { return nil, err } diff --git a/x/auth/vesting/types/expected_keepers.go b/x/auth/vesting/types/expected_keepers.go index 8212f6e375..5705eea30b 100644 --- a/x/auth/vesting/types/expected_keepers.go +++ b/x/auth/vesting/types/expected_keepers.go @@ -7,7 +7,7 @@ import ( // BankKeeper defines the expected interface contract the vesting module requires // for creating vesting accounts with funds. type BankKeeper interface { - SendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error + IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error BlockedAddr(addr sdk.AccAddress) bool } diff --git a/x/bank/atlas/atlas-v0.39.1.md b/x/bank/atlas/atlas-v0.39.1.md index affca5b9e2..cc7a75d951 100644 --- a/x/bank/atlas/atlas-v0.39.1.md +++ b/x/bank/atlas/atlas-v0.39.1.md @@ -35,14 +35,14 @@ with particular kinds of accounts. ``` 4. Create the keeper. Note, the `x/bank` module depends on the `x/auth` module - and a list of blacklisted account addresses which funds are not allowed to be + and a list of blocklisted account addresses which funds are not allowed to be sent to. Your application will need to define this method based your needs. ```go func NewApp(...) *App { // ... app.BankKeeper = bank.NewBaseKeeper( - app.AccountKeeper, app.subspaces[bank.ModuleName], app.BlacklistedAccAddrs(), + app.AccountKeeper, app.subspaces[bank.ModuleName], app.BlocklistedAccAddrs(), ) } ``` diff --git a/x/bank/keeper/keeper.go b/x/bank/keeper/keeper.go index c02b11a17e..cc8ded2401 100644 --- a/x/bank/keeper/keeper.go +++ b/x/bank/keeper/keeper.go @@ -55,6 +55,7 @@ type BaseKeeper struct { paramSpace paramtypes.Subspace } +// GetPaginatedTotalSupply queries for the supply, ignoring 0 coins, with a given pagination func (k BaseKeeper) GetPaginatedTotalSupply(ctx sdk.Context, pagination *query.PageRequest) (sdk.Coins, *query.PageResponse, error) { store := ctx.KVStore(k.storeKey) supplyStore := prefix.NewStore(store, types.SupplyKey) @@ -213,7 +214,8 @@ func (k BaseKeeper) GetSupply(ctx sdk.Context, denom string) sdk.Coin { } } -// GetDenomMetaData retrieves the denomination metadata +// GetDenomMetaData retrieves the denomination metadata. returns the metadata and true if the denom exists, +// false otherwise. func (k BaseKeeper) GetDenomMetaData(ctx sdk.Context, denom string) (types.Metadata, bool) { store := ctx.KVStore(k.storeKey) store = prefix.NewStore(store, types.DenomMetadataKey(denom)) @@ -427,6 +429,7 @@ func (k BaseKeeper) BurnCoins(ctx sdk.Context, moduleName string, amounts sdk.Co return nil } +// setSupply sets the supply for the given coin func (k BaseKeeper) setSupply(ctx sdk.Context, coin sdk.Coin) { intBytes, err := coin.Amount.Marshal() if err != nil { @@ -444,6 +447,7 @@ func (k BaseKeeper) setSupply(ctx sdk.Context, coin sdk.Coin) { } } +// trackDelegation tracks the delegation of the given account if it is a vesting account func (k BaseKeeper) trackDelegation(ctx sdk.Context, addr sdk.AccAddress, balance, amt sdk.Coins) error { acc := k.ak.GetAccount(ctx, addr) if acc == nil { @@ -460,6 +464,7 @@ func (k BaseKeeper) trackDelegation(ctx sdk.Context, addr sdk.AccAddress, balanc return nil } +// trackUndelegation trakcs undelegation of the given account if it is a vesting account func (k BaseKeeper) trackUndelegation(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) error { acc := k.ak.GetAccount(ctx, addr) if acc == nil { @@ -476,6 +481,9 @@ func (k BaseKeeper) trackUndelegation(ctx sdk.Context, addr sdk.AccAddress, amt return nil } +// IterateTotalSupply iterates over the total supply calling the given cb (callback) function +// with the balance of each coin. +// The iteration stops if the callback returns true. func (k BaseViewKeeper) IterateTotalSupply(ctx sdk.Context, cb func(sdk.Coin) bool) { store := ctx.KVStore(k.storeKey) supplyStore := prefix.NewStore(store, types.SupplyKey) diff --git a/x/bank/keeper/keeper_test.go b/x/bank/keeper/keeper_test.go index e20ce3e21f..e9f35b6a11 100644 --- a/x/bank/keeper/keeper_test.go +++ b/x/bank/keeper/keeper_test.go @@ -111,7 +111,7 @@ func (suite *IntegrationTestSuite) SetupTest() { } func (suite *IntegrationTestSuite) TestSupply() { - _, ctx := suite.app, suite.ctx + ctx := suite.ctx require := suite.Require() @@ -119,7 +119,7 @@ func (suite *IntegrationTestSuite) TestSupply() { authKeeper, keeper := suite.initKeepersWithmAccPerms(make(map[string]bool)) initialPower := int64(100) - initTokens := suite.app.StakingKeeper.TokensFromConsensusPower(suite.ctx, initialPower) + initTokens := suite.app.StakingKeeper.TokensFromConsensusPower(ctx, initialPower) totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens)) // set burnerAcc balance @@ -140,7 +140,7 @@ func (suite *IntegrationTestSuite) TestSupply() { require.Equal(total.String(), "") } -func (suite *IntegrationTestSuite) TestSendCoinsFromModuleToAccount_Blacklist() { +func (suite *IntegrationTestSuite) TestSendCoinsFromModuleToAccount_Blocklist() { ctx := suite.ctx // add module accounts to supply keeper @@ -484,11 +484,11 @@ func (suite *IntegrationTestSuite) TestSendEnabled() { barCoin := sdk.NewCoin("barcoin", sdk.OneInt()) // assert with default (all denom) send enabled both Bar and Bond Denom are enabled - suite.Require().Equal(enabled, app.BankKeeper.SendEnabledCoin(ctx, barCoin)) - suite.Require().Equal(enabled, app.BankKeeper.SendEnabledCoin(ctx, bondCoin)) + suite.Require().Equal(enabled, app.BankKeeper.IsSendEnabledCoin(ctx, barCoin)) + suite.Require().Equal(enabled, app.BankKeeper.IsSendEnabledCoin(ctx, bondCoin)) // Both coins should be send enabled. - err := app.BankKeeper.SendEnabledCoins(ctx, fooCoin, bondCoin) + err := app.BankKeeper.IsSendEnabledCoins(ctx, fooCoin, bondCoin) suite.Require().NoError(err) // Set default send_enabled to !enabled, add a foodenom that overrides default as enabled @@ -497,20 +497,20 @@ func (suite *IntegrationTestSuite) TestSendEnabled() { app.BankKeeper.SetParams(ctx, params) // Expect our specific override to be enabled, others to be !enabled. - suite.Require().Equal(enabled, app.BankKeeper.SendEnabledCoin(ctx, fooCoin)) - suite.Require().Equal(!enabled, app.BankKeeper.SendEnabledCoin(ctx, barCoin)) - suite.Require().Equal(!enabled, app.BankKeeper.SendEnabledCoin(ctx, bondCoin)) + suite.Require().Equal(enabled, app.BankKeeper.IsSendEnabledCoin(ctx, fooCoin)) + suite.Require().Equal(!enabled, app.BankKeeper.IsSendEnabledCoin(ctx, barCoin)) + suite.Require().Equal(!enabled, app.BankKeeper.IsSendEnabledCoin(ctx, bondCoin)) // Foo coin should be send enabled. - err = app.BankKeeper.SendEnabledCoins(ctx, fooCoin) + err = app.BankKeeper.IsSendEnabledCoins(ctx, fooCoin) suite.Require().NoError(err) // Expect an error when one coin is not send enabled. - err = app.BankKeeper.SendEnabledCoins(ctx, fooCoin, bondCoin) + err = app.BankKeeper.IsSendEnabledCoins(ctx, fooCoin, bondCoin) suite.Require().Error(err) // Expect an error when all coins are not send enabled. - err = app.BankKeeper.SendEnabledCoins(ctx, bondCoin, barCoin) + err = app.BankKeeper.IsSendEnabledCoins(ctx, bondCoin, barCoin) suite.Require().Error(err) } @@ -538,12 +538,9 @@ func (suite *IntegrationTestSuite) TestMsgSendEvents() { app.AccountKeeper.SetAccount(ctx, acc) newCoins := sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50)) + suite.Require().NoError(simapp.FundAccount(app, ctx, addr, newCoins)) - suite.Require().Error(app.BankKeeper.SendCoins(ctx, addr, addr2, newCoins)) - - events := ctx.EventManager().ABCIEvents() - suite.Require().Equal(2, len(events)) - + suite.Require().NoError(app.BankKeeper.SendCoins(ctx, addr, addr2, newCoins)) event1 := sdk.Event{ Type: types.EventTypeTransfer, Attributes: []abci.EventAttribute{}, @@ -570,17 +567,9 @@ func (suite *IntegrationTestSuite) TestMsgSendEvents() { abci.EventAttribute{Key: []byte(types.AttributeKeySender), Value: []byte(addr.String())}, ) - suite.Require().Equal(abci.Event(event1), events[0]) - suite.Require().Equal(abci.Event(event2), events[1]) - - suite.Require().NoError(simapp.FundAccount(app, ctx, addr, sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50)))) - newCoins = sdk.NewCoins(sdk.NewInt64Coin(fooDenom, 50)) - - suite.Require().NoError(app.BankKeeper.SendCoins(ctx, addr, addr2, newCoins)) - // events are shifted due to the funding account events - events = ctx.EventManager().ABCIEvents() - suite.Require().Equal(12, len(events)) + events := ctx.EventManager().ABCIEvents() + suite.Require().Equal(10, len(events)) suite.Require().Equal(abci.Event(event1), events[8]) suite.Require().Equal(abci.Event(event2), events[9]) } diff --git a/x/bank/keeper/msg_server.go b/x/bank/keeper/msg_server.go index b318db2cc4..5d940b459e 100644 --- a/x/bank/keeper/msg_server.go +++ b/x/bank/keeper/msg_server.go @@ -26,7 +26,7 @@ var _ types.MsgServer = msgServer{} func (k msgServer) Send(goCtx context.Context, msg *types.MsgSend) (*types.MsgSendResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - if err := k.SendEnabledCoins(ctx, msg.Amount...); err != nil { + if err := k.IsSendEnabledCoins(ctx, msg.Amount...); err != nil { return nil, err } @@ -75,7 +75,7 @@ func (k msgServer) MultiSend(goCtx context.Context, msg *types.MsgMultiSend) (*t // NOTE: totalIn == totalOut should already have been checked for _, in := range msg.Inputs { - if err := k.SendEnabledCoins(ctx, in.Coins...); err != nil { + if err := k.IsSendEnabledCoins(ctx, in.Coins...); err != nil { return nil, err } } diff --git a/x/bank/keeper/send.go b/x/bank/keeper/send.go index 8fd7e602a4..675eb67735 100644 --- a/x/bank/keeper/send.go +++ b/x/bank/keeper/send.go @@ -20,8 +20,8 @@ type SendKeeper interface { GetParams(ctx sdk.Context) types.Params SetParams(ctx sdk.Context, params types.Params) - SendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool - SendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error + IsSendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool + IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error BlockedAddr(addr sdk.AccAddress) bool } @@ -131,19 +131,6 @@ func (k BaseSendKeeper) InputOutputCoins(ctx sdk.Context, inputs []types.Input, // SendCoins transfers amt coins from a sending account to a receiving account. // An error is returned upon failure. func (k BaseSendKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error { - ctx.EventManager().EmitEvents(sdk.Events{ - sdk.NewEvent( - types.EventTypeTransfer, - sdk.NewAttribute(types.AttributeKeyRecipient, toAddr.String()), - sdk.NewAttribute(types.AttributeKeySender, fromAddr.String()), - sdk.NewAttribute(sdk.AttributeKeyAmount, amt.String()), - ), - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(types.AttributeKeySender, fromAddr.String()), - ), - }) - err := k.subUnlockedCoins(ctx, fromAddr, amt) if err != nil { return err @@ -164,6 +151,19 @@ func (k BaseSendKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAd k.ak.SetAccount(ctx, k.ak.NewAccountWithAddress(ctx, toAddr)) } + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeTransfer, + sdk.NewAttribute(types.AttributeKeyRecipient, toAddr.String()), + sdk.NewAttribute(types.AttributeKeySender, fromAddr.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, amt.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(types.AttributeKeySender, fromAddr.String()), + ), + }) + return nil } @@ -277,20 +277,20 @@ func (k BaseSendKeeper) setBalance(ctx sdk.Context, addr sdk.AccAddress, balance return nil } -// SendEnabledCoins checks the coins provide and returns an ErrSendDisabled if +// IsSendEnabledCoins checks the coins provide and returns an ErrSendDisabled if // any of the coins are not configured for sending. Returns nil if sending is enabled // for all provided coin -func (k BaseSendKeeper) SendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error { +func (k BaseSendKeeper) IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error { for _, coin := range coins { - if !k.SendEnabledCoin(ctx, coin) { + if !k.IsSendEnabledCoin(ctx, coin) { return sdkerrors.Wrapf(types.ErrSendDisabled, "%s transfers are currently disabled", coin.Denom) } } return nil } -// SendEnabledCoin returns the current SendEnabled status of the provided coin's denom -func (k BaseSendKeeper) SendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool { +// IsSendEnabledCoin returns the current SendEnabled status of the provided coin's denom +func (k BaseSendKeeper) IsSendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool { return k.GetParams(ctx).SendEnabledDenom(coin.Denom) } diff --git a/x/bank/module.go b/x/bank/module.go index ea97a9efa3..c43251acc2 100644 --- a/x/bank/module.go +++ b/x/bank/module.go @@ -186,7 +186,7 @@ func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { } // RegisterStoreDecoder registers a decoder for supply module's types -func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) {} +func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {} // WeightedOperations returns the all the gov module operations with their respective weights. func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { diff --git a/x/bank/simulation/operations.go b/x/bank/simulation/operations.go index 16e1b77500..8c3aef80f8 100644 --- a/x/bank/simulation/operations.go +++ b/x/bank/simulation/operations.go @@ -61,7 +61,7 @@ func SimulateMsgSend(ak types.AccountKeeper, bk keeper.Keeper) simtypes.Operatio simAccount, toSimAcc, coins, skip := randomSendFields(r, ctx, accs, bk, ak) // Check send_enabled status of each coin denom - if err := bk.SendEnabledCoins(ctx, coins...); err != nil { + if err := bk.IsSendEnabledCoins(ctx, coins...); err != nil { return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgSend, err.Error()), nil, nil } @@ -173,7 +173,7 @@ func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simtypes.Ope } // Check send_enabled status of each sent coin denom - if err := bk.SendEnabledCoins(ctx, totalSentCoins...); err != nil { + if err := bk.IsSendEnabledCoins(ctx, totalSentCoins...); err != nil { return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgMultiSend, err.Error()), nil, nil } diff --git a/x/bank/spec/02_keepers.md b/x/bank/spec/02_keepers.md index 8db69c34d0..ea5b9ed692 100644 --- a/x/bank/spec/02_keepers.md +++ b/x/bank/spec/02_keepers.md @@ -12,14 +12,14 @@ require. Best practices dictate careful review of `bank` module code to ensure that permissions are limited in the way that you expect. -## Blacklisting Addresses +## Blocklisting Addresses The `x/bank` module accepts a map of addresses that are considered blocklisted from directly and explicitly receiving funds through means such as `MsgSend` and `MsgMultiSend` and direct API calls like `SendCoinsFromModuleToAccount`. Typically, these addresses are module accounts. If these addresses receive funds -outside of the expected rules of the state machine, invariants are likely to be +outside the expected rules of the state machine, invariants are likely to be broken and could result in a halted network. By providing the `x/bank` module with a blocklisted set of addresses, an error occurs for the operation if a user or client attempts to directly or indirectly send funds to a blocklisted account, for example, by using [IBC](http://docs.cosmos.network/master/ibc/). @@ -58,32 +58,30 @@ The base keeper provides full-permission access: the ability to arbitrary modify // Keeper defines a module interface that facilitates the transfer of coins // between accounts. type Keeper interface { - SendKeeper - - InitGenesis(sdk.Context, *types.GenesisState) - ExportGenesis(sdk.Context) *types.GenesisState - - GetSupply(ctx sdk.Context) exported.SupplyI - SetSupply(ctx sdk.Context, supply exported.SupplyI) - - GetDenomMetaData(ctx sdk.Context, denom string) types.Metadata - SetDenomMetaData(ctx sdk.Context, denomMetaData types.Metadata) - IterateAllDenomMetaData(ctx sdk.Context, cb func(types.Metadata) bool) - - SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error - SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error - SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error - DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error - UndelegateCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error - MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error - BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error - - DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error - UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error - MarshalSupply(supplyI exported.SupplyI) ([]byte, error) - UnmarshalSupply(bz []byte) (exported.SupplyI, error) - - types.QueryServer + SendKeeper + + InitGenesis(sdk.Context, *types.GenesisState) + ExportGenesis(sdk.Context) *types.GenesisState + + GetSupply(ctx sdk.Context, denom string) sdk.Coin + GetPaginatedTotalSupply(ctx sdk.Context, pagination *query.PageRequest) (sdk.Coins, *query.PageResponse, error) + IterateTotalSupply(ctx sdk.Context, cb func(sdk.Coin) bool) + GetDenomMetaData(ctx sdk.Context, denom string) (types.Metadata, bool) + SetDenomMetaData(ctx sdk.Context, denomMetaData types.Metadata) + IterateAllDenomMetaData(ctx sdk.Context, cb func(types.Metadata) bool) + + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + DelegateCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + UndelegateCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error + MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + + DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error + UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error + + types.QueryServer } ``` @@ -96,18 +94,18 @@ accounts. The send keeper does not alter the total supply (mint or burn coins). // SendKeeper defines a module interface that facilitates the transfer of coins // between accounts without the possibility of creating coins. type SendKeeper interface { - ViewKeeper - - InputOutputCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) error - SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error - - GetParams(ctx sdk.Context) types.Params - SetParams(ctx sdk.Context, params types.Params) - - SendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool - SendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error - - BlockedAddr(addr sdk.AccAddress) bool + ViewKeeper + + InputOutputCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) error + SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error + + GetParams(ctx sdk.Context) types.Params + SetParams(ctx sdk.Context, params types.Params) + + IsSendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool + IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error + + BlockedAddr(addr sdk.AccAddress) bool } ``` @@ -119,16 +117,16 @@ The view keeper provides read-only access to account balances. The view keeper d // ViewKeeper defines a module interface that facilitates read only access to // account balances. type ViewKeeper interface { - ValidateBalance(ctx sdk.Context, addr sdk.AccAddress) error - HasBalance(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coin) bool - - GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - GetAccountsBalances(ctx sdk.Context) []types.Balance - GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin - LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins - - IterateAccountBalances(ctx sdk.Context, addr sdk.AccAddress, cb func(coin sdk.Coin) (stop bool)) - IterateAllBalances(ctx sdk.Context, cb func(address sdk.AccAddress, coin sdk.Coin) (stop bool)) + ValidateBalance(ctx sdk.Context, addr sdk.AccAddress) error + HasBalance(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coin) bool + + GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + GetAccountsBalances(ctx sdk.Context) []types.Balance + GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin + LockedCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + + IterateAccountBalances(ctx sdk.Context, addr sdk.AccAddress, cb func(coin sdk.Coin) (stop bool)) + IterateAllBalances(ctx sdk.Context, cb func(address sdk.AccAddress, coin sdk.Coin) (stop bool)) } ``` diff --git a/x/bank/spec/03_messages.md b/x/bank/spec/03_messages.md index 07c42f5f78..0ef6ce72ef 100644 --- a/x/bank/spec/03_messages.md +++ b/x/bank/spec/03_messages.md @@ -6,20 +6,21 @@ order: 3 ## MsgSend +Send coins from one address to another. +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L19-L28 -`handleMsgSend` just runs `inputOutputCoins`. +The message will fail under the following conditions: +- The coins do not have sending enabled +- The `to` address is restricted -```go -handleMsgSend(msg MsgSend) - inputSum = 0 - for input in inputs - inputSum += input.Amount - outputSum = 0 - for output in outputs - outputSum += output.Amount - if inputSum != outputSum: - fail with "input/output amount mismatch" +## MsgMultiSend + +Send coins from and to a series of different address. If any of the receiving addresses do not correspond to an existing account, a new account is created. ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/bank/v1beta1/tx.proto#L33-L39 + +The message will fail under the following conditions: +- Any of the coins do not have sending enabled +- Any of the `to` addresses are restricted +- Any of the coins are locked +- The inputs and outputs do not correctly correspond to one another - return inputOutputCoins(msg.Inputs, msg.Outputs) -``` diff --git a/x/bank/types/bank.pb.go b/x/bank/types/bank.pb.go index 0511d1e961..f190a457b2 100644 --- a/x/bank/types/bank.pb.go +++ b/x/bank/types/bank.pb.go @@ -426,44 +426,44 @@ func init() { func init() { proto.RegisterFile("cosmos/bank/v1beta1/bank.proto", fileDescriptor_dd052eee12edf988) } var fileDescriptor_dd052eee12edf988 = []byte{ - // 587 bytes of a gzipped FileDescriptorProto + // 592 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xbf, 0x6f, 0xd3, 0x40, - 0x14, 0xf6, 0x35, 0x8d, 0x49, 0x2f, 0xb0, 0x1c, 0x15, 0x72, 0x2b, 0x61, 0x1b, 0x4b, 0x48, 0x29, - 0xa2, 0x0e, 0x05, 0xb1, 0x64, 0x41, 0x4a, 0x41, 0xa8, 0x03, 0x02, 0xb9, 0x42, 0x48, 0x30, 0x44, - 0xe7, 0xdc, 0x35, 0x58, 0xb5, 0xef, 0xac, 0xdc, 0x19, 0xd5, 0xff, 0x01, 0x13, 0x30, 0x32, 0x76, - 0x66, 0x62, 0xe0, 0x7f, 0xa0, 0x63, 0x05, 0x0b, 0x53, 0x40, 0xc9, 0xc2, 0xdc, 0xbf, 0x00, 0xdd, - 0x9d, 0xf3, 0xa3, 0x52, 0x40, 0x0c, 0x0c, 0x4c, 0x79, 0xdf, 0x7b, 0xdf, 0xfb, 0xde, 0xd3, 0x77, - 0xcf, 0x81, 0x6e, 0x9f, 0x8b, 0x8c, 0x8b, 0x76, 0x8c, 0xd9, 0x61, 0xfb, 0xd5, 0x4e, 0x4c, 0x25, - 0xde, 0xd1, 0x20, 0xcc, 0x87, 0x5c, 0x72, 0x74, 0xd9, 0xd4, 0x43, 0x9d, 0xaa, 0xea, 0x9b, 0xeb, - 0x03, 0x3e, 0xe0, 0xba, 0xde, 0x56, 0x91, 0xa1, 0x6e, 0x6e, 0x18, 0x6a, 0xcf, 0x14, 0xaa, 0x3e, - 0x53, 0x9a, 0x4f, 0x11, 0x74, 0x36, 0xa5, 0xcf, 0x13, 0x66, 0xea, 0xc1, 0x57, 0x00, 0xed, 0x27, - 0x78, 0x88, 0x33, 0x81, 0x0e, 0xe0, 0x45, 0x41, 0x19, 0xe9, 0x51, 0x86, 0xe3, 0x94, 0x12, 0x07, - 0xf8, 0xb5, 0x56, 0xf3, 0xb6, 0x1f, 0x2e, 0xd9, 0x23, 0xdc, 0xa7, 0x8c, 0x3c, 0x30, 0xbc, 0xee, - 0xb5, 0xb3, 0x91, 0x77, 0xb5, 0xc4, 0x59, 0xda, 0x09, 0x16, 0xfb, 0x6f, 0xf2, 0x2c, 0x91, 0x34, - 0xcb, 0x65, 0x19, 0x44, 0x4d, 0x31, 0xe7, 0xa3, 0x17, 0x70, 0x9d, 0xd0, 0x03, 0x5c, 0xa4, 0xb2, - 0x77, 0x6e, 0xde, 0x8a, 0x0f, 0x5a, 0x8d, 0xee, 0xd6, 0xd9, 0xc8, 0xbb, 0x6e, 0xd4, 0x96, 0xb1, - 0x16, 0x55, 0x51, 0x45, 0x58, 0x58, 0xa6, 0xb3, 0xfa, 0xfe, 0xd8, 0xb3, 0x82, 0x87, 0xb0, 0xb9, - 0x90, 0x44, 0xeb, 0xb0, 0x4e, 0x28, 0xe3, 0x99, 0x03, 0x7c, 0xd0, 0x5a, 0x8b, 0x0c, 0x40, 0x0e, - 0xbc, 0x70, 0x6e, 0x74, 0x34, 0x85, 0x9d, 0x86, 0x12, 0xf9, 0x79, 0xec, 0x81, 0xe0, 0x0d, 0x80, - 0xf5, 0x3d, 0x96, 0x17, 0x52, 0xb1, 0x31, 0x21, 0x43, 0x2a, 0x44, 0xa5, 0x32, 0x85, 0x08, 0xc3, - 0xba, 0x32, 0x54, 0x38, 0x2b, 0xda, 0xb0, 0x8d, 0xb9, 0x61, 0x82, 0xce, 0x0c, 0xdb, 0xe5, 0x09, - 0xeb, 0xde, 0x3a, 0x19, 0x79, 0xd6, 0x87, 0xef, 0x5e, 0x6b, 0x90, 0xc8, 0x97, 0x45, 0x1c, 0xf6, - 0x79, 0x56, 0xbd, 0x56, 0xf5, 0xb3, 0x2d, 0xc8, 0x61, 0x5b, 0x96, 0x39, 0x15, 0xba, 0x41, 0x44, - 0x46, 0xb9, 0xd3, 0x78, 0x6d, 0x16, 0xb2, 0x82, 0xb7, 0x00, 0xda, 0x8f, 0x0b, 0xf9, 0x1f, 0x6d, - 0xf4, 0x11, 0x40, 0x7b, 0xbf, 0xc8, 0xf3, 0xb4, 0x54, 0x73, 0x25, 0x97, 0x38, 0xad, 0x4e, 0xe7, - 0xdf, 0xce, 0xd5, 0xca, 0x9d, 0xdd, 0x6a, 0x2e, 0xf8, 0xf2, 0x69, 0xfb, 0xee, 0x8d, 0x3f, 0x76, - 0x1f, 0x99, 0x4f, 0x8b, 0x1e, 0xe5, 0x7c, 0x28, 0x29, 0x09, 0xcd, 0x92, 0x7b, 0x0e, 0x08, 0x9e, - 0xc1, 0xb5, 0xfb, 0xea, 0x04, 0x9e, 0xb2, 0x44, 0xfe, 0xe6, 0x38, 0x36, 0x61, 0x43, 0x35, 0x32, - 0xca, 0xa4, 0xbe, 0x8e, 0x4b, 0xd1, 0x0c, 0x6b, 0xe3, 0xd3, 0x04, 0x0b, 0x2a, 0x9c, 0x9a, 0x5f, - 0xd3, 0xc6, 0x1b, 0x18, 0x7c, 0x06, 0xb0, 0xf1, 0x88, 0x4a, 0x4c, 0xb0, 0xc4, 0xc8, 0x87, 0x4d, - 0x42, 0x45, 0x7f, 0x98, 0xe4, 0x32, 0xe1, 0xac, 0x92, 0x5f, 0x4c, 0xa1, 0x7b, 0x8a, 0xc1, 0x78, - 0xd6, 0x2b, 0x58, 0x22, 0xa7, 0xaf, 0xe5, 0x2e, 0xfd, 0xe0, 0x66, 0xfb, 0x46, 0x90, 0x4c, 0x43, - 0x81, 0x10, 0x5c, 0x55, 0xde, 0x3a, 0x35, 0xad, 0xad, 0x63, 0xb5, 0x1d, 0x49, 0x44, 0x9e, 0xe2, - 0xd2, 0x59, 0x35, 0x67, 0x51, 0x41, 0xc5, 0x66, 0x38, 0xa3, 0x4e, 0xdd, 0xb0, 0x55, 0x8c, 0xae, - 0x40, 0x5b, 0x94, 0x59, 0xcc, 0x53, 0xc7, 0xd6, 0xd9, 0x0a, 0x75, 0x77, 0x4f, 0xc6, 0x2e, 0x38, - 0x1d, 0xbb, 0xe0, 0xc7, 0xd8, 0x05, 0xef, 0x26, 0xae, 0x75, 0x3a, 0x71, 0xad, 0x6f, 0x13, 0xd7, - 0x7a, 0xbe, 0xf5, 0x37, 0xa6, 0xeb, 0x97, 0x8b, 0x6d, 0xfd, 0x1f, 0x73, 0xe7, 0x57, 0x00, 0x00, - 0x00, 0xff, 0xff, 0xe7, 0x1c, 0xc1, 0x93, 0xeb, 0x04, 0x00, 0x00, + 0x14, 0xf6, 0x35, 0x8d, 0x49, 0x2f, 0xb0, 0x1c, 0x15, 0x72, 0x23, 0x61, 0x1b, 0x4b, 0x48, 0x29, + 0xa2, 0x4e, 0x0a, 0x0c, 0x28, 0x0b, 0x52, 0xca, 0x0f, 0x75, 0x40, 0x20, 0x57, 0x08, 0x09, 0x86, + 0xe8, 0x9c, 0xbb, 0x06, 0xab, 0xf6, 0x9d, 0x95, 0x3b, 0x57, 0xf5, 0x7f, 0xc0, 0x04, 0x8c, 0x8c, + 0x9d, 0x59, 0xe1, 0x7f, 0xa0, 0x63, 0x05, 0x0b, 0x53, 0x40, 0xc9, 0xc2, 0xdc, 0xbf, 0x00, 0xf9, + 0xce, 0xf9, 0x51, 0x29, 0x20, 0x06, 0x06, 0xa6, 0xbc, 0xef, 0xbd, 0xef, 0x7d, 0xef, 0xe9, 0xbb, + 0xe7, 0x40, 0xbb, 0xcf, 0x45, 0xc2, 0x45, 0x2b, 0xc4, 0xec, 0xa0, 0x75, 0xb8, 0x1d, 0x52, 0x89, + 0xb7, 0x15, 0xf0, 0xd3, 0x21, 0x97, 0x1c, 0x5d, 0xd6, 0x75, 0x5f, 0xa5, 0xca, 0x7a, 0x63, 0x7d, + 0xc0, 0x07, 0x5c, 0xd5, 0x5b, 0x45, 0xa4, 0xa9, 0x8d, 0x0d, 0x4d, 0xed, 0xe9, 0x42, 0xd9, 0xa7, + 0x4b, 0xf3, 0x29, 0x82, 0xce, 0xa6, 0xf4, 0x79, 0xc4, 0x74, 0xdd, 0xfb, 0x0a, 0xa0, 0xf9, 0x14, + 0x0f, 0x71, 0x22, 0xd0, 0x3e, 0xbc, 0x28, 0x28, 0x23, 0x3d, 0xca, 0x70, 0x18, 0x53, 0x62, 0x01, + 0xb7, 0xd2, 0xac, 0xdf, 0x72, 0xfd, 0x25, 0x7b, 0xf8, 0x7b, 0x94, 0x91, 0x07, 0x9a, 0xd7, 0xbd, + 0x76, 0x36, 0x72, 0xae, 0xe6, 0x38, 0x89, 0x3b, 0xde, 0x62, 0xff, 0x4d, 0x9e, 0x44, 0x92, 0x26, + 0xa9, 0xcc, 0xbd, 0xa0, 0x2e, 0xe6, 0x7c, 0xf4, 0x12, 0xae, 0x13, 0xba, 0x8f, 0xb3, 0x58, 0xf6, + 0xce, 0xcd, 0x5b, 0x71, 0x41, 0xb3, 0xd6, 0xdd, 0x3c, 0x1b, 0x39, 0xd7, 0xb5, 0xda, 0x32, 0xd6, + 0xa2, 0x2a, 0x2a, 0x09, 0x0b, 0xcb, 0x74, 0x56, 0xdf, 0x1f, 0x3b, 0x86, 0xf7, 0x08, 0xd6, 0x17, + 0x92, 0x68, 0x1d, 0x56, 0x09, 0x65, 0x3c, 0xb1, 0x80, 0x0b, 0x9a, 0x6b, 0x81, 0x06, 0xc8, 0x82, + 0x17, 0xce, 0x8d, 0x0e, 0xa6, 0xb0, 0x53, 0x2b, 0x44, 0x7e, 0x1e, 0x3b, 0xc0, 0x7b, 0x03, 0x60, + 0x75, 0x97, 0xa5, 0x99, 0x2c, 0xd8, 0x98, 0x90, 0x21, 0x15, 0xa2, 0x54, 0x99, 0x42, 0x84, 0x61, + 0xb5, 0x30, 0x54, 0x58, 0x2b, 0xca, 0xb0, 0x8d, 0xb9, 0x61, 0x82, 0xce, 0x0c, 0xdb, 0xe1, 0x11, + 0xeb, 0xb6, 0x4f, 0x46, 0x8e, 0xf1, 0xe1, 0xbb, 0xd3, 0x1c, 0x44, 0xf2, 0x55, 0x16, 0xfa, 0x7d, + 0x9e, 0x94, 0xaf, 0x55, 0xfe, 0x6c, 0x09, 0x72, 0xd0, 0x92, 0x79, 0x4a, 0x85, 0x6a, 0x10, 0x81, + 0x56, 0xee, 0xd4, 0x5e, 0xeb, 0x85, 0x0c, 0xef, 0x2d, 0x80, 0xe6, 0x93, 0x4c, 0xfe, 0x47, 0x1b, + 0x7d, 0x04, 0xd0, 0xdc, 0xcb, 0xd2, 0x34, 0xce, 0x8b, 0xb9, 0x92, 0x4b, 0x1c, 0x97, 0xa7, 0xf3, + 0x6f, 0xe7, 0x2a, 0xe5, 0xce, 0xc3, 0x72, 0x2e, 0xf8, 0xf2, 0x69, 0xeb, 0xee, 0x8d, 0x3f, 0x76, + 0x1f, 0xe9, 0x4f, 0x2b, 0xa6, 0x03, 0xdc, 0xcf, 0x5b, 0x87, 0xed, 0x3b, 0x6d, 0x5f, 0xef, 0xb9, + 0x6b, 0x01, 0xef, 0x39, 0x5c, 0xbb, 0x5f, 0x5c, 0xc1, 0x33, 0x16, 0xc9, 0xdf, 0xdc, 0x47, 0x03, + 0xd6, 0xe8, 0x51, 0xca, 0x19, 0x65, 0x52, 0x1d, 0xc8, 0xa5, 0x60, 0x86, 0x95, 0xf7, 0x71, 0x84, + 0x05, 0x15, 0x56, 0xc5, 0xad, 0x28, 0xef, 0x35, 0xf4, 0x3e, 0x03, 0x58, 0x7b, 0x4c, 0x25, 0x26, + 0x58, 0x62, 0xe4, 0xc2, 0x3a, 0xa1, 0xa2, 0x3f, 0x8c, 0x52, 0x19, 0x71, 0x56, 0xca, 0x2f, 0xa6, + 0xd0, 0xbd, 0x82, 0xc1, 0x78, 0xd2, 0xcb, 0x58, 0x24, 0xa7, 0x0f, 0x66, 0x2f, 0xfd, 0xe6, 0x66, + 0xfb, 0x06, 0x90, 0x4c, 0x43, 0x81, 0x10, 0x5c, 0x2d, 0xec, 0xb5, 0x2a, 0x4a, 0x5b, 0xc5, 0xc5, + 0x76, 0x24, 0x12, 0x69, 0x8c, 0x73, 0x6b, 0x55, 0x5f, 0x46, 0x09, 0x0b, 0x36, 0xc3, 0x09, 0xb5, + 0xaa, 0x9a, 0x5d, 0xc4, 0xe8, 0x0a, 0x34, 0x45, 0x9e, 0x84, 0x3c, 0xb6, 0x4c, 0x95, 0x2d, 0x51, + 0x77, 0xe7, 0x64, 0x6c, 0x83, 0xd3, 0xb1, 0x0d, 0x7e, 0x8c, 0x6d, 0xf0, 0x6e, 0x62, 0x1b, 0xa7, + 0x13, 0xdb, 0xf8, 0x36, 0xb1, 0x8d, 0x17, 0x9b, 0x7f, 0xe3, 0xbb, 0x7a, 0xbc, 0xd0, 0x54, 0x7f, + 0x33, 0xb7, 0x7f, 0x05, 0x00, 0x00, 0xff, 0xff, 0xcc, 0x03, 0xbf, 0xe9, 0xee, 0x04, 0x00, 0x00, } func (this *SendEnabled) Equal(that interface{}) bool { diff --git a/x/bank/types/key.go b/x/bank/types/key.go index a50d04a0a7..5d7f52baa5 100644 --- a/x/bank/types/key.go +++ b/x/bank/types/key.go @@ -21,8 +21,8 @@ const ( // KVStore keys var ( - // BalancesPrefix is the for the account balances store. We use a byte - // (instead of say `[]]byte("balances")` to save some disk space). + // BalancesPrefix is the prefix for the account balances store. We use a byte + // (instead of `[]byte("balances")` to save some disk space). BalancesPrefix = []byte{0x02} SupplyKey = []byte{0x00} DenomMetadataPrefix = []byte{0x1} @@ -35,7 +35,7 @@ func DenomMetadataKey(denom string) []byte { } // AddressFromBalancesStore returns an account address from a balances prefix -// store. The key must not contain the perfix BalancesPrefix as the prefix store +// store. The key must not contain the prefix BalancesPrefix as the prefix store // iterator discards the actual prefix. // // If invalid key is passed, AddressFromBalancesStore returns ErrInvalidKey.