refactor(vesting): remove vesting account creation (#19535)

This commit is contained in:
Marko 2024-02-23 17:59:01 +01:00 committed by GitHub
parent 8b83a2e29d
commit 72e4134710
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 2 additions and 4808 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,203 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc (unknown)
// source: cosmos/vesting/v1beta1/tx.proto
package vestingv1beta1
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
const (
Msg_CreateVestingAccount_FullMethodName = "/cosmos.vesting.v1beta1.Msg/CreateVestingAccount"
Msg_CreatePermanentLockedAccount_FullMethodName = "/cosmos.vesting.v1beta1.Msg/CreatePermanentLockedAccount"
Msg_CreatePeriodicVestingAccount_FullMethodName = "/cosmos.vesting.v1beta1.Msg/CreatePeriodicVestingAccount"
)
// MsgClient is the client API for Msg service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type MsgClient interface {
// CreateVestingAccount defines a method that enables creating a vesting
// account.
CreateVestingAccount(ctx context.Context, in *MsgCreateVestingAccount, opts ...grpc.CallOption) (*MsgCreateVestingAccountResponse, error)
// CreatePermanentLockedAccount defines a method that enables creating a permanent
// locked account.
//
// Since: cosmos-sdk 0.46
CreatePermanentLockedAccount(ctx context.Context, in *MsgCreatePermanentLockedAccount, opts ...grpc.CallOption) (*MsgCreatePermanentLockedAccountResponse, error)
// CreatePeriodicVestingAccount defines a method that enables creating a
// periodic vesting account.
//
// Since: cosmos-sdk 0.46
CreatePeriodicVestingAccount(ctx context.Context, in *MsgCreatePeriodicVestingAccount, opts ...grpc.CallOption) (*MsgCreatePeriodicVestingAccountResponse, error)
}
type msgClient struct {
cc grpc.ClientConnInterface
}
func NewMsgClient(cc grpc.ClientConnInterface) MsgClient {
return &msgClient{cc}
}
func (c *msgClient) CreateVestingAccount(ctx context.Context, in *MsgCreateVestingAccount, opts ...grpc.CallOption) (*MsgCreateVestingAccountResponse, error) {
out := new(MsgCreateVestingAccountResponse)
err := c.cc.Invoke(ctx, Msg_CreateVestingAccount_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *msgClient) CreatePermanentLockedAccount(ctx context.Context, in *MsgCreatePermanentLockedAccount, opts ...grpc.CallOption) (*MsgCreatePermanentLockedAccountResponse, error) {
out := new(MsgCreatePermanentLockedAccountResponse)
err := c.cc.Invoke(ctx, Msg_CreatePermanentLockedAccount_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *msgClient) CreatePeriodicVestingAccount(ctx context.Context, in *MsgCreatePeriodicVestingAccount, opts ...grpc.CallOption) (*MsgCreatePeriodicVestingAccountResponse, error) {
out := new(MsgCreatePeriodicVestingAccountResponse)
err := c.cc.Invoke(ctx, Msg_CreatePeriodicVestingAccount_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// MsgServer is the server API for Msg service.
// All implementations must embed UnimplementedMsgServer
// for forward compatibility
type MsgServer interface {
// CreateVestingAccount defines a method that enables creating a vesting
// account.
CreateVestingAccount(context.Context, *MsgCreateVestingAccount) (*MsgCreateVestingAccountResponse, error)
// CreatePermanentLockedAccount defines a method that enables creating a permanent
// locked account.
//
// Since: cosmos-sdk 0.46
CreatePermanentLockedAccount(context.Context, *MsgCreatePermanentLockedAccount) (*MsgCreatePermanentLockedAccountResponse, error)
// CreatePeriodicVestingAccount defines a method that enables creating a
// periodic vesting account.
//
// Since: cosmos-sdk 0.46
CreatePeriodicVestingAccount(context.Context, *MsgCreatePeriodicVestingAccount) (*MsgCreatePeriodicVestingAccountResponse, error)
mustEmbedUnimplementedMsgServer()
}
// UnimplementedMsgServer must be embedded to have forward compatible implementations.
type UnimplementedMsgServer struct {
}
func (UnimplementedMsgServer) CreateVestingAccount(context.Context, *MsgCreateVestingAccount) (*MsgCreateVestingAccountResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateVestingAccount not implemented")
}
func (UnimplementedMsgServer) CreatePermanentLockedAccount(context.Context, *MsgCreatePermanentLockedAccount) (*MsgCreatePermanentLockedAccountResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreatePermanentLockedAccount not implemented")
}
func (UnimplementedMsgServer) CreatePeriodicVestingAccount(context.Context, *MsgCreatePeriodicVestingAccount) (*MsgCreatePeriodicVestingAccountResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreatePeriodicVestingAccount not implemented")
}
func (UnimplementedMsgServer) mustEmbedUnimplementedMsgServer() {}
// UnsafeMsgServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to MsgServer will
// result in compilation errors.
type UnsafeMsgServer interface {
mustEmbedUnimplementedMsgServer()
}
func RegisterMsgServer(s grpc.ServiceRegistrar, srv MsgServer) {
s.RegisterService(&Msg_ServiceDesc, srv)
}
func _Msg_CreateVestingAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MsgCreateVestingAccount)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MsgServer).CreateVestingAccount(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Msg_CreateVestingAccount_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MsgServer).CreateVestingAccount(ctx, req.(*MsgCreateVestingAccount))
}
return interceptor(ctx, in, info, handler)
}
func _Msg_CreatePermanentLockedAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MsgCreatePermanentLockedAccount)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MsgServer).CreatePermanentLockedAccount(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Msg_CreatePermanentLockedAccount_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MsgServer).CreatePermanentLockedAccount(ctx, req.(*MsgCreatePermanentLockedAccount))
}
return interceptor(ctx, in, info, handler)
}
func _Msg_CreatePeriodicVestingAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(MsgCreatePeriodicVestingAccount)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MsgServer).CreatePeriodicVestingAccount(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Msg_CreatePeriodicVestingAccount_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MsgServer).CreatePeriodicVestingAccount(ctx, req.(*MsgCreatePeriodicVestingAccount))
}
return interceptor(ctx, in, info, handler)
}
// Msg_ServiceDesc is the grpc.ServiceDesc for Msg service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Msg_ServiceDesc = grpc.ServiceDesc{
ServiceName: "cosmos.vesting.v1beta1.Msg",
HandlerType: (*MsgServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "CreateVestingAccount",
Handler: _Msg_CreateVestingAccount_Handler,
},
{
MethodName: "CreatePermanentLockedAccount",
Handler: _Msg_CreatePermanentLockedAccount_Handler,
},
{
MethodName: "CreatePeriodicVestingAccount",
Handler: _Msg_CreatePeriodicVestingAccount_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "cosmos/vesting/v1beta1/tx.proto",
}

View File

@ -47,6 +47,8 @@ Ref: https://keepachangelog.com/en/1.0.0/
* [#19161](https://github.com/cosmos/cosmos-sdk/pull/19161) Remove `simulate` from `SetGasMeter`
* [#19363](https://github.com/cosmos/cosmos-sdk/pull/19363) Remove `IterateAccounts` and `GetAllAccounts` methods from the AccountKeeper interface and Keeper.
* [#19290](https://github.com/cosmos/cosmos-sdk/issues/19290) Pass `appmodule.Environment` to NewKeeper instead of passing individual services.
* [#19535](https://github.com/cosmos/cosmos-sdk/pull/19535) Remove vesting account creation when the chain is running. The accounts module is required for creating vesting accounts on a running chain.
<!-- TODO add a link to lockup accounts docs -->
### Consensus Breaking Changes

View File

@ -1,47 +0,0 @@
package vesting
import (
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
vestingv1beta1 "cosmossdk.io/api/cosmos/vesting/v1beta1"
)
// AutoCLIOptions implements the autocli.HasAutoCLIConfig interface.
func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
return &autocliv1.ModuleOptions{
Tx: &autocliv1.ServiceCommandDescriptor{
Service: vestingv1beta1.Msg_ServiceDesc.ServiceName,
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
{
RpcMethod: "CreateVestingAccount",
Use: "create-vesting-account [to_address] [end_time] [amount]",
Short: "Create a new vesting account funded with an allocation of tokens.",
Long: `Create a new vesting account funded with an allocation of tokens. The
account can either be a delayed or continuous vesting account, which is determined
by the '--delayed' flag. All vesting accounts created will have their start time
set by the committed block's time. The end_time must be provided as a UNIX epoch
timestamp.`,
PositionalArgs: []*autocliv1.PositionalArgDescriptor{
{ProtoField: "to_address"},
{ProtoField: "end_time"},
{ProtoField: "amount", Varargs: true},
},
FlagOptions: map[string]*autocliv1.FlagOptions{
"delayed": {Name: "delayed", Usage: "Create a delayed vesting account if true"},
},
},
{
RpcMethod: "CreatePermanentLockedAccount",
Use: "create-permanent-locked-account [to_address] [amount]",
Short: "Create a new permanently locked account funded with an allocation of tokens.",
Long: `Create a new account funded with an allocation of permanently locked tokens.
These tokens may be used for staking but are non-transferable. Staking rewards will accrue as liquid and transferable tokens.`,
PositionalArgs: []*autocliv1.PositionalArgDescriptor{
{ProtoField: "to_address"},
{ProtoField: "amount", Varargs: true},
},
},
},
EnhanceCustomCommand: true,
},
}
}

View File

@ -1,248 +0,0 @@
package vesting
import (
"context"
"github.com/hashicorp/go-metrics"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/x/auth/keeper"
authtypes "cosmossdk.io/x/auth/types"
"cosmossdk.io/x/auth/vesting/types"
"github.com/cosmos/cosmos-sdk/telemetry"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
type msgServer struct {
keeper.AccountKeeper
types.BankKeeper
}
// NewMsgServerImpl returns an implementation of the vesting MsgServer interface,
// wrapping the corresponding AccountKeeper and BankKeeper.
func NewMsgServerImpl(k keeper.AccountKeeper, bk types.BankKeeper) types.MsgServer {
return &msgServer{AccountKeeper: k, BankKeeper: bk}
}
var _ types.MsgServer = msgServer{}
func (s msgServer) CreateVestingAccount(ctx context.Context, msg *types.MsgCreateVestingAccount) (*types.MsgCreateVestingAccountResponse, error) {
from, err := s.AccountKeeper.AddressCodec().StringToBytes(msg.FromAddress)
if err != nil {
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'from' address: %s", err)
}
to, err := s.AccountKeeper.AddressCodec().StringToBytes(msg.ToAddress)
if err != nil {
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'to' address: %s", err)
}
if err := validateAmount(msg.Amount); err != nil {
return nil, err
}
if msg.StartTime < 0 {
return nil, sdkerrors.ErrInvalidRequest.Wrap("invalid start time")
}
if msg.EndTime <= 0 {
return nil, sdkerrors.ErrInvalidRequest.Wrap("invalid end time")
}
if msg.EndTime <= msg.StartTime {
return nil, sdkerrors.ErrInvalidRequest.Wrap("invalid start and end time (must be start < end)")
}
if err := s.BankKeeper.IsSendEnabledCoins(ctx, msg.Amount...); err != nil {
return nil, err
}
if s.BankKeeper.BlockedAddr(to) {
return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", msg.ToAddress)
}
if acc := s.AccountKeeper.GetAccount(ctx, to); acc != nil {
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "account %s already exists", msg.ToAddress)
}
baseAccount := authtypes.NewBaseAccountWithAddress(to)
baseAccount = s.AccountKeeper.NewAccount(ctx, baseAccount).(*authtypes.BaseAccount)
baseVestingAccount, err := types.NewBaseVestingAccount(baseAccount, msg.Amount.Sort(), msg.EndTime)
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, err.Error())
}
var vestingAccount sdk.AccountI
if msg.Delayed {
vestingAccount = types.NewDelayedVestingAccountRaw(baseVestingAccount)
} else {
start := msg.StartTime
if msg.StartTime == 0 {
start = s.AccountKeeper.Environment.HeaderService.GetHeaderInfo(ctx).Time.Unix()
}
vestingAccount = types.NewContinuousVestingAccountRaw(baseVestingAccount, start)
}
s.AccountKeeper.SetAccount(ctx, vestingAccount)
defer func() {
telemetry.IncrCounter(1, "new", "account")
for _, a := range msg.Amount {
if a.Amount.IsInt64() {
telemetry.SetGaugeWithLabels(
[]string{"tx", "msg", "create_vesting_account"},
float32(a.Amount.Int64()),
[]metrics.Label{telemetry.NewLabel("denom", a.Denom)},
)
}
}
}()
if err = s.BankKeeper.SendCoins(ctx, from, to, msg.Amount); err != nil {
return nil, err
}
return &types.MsgCreateVestingAccountResponse{}, nil
}
func (s msgServer) CreatePermanentLockedAccount(ctx context.Context, msg *types.MsgCreatePermanentLockedAccount) (*types.MsgCreatePermanentLockedAccountResponse, error) {
from, err := s.AccountKeeper.AddressCodec().StringToBytes(msg.FromAddress)
if err != nil {
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'from' address: %s", err)
}
to, err := s.AccountKeeper.AddressCodec().StringToBytes(msg.ToAddress)
if err != nil {
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'to' address: %s", err)
}
if err := validateAmount(msg.Amount); err != nil {
return nil, err
}
if err := s.BankKeeper.IsSendEnabledCoins(ctx, msg.Amount...); err != nil {
return nil, err
}
if s.BankKeeper.BlockedAddr(to) {
return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", msg.ToAddress)
}
if acc := s.AccountKeeper.GetAccount(ctx, to); acc != nil {
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "account %s already exists", msg.ToAddress)
}
baseAccount := authtypes.NewBaseAccountWithAddress(to)
baseAccount = s.AccountKeeper.NewAccount(ctx, baseAccount).(*authtypes.BaseAccount)
vestingAccount, err := types.NewPermanentLockedAccount(baseAccount, msg.Amount)
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, err.Error())
}
s.AccountKeeper.SetAccount(ctx, vestingAccount)
defer func() {
telemetry.IncrCounter(1, "new", "account")
for _, a := range msg.Amount {
if a.Amount.IsInt64() {
telemetry.SetGaugeWithLabels(
[]string{"tx", "msg", "create_permanent_locked_account"},
float32(a.Amount.Int64()),
[]metrics.Label{telemetry.NewLabel("denom", a.Denom)},
)
}
}
}()
if err = s.BankKeeper.SendCoins(ctx, from, to, msg.Amount); err != nil {
return nil, err
}
return &types.MsgCreatePermanentLockedAccountResponse{}, nil
}
func (s msgServer) CreatePeriodicVestingAccount(ctx context.Context, msg *types.MsgCreatePeriodicVestingAccount) (*types.MsgCreatePeriodicVestingAccountResponse, error) {
from, err := s.AccountKeeper.AddressCodec().StringToBytes(msg.FromAddress)
if err != nil {
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'from' address: %s", err)
}
to, err := s.AccountKeeper.AddressCodec().StringToBytes(msg.ToAddress)
if err != nil {
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'to' address: %s", err)
}
if msg.StartTime < 1 {
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "invalid start time of %d, length must be greater than 0", msg.StartTime)
}
var totalCoins sdk.Coins
for i, period := range msg.VestingPeriods {
if period.Length < 1 {
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "invalid period length of %d in period %d, length must be greater than 0", period.Length, i)
}
if err := validateAmount(period.Amount); err != nil {
return nil, err
}
totalCoins = totalCoins.Add(period.Amount...)
}
if s.BankKeeper.BlockedAddr(to) {
return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", msg.ToAddress)
}
if acc := s.AccountKeeper.GetAccount(ctx, to); acc != nil {
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "account %s already exists", msg.ToAddress)
}
if err := s.BankKeeper.IsSendEnabledCoins(ctx, totalCoins...); err != nil {
return nil, err
}
baseAccount := authtypes.NewBaseAccountWithAddress(to)
baseAccount = s.AccountKeeper.NewAccount(ctx, baseAccount).(*authtypes.BaseAccount)
vestingAccount, err := types.NewPeriodicVestingAccount(baseAccount, totalCoins.Sort(), msg.StartTime, msg.VestingPeriods)
if err != nil {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, err.Error())
}
s.AccountKeeper.SetAccount(ctx, vestingAccount)
defer func() {
telemetry.IncrCounter(1, "new", "account")
for _, a := range totalCoins {
if a.Amount.IsInt64() {
telemetry.SetGaugeWithLabels(
[]string{"tx", "msg", "create_periodic_vesting_account"},
float32(a.Amount.Int64()),
[]metrics.Label{telemetry.NewLabel("denom", a.Denom)},
)
}
}
}()
if err = s.BankKeeper.SendCoins(ctx, from, to, totalCoins); err != nil {
return nil, err
}
return &types.MsgCreatePeriodicVestingAccountResponse{}, nil
}
func validateAmount(amount sdk.Coins) error {
if !amount.IsValid() {
return sdkerrors.ErrInvalidCoins.Wrap(amount.String())
}
if !amount.IsAllPositive() {
return sdkerrors.ErrInvalidCoins.Wrap(amount.String())
}
return nil
}

View File

@ -1,481 +0,0 @@
package vesting_test
import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/suite"
"cosmossdk.io/core/header"
"cosmossdk.io/log"
"cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"
authcodec "cosmossdk.io/x/auth/codec"
authkeeper "cosmossdk.io/x/auth/keeper"
authtypes "cosmossdk.io/x/auth/types"
"cosmossdk.io/x/auth/vesting"
vestingtestutil "cosmossdk.io/x/auth/vesting/testutil"
vestingtypes "cosmossdk.io/x/auth/vesting/types"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
)
var (
fromAddr = sdk.AccAddress([]byte("from1________________"))
to1Addr = sdk.AccAddress([]byte("to1__________________"))
to2Addr = sdk.AccAddress([]byte("to2__________________"))
to3Addr = sdk.AccAddress([]byte("to3__________________"))
fooCoin = sdk.NewInt64Coin("foo", 100)
periodCoin = sdk.NewInt64Coin("foo", 20)
)
type VestingTestSuite struct {
suite.Suite
ctx sdk.Context
accountKeeper authkeeper.AccountKeeper
bankKeeper *vestingtestutil.MockBankKeeper
msgServer vestingtypes.MsgServer
}
func (s *VestingTestSuite) SetupTest() {
key := storetypes.NewKVStoreKey(authtypes.StoreKey)
env := runtime.NewEnvironment(runtime.NewKVStoreService(key), log.NewNopLogger())
testCtx := testutil.DefaultContextWithDB(s.T(), key, storetypes.NewTransientStoreKey("transient_test"))
s.ctx = testCtx.Ctx.WithHeaderInfo(header.Info{Time: time.Now()})
encCfg := moduletestutil.MakeTestEncodingConfig()
maccPerms := map[string][]string{}
ctrl := gomock.NewController(s.T())
s.bankKeeper = vestingtestutil.NewMockBankKeeper(ctrl)
s.accountKeeper = authkeeper.NewAccountKeeper(
env,
encCfg.Codec,
authtypes.ProtoBaseAccount,
maccPerms,
authcodec.NewBech32Codec("cosmos"),
"cosmos",
authtypes.NewModuleAddress("gov").String(),
)
vestingtypes.RegisterInterfaces(encCfg.InterfaceRegistry)
authtypes.RegisterInterfaces(encCfg.InterfaceRegistry)
s.msgServer = vesting.NewMsgServerImpl(s.accountKeeper, s.bankKeeper)
}
func (s *VestingTestSuite) TestCreateVestingAccount() {
testCases := map[string]struct {
preRun func()
input *vestingtypes.MsgCreateVestingAccount
expErr bool
expErrMsg string
}{
"empty from address": {
input: vestingtypes.NewMsgCreateVestingAccount(
[]byte{},
to1Addr,
sdk.Coins{fooCoin},
time.Now().Unix(),
true,
),
expErr: true,
expErrMsg: "invalid 'from' address",
},
"empty to address": {
input: vestingtypes.NewMsgCreateVestingAccount(
fromAddr,
[]byte{},
sdk.Coins{fooCoin},
time.Now().Unix(),
true,
),
expErr: true,
expErrMsg: "invalid 'to' address",
},
"invalid coins": {
input: vestingtypes.NewMsgCreateVestingAccount(
fromAddr,
to1Addr,
sdk.Coins{sdk.Coin{Denom: "stake", Amount: math.NewInt(-1)}},
time.Now().Unix(),
true,
),
expErr: true,
expErrMsg: "-1stake: invalid coins",
},
"invalid end time": {
input: vestingtypes.NewMsgCreateVestingAccount(
fromAddr,
to1Addr,
sdk.Coins{fooCoin},
-10,
true,
),
expErr: true,
expErrMsg: "invalid end time",
},
"create for existing account": {
preRun: func() {
toAcc := s.accountKeeper.NewAccountWithAddress(s.ctx, to1Addr)
s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil)
s.accountKeeper.SetAccount(s.ctx, toAcc)
s.bankKeeper.EXPECT().BlockedAddr(to1Addr).Return(false)
},
input: vestingtypes.NewMsgCreateVestingAccount(
fromAddr,
to1Addr,
sdk.Coins{fooCoin},
time.Now().Unix(),
true,
),
expErr: true,
expErrMsg: "already exists",
},
"create for blocked account": {
preRun: func() {
s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil)
s.bankKeeper.EXPECT().BlockedAddr(to1Addr).Return(true)
},
input: vestingtypes.NewMsgCreateVestingAccount(
fromAddr,
to1Addr,
sdk.Coins{fooCoin},
time.Now().Unix(),
true,
),
expErr: true,
expErrMsg: "not allowed to receive funds",
},
"create a valid delayed vesting account": {
preRun: func() {
s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil)
s.bankKeeper.EXPECT().BlockedAddr(to2Addr).Return(false)
s.bankKeeper.EXPECT().SendCoins(gomock.Any(), fromAddr, to2Addr, sdk.Coins{fooCoin}).Return(nil)
},
input: vestingtypes.NewMsgCreateVestingAccount(
fromAddr,
to2Addr,
sdk.Coins{fooCoin},
time.Now().Unix(),
true,
),
expErr: false,
expErrMsg: "",
},
"create a valid continuous vesting account": {
preRun: func() {
s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil)
s.bankKeeper.EXPECT().BlockedAddr(to3Addr).Return(false)
s.bankKeeper.EXPECT().SendCoins(gomock.Any(), fromAddr, to3Addr, sdk.Coins{fooCoin}).Return(nil)
},
input: vestingtypes.NewMsgCreateVestingAccount(
fromAddr,
to3Addr,
sdk.Coins{fooCoin},
time.Now().Unix(),
false,
),
expErr: false,
expErrMsg: "",
},
}
for name, tc := range testCases {
s.Run(name, func() {
if tc.preRun != nil {
tc.preRun()
}
_, err := s.msgServer.CreateVestingAccount(s.ctx, tc.input)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.expErrMsg)
} else {
s.Require().NoError(err)
}
})
}
}
func (s *VestingTestSuite) TestCreatePermanentLockedAccount() {
testCases := map[string]struct {
preRun func()
input *vestingtypes.MsgCreatePermanentLockedAccount
expErr bool
expErrMsg string
}{
"empty from address": {
input: vestingtypes.NewMsgCreatePermanentLockedAccount(
[]byte{},
to1Addr,
sdk.Coins{fooCoin},
),
expErr: true,
expErrMsg: "invalid 'from' address",
},
"empty to address": {
input: vestingtypes.NewMsgCreatePermanentLockedAccount(
fromAddr,
[]byte{},
sdk.Coins{fooCoin},
),
expErr: true,
expErrMsg: "invalid 'to' address",
},
"invalid coins": {
input: vestingtypes.NewMsgCreatePermanentLockedAccount(
fromAddr,
to1Addr,
sdk.Coins{sdk.Coin{Denom: "stake", Amount: math.NewInt(-1)}},
),
expErr: true,
expErrMsg: "-1stake: invalid coins",
},
"create for existing account": {
preRun: func() {
toAcc := s.accountKeeper.NewAccountWithAddress(s.ctx, to1Addr)
s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil)
s.bankKeeper.EXPECT().BlockedAddr(to1Addr).Return(false)
s.accountKeeper.SetAccount(s.ctx, toAcc)
},
input: vestingtypes.NewMsgCreatePermanentLockedAccount(
fromAddr,
to1Addr,
sdk.Coins{fooCoin},
),
expErr: true,
expErrMsg: "already exists",
},
"create for blocked account": {
preRun: func() {
toAcc := s.accountKeeper.NewAccountWithAddress(s.ctx, to1Addr)
s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil)
s.bankKeeper.EXPECT().BlockedAddr(to1Addr).Return(true)
s.accountKeeper.SetAccount(s.ctx, toAcc)
},
input: vestingtypes.NewMsgCreatePermanentLockedAccount(
fromAddr,
to1Addr,
sdk.Coins{fooCoin},
),
expErr: true,
expErrMsg: "not allowed to receive funds",
},
"create a valid permanent locked account": {
preRun: func() {
s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), fooCoin).Return(nil)
s.bankKeeper.EXPECT().BlockedAddr(to2Addr).Return(false)
s.bankKeeper.EXPECT().SendCoins(gomock.Any(), fromAddr, to2Addr, sdk.Coins{fooCoin}).Return(nil)
},
input: vestingtypes.NewMsgCreatePermanentLockedAccount(
fromAddr,
to2Addr,
sdk.Coins{fooCoin},
),
expErr: false,
expErrMsg: "",
},
}
for name, tc := range testCases {
s.Run(name, func() {
if tc.preRun != nil {
tc.preRun()
}
_, err := s.msgServer.CreatePermanentLockedAccount(s.ctx, tc.input)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.expErrMsg)
} else {
s.Require().NoError(err)
}
})
}
}
func (s *VestingTestSuite) TestCreatePeriodicVestingAccount() {
testCases := []struct {
name string
preRun func()
input *vestingtypes.MsgCreatePeriodicVestingAccount
expErr bool
expErrMsg string
}{
{
name: "empty from address",
input: vestingtypes.NewMsgCreatePeriodicVestingAccount(
[]byte{},
to1Addr,
time.Now().Unix(),
[]vestingtypes.Period{
{
Length: 10,
Amount: sdk.NewCoins(periodCoin),
},
},
),
expErr: true,
expErrMsg: "invalid 'from' address",
},
{
name: "empty to address",
input: vestingtypes.NewMsgCreatePeriodicVestingAccount(
fromAddr,
[]byte{},
time.Now().Unix(),
[]vestingtypes.Period{
{
Length: 10,
Amount: sdk.NewCoins(periodCoin),
},
},
),
expErr: true,
expErrMsg: "invalid 'to' address",
},
{
name: "invalid start time",
input: vestingtypes.NewMsgCreatePeriodicVestingAccount(
fromAddr,
to1Addr,
0,
[]vestingtypes.Period{
{
Length: 10,
Amount: sdk.NewCoins(periodCoin),
},
},
),
expErr: true,
expErrMsg: "invalid start time",
},
{
name: "invalid period",
input: vestingtypes.NewMsgCreatePeriodicVestingAccount(
fromAddr,
to1Addr,
time.Now().Unix(),
[]vestingtypes.Period{
{
Length: 0,
Amount: sdk.NewCoins(periodCoin),
},
},
),
expErr: true,
expErrMsg: "invalid period",
},
{
name: "invalid coins",
input: vestingtypes.NewMsgCreatePeriodicVestingAccount(
fromAddr,
to1Addr,
time.Now().Unix(),
[]vestingtypes.Period{
{
Length: 1,
Amount: sdk.Coins{sdk.Coin{Denom: "stake", Amount: math.NewInt(-1)}},
},
},
),
expErr: true,
expErrMsg: "-1stake: invalid coins",
},
{
name: "create for existing account",
preRun: func() {
s.bankKeeper.EXPECT().BlockedAddr(to1Addr).Return(false)
toAcc := s.accountKeeper.NewAccountWithAddress(s.ctx, to1Addr)
s.accountKeeper.SetAccount(s.ctx, toAcc)
},
input: vestingtypes.NewMsgCreatePeriodicVestingAccount(
fromAddr,
to1Addr,
time.Now().Unix(),
[]vestingtypes.Period{
{
Length: 10,
Amount: sdk.NewCoins(periodCoin),
},
},
),
expErr: true,
expErrMsg: "already exists",
},
{
name: "create for blocked address",
preRun: func() {
s.bankKeeper.EXPECT().BlockedAddr(to2Addr).Return(true)
},
input: vestingtypes.NewMsgCreatePeriodicVestingAccount(
fromAddr,
to2Addr,
time.Now().Unix(),
[]vestingtypes.Period{
{
Length: 10,
Amount: sdk.NewCoins(periodCoin),
},
{
Length: 20,
Amount: sdk.NewCoins(fooCoin),
},
},
),
expErr: true,
expErrMsg: "not allowed to receive funds",
},
{
name: "create a valid periodic vesting account",
preRun: func() {
s.bankKeeper.EXPECT().IsSendEnabledCoins(gomock.Any(), periodCoin.Add(fooCoin)).Return(nil)
s.bankKeeper.EXPECT().BlockedAddr(to2Addr).Return(false)
s.bankKeeper.EXPECT().SendCoins(gomock.Any(), fromAddr, to2Addr, gomock.Any()).Return(nil)
},
input: vestingtypes.NewMsgCreatePeriodicVestingAccount(
fromAddr,
to2Addr,
time.Now().Unix(),
[]vestingtypes.Period{
{
Length: 10,
Amount: sdk.NewCoins(periodCoin),
},
{
Length: 20,
Amount: sdk.NewCoins(fooCoin),
},
},
),
expErr: false,
expErrMsg: "",
},
}
for _, tc := range testCases {
s.Run(tc.name, func() {
if tc.preRun != nil {
tc.preRun()
}
_, err := s.msgServer.CreatePeriodicVestingAccount(s.ctx, tc.input)
if tc.expErr {
s.Require().Error(err)
s.Require().Contains(err.Error(), tc.expErrMsg)
} else {
s.Require().NoError(err)
}
})
}
}
func TestVestingTestSuite(t *testing.T) {
suite.Run(t, new(VestingTestSuite))
}

View File

@ -1,106 +0,0 @@
syntax = "proto3";
package cosmos.vesting.v1beta1;
import "gogoproto/gogo.proto";
import "cosmos/base/v1beta1/coin.proto";
import "cosmos_proto/cosmos.proto";
import "cosmos/vesting/v1beta1/vesting.proto";
import "cosmos/msg/v1/msg.proto";
import "amino/amino.proto";
option go_package = "cosmossdk.io/x/auth/vesting/types";
// Msg defines the bank Msg service.
service Msg {
option (cosmos.msg.v1.service) = true;
// CreateVestingAccount defines a method that enables creating a vesting
// account.
rpc CreateVestingAccount(MsgCreateVestingAccount) returns (MsgCreateVestingAccountResponse);
// CreatePermanentLockedAccount defines a method that enables creating a permanent
// locked account.
//
// Since: cosmos-sdk 0.46
rpc CreatePermanentLockedAccount(MsgCreatePermanentLockedAccount) returns (MsgCreatePermanentLockedAccountResponse);
// CreatePeriodicVestingAccount defines a method that enables creating a
// periodic vesting account.
//
// Since: cosmos-sdk 0.46
rpc CreatePeriodicVestingAccount(MsgCreatePeriodicVestingAccount) returns (MsgCreatePeriodicVestingAccountResponse);
}
// MsgCreateVestingAccount defines a message that enables creating a vesting
// account.
message MsgCreateVestingAccount {
option (cosmos.msg.v1.signer) = "from_address";
option (amino.name) = "cosmos-sdk/MsgCreateVestingAccount";
option (gogoproto.equal) = true;
string from_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string to_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
repeated cosmos.base.v1beta1.Coin amount = 3 [
(gogoproto.nullable) = false,
(amino.dont_omitempty) = true,
(amino.encoding) = "legacy_coins",
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
// end of vesting as unix time (in seconds).
int64 end_time = 4;
bool delayed = 5;
// start of vesting as unix time (in seconds).
//
// Since 0.51.x
int64 start_time = 6;
}
// MsgCreateVestingAccountResponse defines the Msg/CreateVestingAccount response type.
message MsgCreateVestingAccountResponse {}
// MsgCreatePermanentLockedAccount defines a message that enables creating a permanent
// locked account.
//
// Since: cosmos-sdk 0.46
message MsgCreatePermanentLockedAccount {
option (cosmos.msg.v1.signer) = "from_address";
option (amino.name) = "cosmos-sdk/MsgCreatePermLockedAccount";
option (gogoproto.equal) = true;
string from_address = 1 [(gogoproto.moretags) = "yaml:\"from_address\""];
string to_address = 2 [(gogoproto.moretags) = "yaml:\"to_address\""];
repeated cosmos.base.v1beta1.Coin amount = 3 [
(gogoproto.nullable) = false,
(amino.dont_omitempty) = true,
(amino.encoding) = "legacy_coins",
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
}
// MsgCreatePermanentLockedAccountResponse defines the Msg/CreatePermanentLockedAccount response type.
//
// Since: cosmos-sdk 0.46
message MsgCreatePermanentLockedAccountResponse {}
// MsgCreateVestingAccount defines a message that enables creating a vesting
// account.
//
// Since: cosmos-sdk 0.46
message MsgCreatePeriodicVestingAccount {
option (cosmos.msg.v1.signer) = "from_address";
option (amino.name) = "cosmos-sdk/MsgCreatePeriodVestAccount";
option (gogoproto.equal) = false;
string from_address = 1;
string to_address = 2;
// start of vesting as unix time (in seconds).
int64 start_time = 3;
repeated Period vesting_periods = 4 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
}
// MsgCreateVestingAccountResponse defines the Msg/CreatePeriodicVestingAccount
// response type.
//
// Since: cosmos-sdk 0.46
message MsgCreatePeriodicVestingAccountResponse {}